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.
What TikTok's APIs offer for search
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.
curl "https://api.toolzerhub.com/v2/tiktok/app/search/videos?keyword=skincare%20routine&count=12®ion=US" \
-H "x-api-key: $TOOLZERHUB_API_KEY"
The first result for skincare routine, trimmed:
{
"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.
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:])
export TOOLZERHUB_API_KEY=your_key
python tiktok_search.py "skincare routine,python tutorial,street food" 10
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.