TikTok Creator Search Insights API: Find Content Gaps
Build a TikTok content-gap research workflow with Creator Search Insights, demographics, trend data, related videos, and cursor pagination.

TikTok’s Creator Search Insights is more useful than a list of popular hashtags. It exposes searches that TikTok labels as content gaps, then lets you inspect the audience, trend context, and related videos behind an idea. That makes it a workable first stage for an editorial research agent: discover opportunities broadly, validate each one with real posts, and keep the final decision with an editor.
This tutorial uses four endpoints from the TikTok API catalog. I ran every call on October 25, 2026 (UTC). Field names in the examples are observations from those responses; the endpoint reference remains the contract. For creator-level research rather than topic discovery, use the separate TikTok creator research API guide.
Key takeaway
- Start with
creator-search-insightsandtab: content_gap; do not infer a gap from popularity alone.- Preserve
query_id_stras a string. Detail and trend calls require it.- Validate a promising phrase with
creator-search-insights-videosbefore briefing a creator.- Treat missing demographics as missing data, not as a zero-valued audience segment.
What the live run returned
The first live page used Science, English, a limit of 10, and the content_gap tab. It returned ten ideas, cursor: 10, and has_more: true. The first item was “mount hood,” labelled content_gap, with a popularity score of 70 and 1,020 related videos. Those values are a snapshot, not a forecast or an SEO difficulty score.
The first observed content-gap result and the page cursor from the October 25 live call.
The detail call accepted past_30_days plus age,country. Its payload exposed age, location, and demographics keys, but individual dimensions can be sparse. A pipeline should retain the raw “unknown” state instead of replacing it with a confident audience profile.
The detail response exposes optional audience dimensions; sparse results must remain sparse.
The related-video call returned ten posts under search_item_list[].aweme_info with its own cursor; its top-level aweme_list was empty. This is the validation step: examine how the query is currently answered, whether the top posts actually match the phrase, and whether the gap fits your product or editorial scope.
The related-video endpoint turns an abstract search opportunity into posts an editor can review.
Set up one strict API helper
Install Requests and keep the key outside source control:
python -m pip install requests
export SANDBASE_API_KEY="your_key_here"
Every endpoint is called with POST /v1/api/<model>. The helper below accepts only the documented outputs[0].data envelope and fails if a run is incomplete or malformed.
import os, time, requests
BASE = "https://api.sandbase.ai/v1/api"
HEADERS = {
"Authorization": f"Bearer {os.environ['SANDBASE_API_KEY']}",
"Content-Type": "application/json",
}
def call(model, payload, retries=2):
for attempt in range(retries + 1):
response = requests.post(
f"{BASE}/{model}", headers=HEADERS, json=payload, timeout=90
)
if response.status_code < 500 and response.status_code != 429:
response.raise_for_status()
break
if attempt == retries:
response.raise_for_status()
time.sleep(2 ** attempt)
body = response.json()
if body.get("status") != "completed":
raise RuntimeError(body.get("error") or body.get("status"))
try:
return body["outputs"][0]["data"]
except (KeyError, IndexError, TypeError) as exc:
raise RuntimeError("Missing outputs[0].data") from exc
Discover and page content gaps
def discover(category="Science", language="en", pages=2):
cursor, rows, seen = 0, [], set()
for _ in range(pages):
page = call("tiktok/app-v3/creator-search-insights", {
"tab": "content_gap",
"limit": 20,
"offset": cursor,
"category_filters": category,
"language_filters": language,
})
for item in page.get("inspiration_list") or []:
query_id = item.get("query_id_str")
if not query_id or query_id in seen:
continue
seen.add(query_id)
rows.append({
"query_id": query_id,
"query": item.get("query_text"),
"labels": item.get("query_labels") or [],
"popularity": item.get("popularity"),
"video_count": item.get("video_num"),
"trend": item.get("trending_seq") or [],
})
if not page.get("has_more"):
break
cursor = page.get("cursor")
if cursor is None:
raise RuntimeError("has_more was true but cursor was missing")
return rows
Keep the explicit content_gap label in the record. A high popularity value is interesting, but it does not independently prove unmet demand. Also deduplicate on query_id_str: live feeds can shift between requests.
Enrich one candidate
def enrich(row):
detail = call("tiktok/app-v3/creator-search-insights-detail", {
"query_id_str": row["query_id"],
"time_range": "past_30_days",
"dimension_list": "age,country",
})
trend = call("tiktok/app-v3/creator-search-insights-trend", {
"query_id_str": row["query_id"],
"from_tab_path": "TRENDING,TOPICS",
"query_analysis_required": True,
})
videos = call("tiktok/app-v3/creator-search-insights-videos", {
"keyword": row["query"], "offset": 0, "count": 20,
})
related = [item.get("aweme_info")
for item in videos.get("search_item_list") or []
if item.get("aweme_info")]
return {**row, "detail": detail, "trend_context": trend,
"videos": related}
Do not rank directly on one undocumented internal field. A safer local score uses only stable, visible inputs and keeps its assumptions obvious:
def editorial_score(row):
popularity = row.get("popularity") or 0
supply = max(row.get("video_count") or 1, 1)
recent = row.get("trend") or []
momentum = (recent[-1] - recent[0]) if len(recent) > 1 else 0
return round(popularity / (supply ** 0.5) + max(momentum, 0) * 0.1, 2)
This score is a queueing aid, not a TikTok metric. Save the formula version next to each result so later reports remain reproducible.
Configuration reference
| Endpoint | Required | Useful optional fields | Pagination |
|---|---|---|---|
creator-search-insights | none | tab, limit, offset, category and language filters | returned cursor → next offset |
creator-search-insights-detail | query_id_str | time_range, dimension_list, custom dates | none observed |
creator-search-insights-trend | query_id_str | from_tab_path, query_analysis_required | none observed |
creator-search-insights-videos | keyword | count, offset | returned cursor/has-more fields |
Production rules that prevent bad briefs
Cache discovery pages for a short, fixed window and store the capture time. Retry transport errors and 429/5xx responses, but do not retry a 400 with the same payload. Log the run ID without logging the API key. Put a maximum page count on every job. If an endpoint returns a new shape, quarantine that run instead of silently emitting empty rows.
Most importantly, require a human check before a topic becomes a brief. The reviewer should open several related posts, confirm language and intent, note whether results are current, and reject ideas that only look relevant because of an ambiguous phrase. Public data can guide research; it does not grant rights to reuse creators’ media or personal information.
Troubleshooting
- Detail returns little demographic data: keep it nullable and continue with trend and video validation.
- The next page repeats ideas: deduplicate by
query_id_str. - A numeric query ID changes in JavaScript: transport and store
query_id_str, never a floating-point number. - The list changes between runs: expected for a live recommendation surface; compare dated snapshots.
- The call completes without
outputs[0].data: fail the job and preserve the response for debugging.
FAQ
Is a content-gap label a guarantee that a video will perform?
No. It is an observed platform label for a query, not a prediction for a specific account, creative, or posting time.
Can I monitor only one niche?
Yes. Use category and language filters, then add your own allowlist of terms. The API reference lists the currently accepted category and language values.
Should an LLM choose the final topics?
Use it to summarize clusters after deterministic filtering. Keep the source rows, scores, and related-video links available to an editor.
Where do I inspect the current parameters?
Open the endpoint inside the TikTok API catalog; schemas and availability can change after this dated test.