TikTokHow-to

TikTok search API: search videos by keyword to CSV

Our TikTok search route returns up to 30 videos a call for 2 credits, with plays and likes. TikTok's own keyword search is for approved researchers.

5 min read

Short answer

  • TikTok's Research API, open only to approved academic and non-profit researchers, is its official keyword search; the Display API has no search.
  • Our search route returns public videos for a keyword with plays, likes, comments, shares and saves, 2 to 30 a call, and pages with a cursor.
  • Each call is 2 credits; 10 pages gave 88 to 120 videos per keyword, so 1,000 videos cost 167 to 227 credits.
On this page

Send a keyword to /v2/tiktok/app/search/videos and you get public TikTok videos with their stats, 2 to 30 per call, plus a cursor for the next page. The script below saves several keywords to one CSV. TikTok's own keyword search sits in the Research API, which only approved researchers can use.

TikTok's Research API can query videos by keyword, but only for approved researchers at academic or not-for-profit institutions; creators, advertisers and commercial users are not eligible. Approved projects get 1,000 requests a day, and each video query covers at most 30 days. The Display API reads the videos of users who sign in to your app and has no search. Who can get Research API access covers the application.

What one call returns

One call returns a page of videos with their author, post time and stats, plus a cursor for the next page. We make ToolzerHub. Our TikTok API reads public TikTok data with one API key. The search route takes keyword, count, offset and region, and costs 2 credits.

bash
curl "https://api.toolzerhub.com/v2/tiktok/app/search/videos?keyword=skincare%20routine&count=12&region=US" \
  -H "x-api-key: $TOOLZERHUB_API_KEY"

The first result for skincare routine, trimmed:

json
{
  "data": {
    "search_item_list": [
      {
        "aweme_info": {
          "aweme_id": "7701302261489700148",
          "desc": "Funny,raw,unfiltered…😅Updated skincare routine🫶 telling you ",
          "create_time": 1793099174,
          "author": {
            "unique_id": "_d_vine"
          },
          "statistics": {
            "play_count": 871710,
            "digg_count": 165666,
            "comment_count": 747,
            "share_count": 913,
            "collect_count": 10705
          }
        }
      }
    ],
    "has_more": 1,
    "cursor": 12
  }
}

digg_count is likes and collect_count is saves. Send cursor back as offset while has_more is 1. count is a request, not a promise: asking for 12 gave between 2 and 30 videos a page. To find accounts instead of videos, /v2/tiktok/app/search/users takes the same keyword and returns them in user_list, also for 2 credits.

Save search results to CSV

The script searches each keyword, follows the cursor up to your page cap, drops repeated videos and writes one row per video.

python
import csv
import os
import sys
import time
from datetime import datetime, timezone

import requests

API = "https://api.toolzerhub.com/v2/tiktok/app/search/videos"
HEADERS = {"x-api-key": os.environ.get("TOOLZERHUB_API_KEY", "")}
FIELDS = ["keyword", "video_id", "author", "posted", "plays", "likes", "comments", "shares",
          "saves", "description", "url"]
calls = 0


def cell(value):
    # A leading = + - or @ would run as a formula when the CSV opens in a spreadsheet.
    return "'" + value if isinstance(value, str) and value[:1] in ("=", "+", "-", "@") else value


def get(params, tries=4):
    # 429 and 503 are worth a retry; the body or the Retry-After header says how long to wait.
    global calls
    for attempt in range(tries):
        r = requests.get(API, params=params, headers=HEADERS, timeout=120)
        calls += 1
        if r.status_code in (429, 503) and attempt < tries - 1:
            wait = int(((r.json().get("error") or {}).get("details") or {}).get("retryAfter") or r.headers.get("Retry-After") or 5 * (attempt + 1))
            time.sleep(min(wait, 60))
            continue
        r.raise_for_status()
        return r.json().get("data") or {}


def row(keyword, v):
    stats, author = v.get("statistics") or {}, v.get("author") or {}
    posted = v.get("create_time")
    return {
        "keyword": cell(keyword), "video_id": v["aweme_id"], "author": author.get("unique_id"),
        "posted": datetime.fromtimestamp(posted, timezone.utc).date().isoformat() if posted else "",
        "plays": stats.get("play_count"), "likes": stats.get("digg_count"),
        "comments": stats.get("comment_count"), "shares": stats.get("share_count"),
        "saves": stats.get("collect_count"),
        "description": cell((v.get("desc") or "").replace("\n", " ")),
        "url": f"https://www.tiktok.com/@{author.get('unique_id')}/video/{v['aweme_id']}",
    }


def search(writer, keyword, max_pages, region):
    params, seen, pages = {"keyword": keyword, "offset": "0", "count": "12", "region": region}, set(), 0
    while pages < max_pages:
        data = get(params)
        pages += 1
        for item in data.get("search_item_list") or []:
            v = item.get("aweme_info") or {}
            if v.get("aweme_id") and v["aweme_id"] not in seen:  # keep one row per video
                seen.add(v["aweme_id"])
                writer.writerow(row(keyword, v))
        cursor = data.get("cursor")
        if not data.get("has_more") or cursor is None or str(cursor) == params["offset"]:
            break
        params["offset"] = str(cursor)
    print(f"{keyword}: {len(seen)} videos in {pages} pages")
    return len(seen)


def main(keywords, max_pages="5", region="US", out="tiktok_search.csv"):
    with open(out, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        writer.writeheader()
        total = sum(search(writer, k.strip(), int(max_pages), region) for k in keywords.split(",") if k.strip())
    print(f"wrote {total} rows to {out} in {calls} calls")


if __name__ == "__main__":
    if not HEADERS["x-api-key"] or len(sys.argv) < 2:
        sys.exit('usage: TOOLZERHUB_API_KEY=... python tiktok_search.py "skincare routine,street food" [max_pages] [region] [file.csv]')
    main(*sys.argv[1:])
bash
export TOOLZERHUB_API_KEY=your_key
python tiktok_search.py "skincare routine,python tutorial,street food" 10
text
skincare routine: 120 videos in 10 pages
python tutorial: 88 videos in 10 pages
street food: 118 videos in 10 pages
wrote 326 rows to tiktok_search.csv in 30 calls

What came out

We ran the script on three keywords with a 10-page cap, and it took 126 seconds. No video appeared under two keywords.

Keyword Videos Authors Median plays 1M+ plays
skincare routine 120 109 31,459 9
python tutorial 88 78 73,178.5 6
street food 118 86 526,714 48

The run averaged 10.9 videos a call. 289 of the 326 videos were posted in 2026, and the oldest is from November 2022.

What it costs

Each call is 2 credits, so the run above cost 60 credits, and 1,000 videos cost 167 to 227 credits depending on the keyword. You get 100 free credits a month; $19 buys 10,000 credits that never expire, and plans for higher volume cover more keywords. Set your key and your first keyword is a CSV in under a minute.

For a hashtag's videos, see the TikTok hashtag API; to look up single videos from their links, see TikTok API get video; for the videos that use a sound, see the TikTok sound API, and for streams on now, the TikTok live API.

We are not affiliated with TikTok or ByteDance.

Get this data from your own code

The TikTok API returns it as JSON with one x-api-key header. 100 free credits monthly.