quick start
- sign in and make an api key at searchtok.app/app/settings/keys
- keep it in an environment variable:
export SEARCHTOK_API_KEY=st_live_...
- check it works. this one is free:
curl https://searchtok.app/api/v1/usage \
-H "Authorization: Bearer $SEARCHTOK_API_KEY"
keys
send your key on every request as Authorization: Bearer st_live_.... make and revoke keys at searchtok.app/app/settings/keys. every endpoint needs a searchtok plan, except usage and profile, which always work so an agent can tell you where you stand.
credits
each call costs credits from one pool, shared by every way in. every signed-in response says what it used and what's left in two headers:
credits-used: credits this request usedcredits-left: credits left after it
a call that fails is refunded. send the same Idempotency-Key header to retry a call without paying twice. credit costs are provisional and may change before launch.
rate limit
up to 60 requests a minute per key. past that you get a 429 with a Retry-After header.
errors
every error has the same shape. hint says what to try next.
{
"error": {
"code": "no_credits",
"message": "search_content costs 0 credits and 12 are left",
"hint": "..."
}
}
| code | status | what it means |
|---|
bad_input | 400 | the request doesn't match the input schema; message says what's wrong |
unsupported | 400 | that platform or kind of link isn't supported by this endpoint |
unauthorized | 401 | no key, or the key isn't valid or was revoked |
no_plan | 402 | the account has no searchtok plan |
no_credits | 402 | not enough credits left for this call |
not_allowed | 403 | this key can't do that |
not_found | 404 | the post, account, run or endpoint doesn't exist (or is private) |
unknown_tool | 404 | no such endpoint |
budget_exceeded | 422 | the call needed more data than one call may use; narrow it down |
rate_limited | 429 | more than 60 requests in a minute with this key |
internal | 500 | something broke on our side; credits are refunded |
not_available | 503 | switched off for now (research has a kill switch and a daily cap) |
unavailable | 503 | tiktok or instagram data couldn't be fetched right now; credits are refunded |
POST/api/v1/search
search tiktok and reels
credits: 20 per querysearch_content
Find the TikToks and Instagram Reels that did best for a topic or niche, ranked by outlier score (a post's views ÷ its account's usual views). searchtok's main tool.
Use it for: what's working in a niche, viral examples of a format, hooks and ideas to copy.
Don't use it for: a known account (get_account), a known post (analyze_post), posts like a given post (find_similar), finding creators (find_accounts).
Each query runs several searches plus searchtok's index, removes repeats and scores each post. Several angles or niches? Pass them together as queries (up to 5): they run at the same time, much faster than a call each.
Returns compact rows (id, url, handle, views, outlier_score with its explanation, caption, hook, format), what was searched and a dashboard link. Top rows carry their hook word for word, so analyze_post is rarely needed just to quote one. Paid posts are flagged paid.
input
query string- What to look for, in the words people would search on TikTok: a niche, topic or format ("habit tracker app", "smile transformation"). Short beats long.
queries string[]- Up to 5 searches in one call, run at the same time (several angles on a niche, or several niches). Faster than one call per query; each query costs what a single search costs. Rows say which queries found them.
platforms ("tiktok" | "instagram")[]- Default: both
timeframe "7d" | "30d" | "90d" | "1y"- Only posts from this long ago or newer. Default: any time
language string- Two-letter language, e.g. "en". Posts in other languages are dropped; posts whose language is unknown are kept
region string- Two-letter country that biases TikTok results (it doesn't filter them)
limit integer- Rows to return. Default 15 (more for several queries). Results are cut to about 2k tokens (plus about 1k per extra query); the rest are at
link
returns
search_id string required- This search's id in searchtok
rows object[] required- Best first: id, platform, url, handle, posted_at, views, outlier_score, outlier_explained, caption, hook, format, why_matched
total_found number required- Distinct posts found before the limit
searched object[] required- Each search that ran, and how many posts it returned
missed string[]- Calls that failed or were too slow. Results may be thinner than usual
left_out number- Weaker rows (under 1x their account's usual views, or no score) left out. They're at
link truncated number- Rows left out to keep this result short. They're at
link note string- Said when there's nothing to show or no scores, with what to try
link string required- This search in the searchtok dashboard
example
curl -X POST https://searchtok.app/api/v1/search \
-H "Authorization: Bearer $SEARCHTOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"habit tracker app"}'
example response (real output, lists cut short)
{
"search_id": "00000000-0000-4000-8000-000000000001",
"rows": [
{
"id": "tiktok:7377548155597950251",
"platform": "tiktok",
"url": "https://www.tiktok.com/@spaceyflower/video/7377548155597950251",
"handle": "spaceyflower",
"posted_at": "2024-06-07",
"views": 565678,
"outlier_score": 1457.9,
"outlier_explained": "1457.9x this account's usual views (median 388 over its last 20 posts)",
"caption": "#habittracker #habittracker2024 #BulletJournal #bulletjournaling #yearinpixels…",
"hook": null,
"format": "video",
"cover_url": "https://searchtok.app/api/c/tiktok:7377548155597950251",
"why_matched": [
"#habittracker"
]
},
"... 14 more"
],
"total_found": 111,
"searched": [
{
"platform": "instagram",
"search": "reels search \"habit tracker app\"",
"found": 9
},
"... 5 more"
],
"link": "https://searchtok.app/app/searches/00000000-0000-4000-8000-000000000001"
}
POST/api/v1/accounts/find
find creators and brands
15 creditsfind_accounts
Find TikTok or Instagram creators and brands in a niche, with how well their posts do: followers, usual (median) views and outlier rate (how often a post gets 3x+ their usual views).
Use it for: creators to learn from or partner with, competitors in a niche, accounts that reliably go viral.
Don't use it for: one account you already know (get_account), a brand's website or name (analyze_brand), posts (search_content).
Returns up to 8 accounts (6 on Instagram), most reliable outlier-makers first, each with id, handle, url, followers, median views, outlier rate and posts per week. Never returns or infers gender, ethnicity or age.
input
query string required- A niche or topic ("habit tracker app"), or a name to look up
platform "tiktok" | "instagram"- Default: tiktok
returns
rows object[] requiredmissed string[]truncated numbernote string
example
curl -X POST https://searchtok.app/api/v1/accounts/find \
-H "Authorization: Bearer $SEARCHTOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"habit tracker app"}'
example response (real output, lists cut short)
{
"rows": [
{
"id": "tiktok:7597922150545507350",
"platform": "tiktok",
"handle": "completerapp",
"display_name": "Completer - Habit Tracker",
"url": "https://www.tiktok.com/@completerapp",
"followers": 667,
"verified": null,
"bio": null,
"median_views": 674,
"posts_sampled": 30,
"baseline_explained": "median 674 views over its last 30 posts (2+ days old)",
"outlier_rate": 0.17,
"posts_per_week": 1.1
},
"... 7 more"
]
}
GET/api/v1/accounts/{platform}/{handle}
look up an account
3 creditsget_account
Look up one TikTok or Instagram account: profile, its usual (median) views, outlier rate, posting rate, format mix and its best recent posts by outlier score.
Use it for: checking a creator or competitor you already know by handle or url, and following up on an account from another searchtok result.
Don't use it for: finding accounts (find_accounts), a brand known only by website or name, or comparing brands (analyze_brand).
Returns the profile, numbers (all worked out from its latest 30 posts) and up to 5 top recent posts with outlier scores and ids for analyze_post.
input
platform "tiktok" | "instagram" required in the path- tiktok or instagram
handle string required in the path- The account's handle, with or without "@"
returns
account object requirednumbers object requiredtop_outliers object[] required- Its best recent posts by outlier score
link string required
example
curl https://searchtok.app/api/v1/accounts/tiktok/completerapp \
-H "Authorization: Bearer $SEARCHTOK_API_KEY"
example response (real output, lists cut short)
{
"account": {
"id": "tiktok:7597922150545507350",
"platform": "tiktok",
"handle": "completerapp",
"display_name": "Completer - Habit Tracker",
"url": "https://www.tiktok.com/@completerapp",
"followers": 667,
"following": 493,
"posts": 79,
"likes_total": 13861,
"verified": false,
"bio": "Build better habits with a simple habit tracker. Download \"COMPLETER\" on the App Store. 📲 getcompleter.app"
},
"numbers": {
"median_views": 674,
"posts_sampled": 30,
"baseline_explained": "median 674 views over its last 30 posts (2+ days old)",
"outlier_rate": 0.17,
"posts_per_week": 1.1,
"format_mix": {
"carousel": 0.87,
"video": 0.13
}
},
"top_outliers": [
{
"id": "tiktok:7616340301648563488",
"platform": "tiktok",
"url": "https://www.tiktok.com/@completerapp/photo/7616340301648563488",
"handle": "completerapp",
"posted_at": "2026-03-12",
"views": 216733,
"outlier_score": 344,
"outlier_explained": "344x this account's usual views (median 630 over its last 29 posts)",
"caption": "Discover my favorite iPhone apps for 2026 — handy tools to stay productive, org…",
"hook": null,
"format": "carousel",
"cover_url": "https://searchtok.app/api/c/tiktok:7616340301648563488"
},
"... 4 more"
],
"link": "https://searchtok.app/app/accounts/tiktok%3A7597922150545507350"
}
POST/api/v1/brands/analyze
analyse a brand
credits: 5 per brand (the brand plus each one compared)analyze_brand
Analyse a brand's or creator's organic TikTok and Instagram from its website, handle or name, optionally side by side with up to 3 others (compare).
Use it for: competitor research, 'how is X doing on TikTok', benchmarking your brand against others, a brand known only by its website.
Don't use it for: one handle when you only need its profile and top posts (get_account, cheaper), finding brands in a niche (find_accounts), ads (organic posts only).
A website is read for its TikTok and Instagram links; a name is looked up with TikTok account search (resolved_from says how, check it). Returns per account: followers, usual (median) views, outlier rate, posts per week, format mix, last-30-days vs previous-30-days posting and views, top outlier posts and quoted hooks of analysed posts. Every number is worked out in code; interpret them yourself.
input
website string- The brand's site, e.g. "acme.com". Its TikTok and Instagram links are read from the page
handle string- A TikTok or Instagram handle, e.g. "@acme"
platform "tiktok" | "instagram"- The handle's platform. Default: tiktok
name string- The brand's name, looked up with TikTok account search
compare object[]- Up to 3 other brands, same fields, analysed side by side
returns
brands object[] required- The brand first, then each one compared
missed string[]link string required
example
curl -X POST https://searchtok.app/api/v1/brands/analyze \
-H "Authorization: Bearer $SEARCHTOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"handle":"@completerapp","compare":[{"handle":"@thehabittracker"}]}'
example response (real output, lists cut short)
{
"brands": [
{
"brand": "@completerapp",
"resolved_from": "handle on tiktok",
"accounts": [
{
"id": "tiktok:7597922150545507350",
"platform": "tiktok",
"handle": "completerapp",
"url": "https://www.tiktok.com/@completerapp",
"followers": 667,
"median_views": 674,
"posts_sampled": 30,
"outlier_rate": 0.17,
"posts_per_week": 1.1,
"format_mix": {
"carousel": 0.87,
"video": 0.13
},
"change_30d": {
"posts_last_30d": 2,
"posts_prev_30d": 0,
"median_views_last_30d": 1165,
"median_views_prev_30d": null
},
"top_outliers": [
{
"id": "tiktok:7616340301648563488",
"platform": "tiktok",
"url": "https://www.tiktok.com/@completerapp/photo/7616340301648563488",
"handle": "completerapp",
"posted_at": "2026-03-12",
"views": 216733,
"outlier_score": 344,
"outlier_explained": "344x this account's usual views (median 630 over its last 29 posts)",
"caption": "Discover my favorite iPhone apps for 2026 — handy tools to stay productive, org…",
"hook": null,
"format": "carousel"
},
"... 2 more"
],
"hooks": []
}
]
},
"... 1 more"
],
"link": "https://searchtok.app/app/brief?accounts=tiktok%3A7597922150545507350,tiktok%3A7285627514402653186"
}
POST/api/v1/posts/analyze
analyse a post
credits: 6 per postanalyze_post
Everything about a TikTok or Instagram post, by url or searchtok id. For several posts, pass them together as ids or urls (up to 8): they're analysed at the same time, much faster than one call each.
Use it for: breaking down why a post worked, quoting its hook or script, checking a post from another searchtok result or a link the user pasted.
Don't use it for: finding posts (search_content), posts like this one (find_similar), an account's overall numbers (get_account).
Returns fresh views, likes, comments, shares and saves, the account's usual views, the outlier score with its explanation, the transcript and, once analysed, on-screen text, spoken and on-screen hooks, format and structure (analysis_status says whether that has run). Hooks are quoted word for word from the post, never written by a model. With ids or urls, a list (posts), one per post, in order.
input
url string- The post's TikTok or Instagram url
id string- A searchtok post id from another result, e.g. "tiktok:7301234567890"
urls string[]- Several posts by url (up to 8 in all, with ids), analysed at the same time
ids string[]- Several posts by searchtok id (up to 8 in all, with urls), analysed at the same time
returns
no input.
example
curl -X POST https://searchtok.app/api/v1/posts/analyze \
-H "Authorization: Bearer $SEARCHTOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.tiktok.com/@spaceyflower/video/7377548155597950251"}'
example response (real output, lists cut short)
{
"post": {
"id": "tiktok:7377548155597950251",
"platform": "tiktok",
"url": "https://www.tiktok.com/@spaceyflower/video/7377548155597950251",
"handle": "spaceyflower",
"posted_at": "2024-06-07",
"caption": "#habittracker #habittracker2024 #BulletJournal #bulletjournaling #yearinpixels #ratemyday",
"hashtags": [
"habittracker",
"... 5 more"
],
"duration_s": 30.3,
"format": "video",
"sound": "original sound - neil.chapman1",
"cover_url": "https://searchtok.app/api/c/tiktok:7377548155597950251"
},
"metrics": {
"views": 565678,
"likes": 8281,
"comments": 66,
"shares": 729,
"saves": 2545,
"views_per_day": 658,
"engagement_rate": 0.016
},
"baseline": {
"median_views": 388,
"posts_sampled": 20,
"confidence": "ok"
},
"outlier_score": 1457.9,
"outlier_explained": "1457.9x this account's usual views (median 388 over its last 20 posts)",
"transcript": {
"status": "available",
"text": "the reason why I wake up at 5:00am and I run every morning is it because I'm a Marathon runner or because I'm some kind of triathlete it's because I fucking hate running man I hate getting up early even more so I'm starting the day by building a non negotiable standard for myself which I've come to realize is the only way we can ever grow and improve when people say no one's coming to save you they're not talking about how no one's coming to help you reach your goals talking about how no one's coming to save you from yourself because without standards man we always lose direction and we always fall back to our lowest automatic systems binging procrastinating clinging to comfort",
"cut": false
},
"on_screen_text": null,
"spoken_hook": null,
"on_screen_hook": null,
"format_label": null,
"second_format_label": null,
"structure": null,
"seen": null,
"is_ad": null,
"country": "US",
"analysis_status": "not_available",
"link": "https://searchtok.app/app/posts/tiktok%3A7377548155597950251"
}
POST/api/v1/similar
find similar posts
12 creditsfind_similar
Find posts like a given TikTok or Instagram post (same topic or format), ranked by outlier score.
Use it for: 'more like this', finding other creators doing the same format, checking whether a format works beyond one account.
Don't use it for: a topic in words (search_content), one post's details (analyze_post).
Searches the post's own hashtags and caption words on its platform, plus searchtok's index by meaning. Returns the same compact rows as search_content (the source post left out), with why_matched saying which seed found each. Paid posts are flagged paid.
input
url string- The post's TikTok or Instagram url
id string- A searchtok post id, e.g. "tiktok:7301234567890"
limit integer- Default 10
returns
source object requiredrows object[] requiredtotal_found number requiredsearched string[] requiredmissed string[]truncated numbernote string
example
curl -X POST https://searchtok.app/api/v1/similar \
-H "Authorization: Bearer $SEARCHTOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id":"tiktok:7377548155597950251","limit":10}'
example response (real output, lists cut short)
{
"source": {
"id": "tiktok:7377548155597950251",
"url": "https://www.tiktok.com/@spaceyflower/video/7377548155597950251",
"handle": "spaceyflower",
"views": 565678
},
"rows": [
{
"id": "tiktok:7400516824174988590",
"platform": "tiktok",
"url": "https://www.tiktok.com/@spaceyflower/video/7400516824174988590",
"handle": "spaceyflower",
"posted_at": "2024-08-07",
"views": 1360488,
"outlier_score": 2846.2,
"outlier_explained": "2846.2x this account's usual views (median 478 over its last 27 posts)",
"caption": "#habittracker2024 #habittracker #BulletJournal #bulletjournaling #yearinpixels…",
"hook": null,
"format": "video",
"cover_url": "https://searchtok.app/api/c/tiktok:7400516824174988590",
"why_matched": [
"#habittracker2024"
]
},
"... 9 more"
],
"total_found": 37,
"searched": [
"tiktok #habittracker (20)",
"... 1 more"
]
}
POST/api/v1/research
research a question
credits: 50 quick, 150 deepresearch
Hand a whole question to searchtok's own research agent, which runs many searches and writes a checked report, every claim citing real posts.
Use it when the user asks for a full research report, or wants a big question run in the background (depth deep, then get_research for the answer).
Don't use it for: most questions. Calling search_content, find_accounts and analyze_post yourself is faster, cheaper and shows each step. It costs the most credits.
Returns a summary, the posts it's based on (ids, outlier scores, quoted hooks) and a dashboard link. quick waits about 15s; deep returns at once with its search_id and link, and the answer is ready within about a minute (get_research).
input
question string required- The research question, in the user's words
depth "quick" | "deep"- quick: under 15s, only already-analysed posts. deep: up to a minute, analyses new posts. Default quick
returns
search_id string required- This run's id in searchtok
status "done" | "partial" | "running" required- partial: some data couldn't be fetched, see answer.gaps. running: a deep run, still working
answer any- The checked answer: summary, patterns (each with claims citing post ids, with their numbers and quoted hooks) and example posts, what was searched, gaps. Not set while running
posts any[]- More results, best first: id, url, handle, views, outlier_score, caption
events_url string- Live progress and the answer as server-sent events (deep runs)
link string required- This run in the searchtok dashboard
outage string- Set when the run has no answer because searchtok's side failed: its credits were given back
note string
example
curl -X POST https://searchtok.app/api/v1/research \
-H "Authorization: Bearer $SEARCHTOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"question":"smile transformation"}'
example response (real output, lists cut short)
{
"search_id": "00000000-0000-4000-8000-000000000001",
"status": "done",
"answer": {
"summary": "Smile transformation videos work best as a clear before and after reveal, or as an emotional story about a person who hid their smile. Dentists also get reach from a rating question or a warning about bad veneers. No hooks were analysed, so none are quoted.",
"groups": [
{
"pattern": "Rate-this-smile reveal",
"why": "A direct question invites comments and replays, and a clinic can film it with any patient case.",
"claims": [
{
"text": "A dentist's rate-this-smile veneers video reached 10.99M views, 226.7x the account's usual.",
"post_ids": [
"tiktok:7598707763442748703"
],
"numbers": [
{
"post_id": "tiktok:7598707763442748703",
"metric": "views",
"value": 10989752
},
"... 1 more"
],
"hooks": []
},
"... 1 more"
],
"examples": [
"tiktok:7598707763442748703",
"... 1 more"
]
},
"... 3 more"
],
"searched": [
"smile transformation",
"... 1 more"
],
"gaps": [
"TikTok only; Instagram not searched.",
"... 3 more"
]
},
"posts": [
{
"id": "tiktok:7002262291064360197",
"url": "https://www.tiktok.com/@austincosmeticdentistry/video/7002262291064360197",
"handle": "austincosmeticdentistry",
"views": 84080,
"outlier_score": 81.8,
"caption": "#beforeandafter #beforeafter #smile #smiledekedekho #makeover #transformation #…"
},
"... 18 more"
],
"link": "https://searchtok.app/app/searches/00000000-0000-4000-8000-000000000001"
}
GET/api/v1/research/{search_id}
get a research answer
freeget_researchworks without a plan
Fetch a research run's answer by its search_id: a deep run that research started in the background, or any earlier run.
Use it when research returned status running, or the user asks about an earlier research run.
Don't use it for: search_content results (they come back in full straight away).
Returns the status and, once it's done, the same answer research returns, with a dashboard link. Free.
input
search_id string required in the path- The run's search_id, from POST /research
returns
search_id string requiredquestion string requireddepth "quick" | "deep" requiredstatus "queued" | "running" | "done" | "partial" | "failed" | "cancelled" required- running: not finished yet, ask again in 20 to 30 seconds. partial: some data couldn't be fetched, see answer.gaps
answer any- The checked answer, the same shape research returns. Set once the run is done or partial
posts any[]- More results, best first
error stringlink string required- This run in the searchtok dashboard
events_url string- Live progress as server-sent events, while it runs
created_at string requiredfinished_at string | null requiredtruncated numbernote string
example
curl https://searchtok.app/api/v1/research/$SEARCH_ID \
-H "Authorization: Bearer $SEARCHTOK_API_KEY"
example response (real output, lists cut short)
{
"search_id": "00000000-0000-4000-8000-000000000001",
"question": "smile transformation",
"depth": "deep",
"status": "done",
"answer": {
"summary": "Smile transformation videos work best as a clear before and after reveal, or as an emotional story about a person who hid their smile. Dentists also get reach from a rating question or a warning about bad veneers. No hooks were analysed, so none are quoted.",
"groups": [
{
"pattern": "Rate-this-smile reveal",
"why": "A direct question invites comments and replays, and a clinic can film it with any patient case.",
"claims": [
{
"text": "A dentist's rate-this-smile veneers video reached 10.99M views, 226.7x the account's usual.",
"post_ids": [
"tiktok:7598707763442748703"
],
"numbers": [
{
"post_id": "tiktok:7598707763442748703",
"metric": "views",
"value": 10989752
},
"... 1 more"
],
"hooks": []
},
"... 1 more"
],
"examples": [
"tiktok:7598707763442748703",
"... 1 more"
]
},
"... 3 more"
],
"searched": [
"smile transformation",
"... 1 more"
],
"gaps": [
"TikTok only; Instagram not searched.",
"... 3 more"
]
},
"posts": [
{
"id": "tiktok:7002262291064360197",
"url": "https://www.tiktok.com/@austincosmeticdentistry/video/7002262291064360197",
"handle": "austincosmeticdentistry",
"views": 84080,
"outlier_score": 81.8,
"caption": "#beforeandafter #beforeafter #smile #smiledekedekho #makeover #transformation #…"
},
"... 15 more"
],
"link": "https://searchtok.app/app/searches/00000000-0000-4000-8000-000000000001",
"created_at": "2026-10-16T16:11:50.841Z",
"finished_at": "2026-10-16T16:11:50.844Z",
"truncated": 3
}
POST/api/v1/ideas
content ideas
150 creditscontent_ideas
Content ideas for the user's profile, each built on one video in their niche that beat its account's usual views 2x+ and fits the user (plus up to 2 more posts in its format), with hooks quoted word for word and the beats to film.
Each call finds new videos: a video shown in an earlier set for the profile is never shown again.
Use it for: 'what should I post', a content plan, ideas in a given format ('talking head ideas') or from an account's posts.
Don't use it for: finding posts to study (search_content). Ideas never invent hooks or formats: fewer new videos means fewer ideas, said in note.
Takes about a minute. Returns the ideas with their post ids, outlier scores and quoted hooks, plus a dashboard link.
input
profile_id string- Which saved profile (get_profile). Default: the first
count integer- How many ideas, 1 to 8. Default 8
focus string- Narrow the ideas: a theme ("leg day"), a format ("talking head", "talking head fitness") or an account ("@acme": ideas from its posts)
returns
set_id string requiredprofile_id string | null requiredstatus "running" | "done" | "thin" | "empty" required- thin: fewer new videos than ideas asked for; empty: none (the credits go back). running: started in the background (the dashboard)
focus string | null requiredideas object[] requirednote string | null required- Said when there are fewer ideas than asked for, and why
formats number required- Formats among the fitting posts read
scanned number required- Posts checked
searched string[] requireddropped number required- Ideas the checks removed
link string required- These ideas in the searchtok dashboard
example
curl -X POST https://searchtok.app/api/v1/ideas \
-H "Authorization: Bearer $SEARCHTOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
example response (real output, lists cut short)
{
"set_id": "00000000-0000-4000-8000-000000000001",
"profile_id": "00000000-0000-4000-8000-000000000001",
"status": "thin",
"focus": null,
"ideas": [
{
"id": "idea-1",
"title": "Home tour with a personal story",
"format": "day in the life / vlog",
"format_id": "day-in-the-life",
"fits": {
"text": "A house tour told with a real story gives home buyers and sellers in your area a reason to trust you, which suits a residential realtor.",
"phrase": "a residential realtor",
"brief_field": "what"
},
"why": [
"It opens by saying plainly what it is: a house tour, and the same words appear on screen.",
"... 3 more"
],
"posts": [
{
"id": "tiktok:7649541016927735054",
"platform": "tiktok",
"url": "https://www.tiktok.com/@meredithboydd/video/7649541016927735054",
"handle": "meredithboydd",
"views": 3518505,
"outlier_score": 2398.4,
"outlier_explained": "2398.4x this account's usual views (median 1.5k over its last 20 posts)",
"on_screen_hook": "let's do a house tour",
"own": false
},
"... 2 more"
],
"proof": 7,
"beats": [
{
"text": "Open outside the home and say you are doing a house tour, with the same words on screen.",
"post_id": "tiktok:7649541016927735054"
},
"... 4 more"
],
"hooks": [
{
"text": "let's do a house tour",
"post_id": "tiktok:7649541016927735054",
"field": "on_screen"
},
"... 4 more"
]
}
],
"note": "only 1 proven format in your niche right now. more niche words in your brief find more.",
"formats": 2,
"scanned": 207,
"searched": [
"Instagram reels search \"realtor\" (10)",
"... 13 more"
],
"dropped": 1,
"link": "https://searchtok.app/app/ideas?set=00000000-0000-4000-8000-000000000001"
}
POST/api/v1/profile/build
build a profile
10 creditsbuild_profile
Turn one link into the user's searchtok profile: an App Store or Play link, a website, a TikTok or Instagram handle, or a sentence about the business.
Use it when the user has no profile yet (get_profile is empty) or adds a new app, business or client. Agencies keep one profile per client.
Don't use it to research someone else's account (get_account, analyze_brand).
It reads the listing, site or account. Returns (and saves) the brief: type (app, service, creator, ecommerce or agency), what it is, who it's for, 5 to 10 niche phrases in the words people use on TikTok, content goal, language, region, its own accounts, and 3 to 5 lookalike TikTok accounts that account search returned (never made up). Building the same link again updates that profile.
input
input string required- What the profile is for: an App Store or Google Play link, a website ("acme.com"), a TikTok or Instagram handle or profile url ("@acme", "instagram @acme"), or a sentence ("i run a dog grooming van in leeds")
save boolean- Save it as one of the user's profiles. Default true. Building the same link again updates that profile
returns
profile_id string | null required- Pass it to content_ideas as profile_id. Null when save was false
saved "created" | "updated" | "not_saved" requiredsource object requiredbrief object requiredmissed string[]- Pages, accounts or searches that couldn't be read
link string required
example
curl -X POST https://searchtok.app/api/v1/profile/build \
-H "Authorization: Bearer $SEARCHTOK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"opal.so"}'
example response (real output, lists cut short)
{
"profile_id": "00000000-0000-4000-8000-000000000001",
"saved": "created",
"source": {
"kind": "website",
"url": "https://opal.so"
},
"brief": {
"type": "app",
"name": "Opal",
"what": "Opal is a screen time and focus app for iPhone, iPad, Mac and Android. It blocks apps you choose, runs a focus timer and gives a daily score for how your tech use fits your wellbeing.",
"who_for": "People who spend too much time on their phone and want to scroll less and focus more.",
"niche": [
"screen time app",
"... 9 more"
],
"content_goal": "downloads",
"language": "en",
"region": null,
"own_accounts": [
{
"platform": "tiktok",
"handle": "opal",
"url": "https://www.tiktok.com/@opal",
"followers": 10931,
"median_views": 4433,
"posts_per_week": 0.3,
"found": "linked from opal.so"
},
"... 1 more"
],
"lookalikes": [
{
"platform": "tiktok",
"handle": "bepresent.app",
"display_name": "BePresent | Screen Time App",
"url": "https://www.tiktok.com/@bepresent.app",
"followers": 10124,
"verified": null,
"bio": null,
"why": "a screen time app, by its name",
"found_with": "screen time app"
},
"... 4 more"
],
"gaps": [
"The material doesn't say which country they mainly serve. Posts mention the US, UK, Japan and Australia.",
"... 2 more"
]
},
"link": "https://searchtok.app/app/profiles"
}
GET/api/v1/profile
get my profile
freeget_profileworks without a plan
The user's saved searchtok profile(s): what they make content for (an app, a business, a creator...), their niche, audience and language.
Use it first when a question depends on who the user is ('ideas for me', 'what should I post'), so searches match their niche and language.
Don't use it to look up other accounts (get_account). Free.
Returns each profile's id, type, name, link, language, region and brief.
input
no input.
returns
profiles object[] requirednote stringlink string required
example
curl https://searchtok.app/api/v1/profile \
-H "Authorization: Bearer $SEARCHTOK_API_KEY"
example response (real output, lists cut short)
{
"profiles": [
{
"id": "00000000-0000-4000-8000-000000000001",
"type": "app",
"name": "example habit app (test data)",
"source_url": "https://app.example",
"language": "en",
"region": null,
"brief": {
"note": "test data for this example",
"niche": "habit tracker app"
}
}
],
"link": "https://searchtok.app/app/profiles"
}
GET/api/v1/usage
check credits
freeget_usageworks without a plan
The user's searchtok plan, credits left, when they reset, and what each tool costs.
Use it before a long run of calls (or after a no_credits error) so you can warn the user before they run out. Free and always works, even without a plan.
Returns plan, plan_status, can_use_tools, credits_left, credits_used, resets_at, credit_costs and upgrade_url.
input
no input.
returns
plan string requiredplan_status string requiredcan_use_tools boolean required- False without an active plan or with no credits left
credits_left number requiredcredits_used number required- Used this credit month; refunded calls don't count
monthly_allowance number required- The plan's monthly credits. 0 without a plan
period_start string | null required- When this credit month started (ISO date)
resets_at string | null required- When the plan renews and credits are topped up (ISO date). Null without a plan
by_surface object[] required- This month's credits per door: dashboard, mcp, cli, api
by_tool object[] required- This month's credits per tool, most first
credit_costs object required- What each tool costs
upgrade_url string required- Pick a plan or buy more credits
usage_url string required- The same numbers on the dashboard
example
curl https://searchtok.app/api/v1/usage \
-H "Authorization: Bearer $SEARCHTOK_API_KEY"
example response (real output, lists cut short)
{
"plan": "creator",
"plan_status": "active",
"can_use_tools": true,
"credits_left": 1000,
"credits_used": 0,
"monthly_allowance": 1250,
"period_start": "2026-10-16",
"resets_at": "2026-11-03",
"by_surface": [
{
"surface": "dashboard",
"credits": 0,
"calls": 0
},
"... 3 more"
],
"by_tool": [],
"credit_costs": {
"search_content": 20,
"find_accounts": 15,
"get_account": 3,
"analyze_brand": "5 per brand",
"analyze_post": 6,
"find_similar": 12,
"research": "50 quick, 150 deep",
"content_ideas": 150,
"build_profile": 10,
"get_profile": 0,
"get_usage": 0
},
"upgrade_url": "https://searchtok.app/app/billing",
"usage_url": "https://searchtok.app/app/usage"
}