TikTok
TikTok profiles, videos, comments, single-comment lookup, search, trending, sounds, transcripts, and on-screen text through one unified API
TikTok accounts and their videos, the comment section down to a single named comment, three flavours of search, per-country trending, sound-level discovery, spoken transcripts, and the text burned into the frame. Commerce data lives on the separate TikTok Shop API.
Base URL: /v1/tiktok/...
A TikTok video carries three different texts and each has its own endpoint.
The caption comes back on post, the spoken words on
post/transcript, and the on-screen overlay on video/screen-text.
post also surfaces the creator's own text stickers at
post.ext.on_screen_texts without the OCR pass.
Quickstart
1. Fetch a profile
curl "https://www.socialcrawl.dev/v1/tiktok/profile?handle=charlidamelio" \
-H "x-api-key: YOUR_API_KEY"2. Fetch their content
curl "https://www.socialcrawl.dev/v1/tiktok/profile/videos?handle=charlidamelio" \
-H "x-api-key: YOUR_API_KEY"3. Read computed fields
When an endpoint supports a computed field and the required source inputs are present, the unified response includes that optional field. Depending on the endpoint, optional fields can include engagement_rate, language, content_category, and estimated_reach. See Computed fields for formulas, clamping rules, and null semantics.
Accounts
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/tiktok/profile | 1 | Display name, bio, follower and like counts, verification, the two-letter region, numeric user_id | handle or user_id |
GET /v1/tiktok/profile/full | 5 | Profile, recent videos, and computed metrics in one call instead of three | handle or user_id, posts (1-100, default 25), include |
GET /v1/tiktok/profile/videos | 1 | A page of recent public videos with captions, view/like/comment/share counts | handle, sort_by, region, max_cursor |
GET /v1/tiktok/profile/region | 1 | Just the two-letter country code, for routing or de-duplicating by market | handle |
GET /v1/tiktok/user/followers | 1 | Who follows this account, paged | handle or user_id, min_time |
GET /v1/tiktok/user/following | 1 | Who this account follows, paged | handle or user_id, min_time |
GET /v1/tiktok/user/audience | 5 | Top follower countries with sampled counts and each country's share | handle |
GET /v1/tiktok/user/live | 1 | The account's live room: cover, title, start time, status, viewer and entry counts | handle |
user/audience is geography only. Age and gender are not publicly available on TikTok, or on any other platform here, so no endpoint returns them.
user/live can still return a room after the broadcast has ended. Check is_live, status and the start time yourself rather than treating a response as proof the stream is on. Answers are cached for at most a minute, an account that has never streamed returns is_live: false with an empty room, and a handle that does not exist is a 404 at 0 credits.
Videos and text
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/tiktok/post | 1 | One video in full: caption, engagement, author, sound, video metadata | url, download_media, region |
GET /v1/tiktok/post/transcript | 10 | The spoken words from the video's caption track | url, use_ai_as_fallback, language |
GET /v1/tiktok/video/screen-text | 5 | Native text stickers plus AI OCR of the cover frame | url |
GET /v1/tiktok/collection/videos | 1 | The public videos inside a collection URL, paged | url, max_cursor |
download_media=true on post adds direct media URLs. use_ai_as_fallback=true on post/transcript transcribes the audio when TikTok publishes no caption track.
Comments
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/tiktok/post/comments | 1 | A page of the comment section: username, text, likes, reply count | url, cursor |
GET /v1/tiktok/video/comment/replies | 1 | One thread opened up | comment_id and url, both required |
GET /v1/tiktok/comment | 2 or 6 | One named comment, or the comments matching an author or a snippet | comment_url, or post_url plus comment_id |
Pull a page of the section first, then expand any thread you want using that comment's id together with the same video URL.
curl "https://www.socialcrawl.dev/v1/tiktok/post/comments?url=https://www.tiktok.com/@stoolpresidente/video/7623818255903329566" \
-H "x-api-key: YOUR_API_KEY"
curl "https://www.socialcrawl.dev/v1/tiktok/video/comment/replies?url=https://www.tiktok.com/@stoolpresidente/video/7623818255903329566&comment_id=7623828115408274207" \
-H "x-api-key: YOUR_API_KEY"For the whole nested thread in one call, use /v1/prism/comments. See Which endpoint should I use? for the full comparison.
Finding one comment without paging the section
TikTok exposes no fetch-one-comment read, so GET /v1/tiktok/comment is a bounded server-side scan that stops the instant it finds your target. That is why it is priced flat rather than per page. You can address the target three ways:
comment_url: a.../@{handle}/video/{id}?comment_id={cid}link, anm.tiktok.comshare link, or avm.tiktok.comshortlink, all resolved for you. TikTok's share sheet emits thecidbase64-encoded (?cid=NzY1...), and that form is decoded automatically.post_urlpluscomment_id: the same thing in two parts.comment_idmust be the numeric id. If the target is a reply, also passparent_comment_id: a reply URL does not carry its parent, and supplying it resolves the reply directly instead of scanning.- A search:
post_urlplus eitherauthor_usernameortext_contains(an exact, case-insensitive snippet), returning up tomaxmatches (1-20, default 5).
curl "https://www.socialcrawl.dev/v1/tiktok/comment?comment_url=https://www.tiktok.com/@stoolpresidente/video/7623818255903329566?comment_id=7623828115408274207" \
-H "x-api-key: YOUR_API_KEY"Three things make it safe to call speculatively. It costs a flat 2 credits, or 6 with deep_scan=true, which raises the page ceiling and the deadline for deeply-buried comments; there is nothing in between and nothing above. A not-found returns 404 and is fully refunded, so you can point it at a comment that may have been deleted without paying to find out. And every response carries lookup.position_hint: store it, pass it back on the next lookup of the same comment, and the scan probes that last-known location first.
For recurring tracking of a known set, POST /v1/prism/comment-lookup re-checks up to 25 comments in one call with per-item found, not-found, errored, or deferred status.
Search
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/tiktok/search | 1 | Video-only keyword search, paged and filterable | query, date_posted, sort_by, region |
GET /v1/tiktok/search/top | 1 | TikTok's own Top-tab relevance ranking rather than a recency list | query, publish_time, sort_by, region |
GET /v1/tiktok/search/users | 1 to 31 | Accounts matching a term, with follower counts and verification; include=profile adds the bio, region, bio link and category per row | query, cursor, include, limit (1-30) |
GET /v1/tiktok/search/hashtag | 1 | The feed under one tag, the right shape for tracking a campaign | hashtag, region, cursor |
GET /v1/tiktok/search/suggestions | 1 | Autocomplete terms for a partial query, one fixed list | query, region |
search/top returns videos only; it does not include accounts. Expand a seed with search/suggestions first, then call search or search/top.
Bio and region next to user search results
search/users rows carry the counts, the identity and the verification flag, and nothing the search surface does not publish: author.bio, author.location, author.ext.bio_link and author.ext.business_category are null on a plain call. Add include=profile and every row is filled from the profile lookup in the same call: the bio, the two-letter region, the link in the bio and the account category. 1 credit for the page plus 1 per row filled from a fresh lookup, so 31 at most for the 30-row page; rows already in cache are free, rows that could not be filled are refunded and named in _warnings, and the counts the row already carried are never overwritten. limit=N (1 to 30) takes the top N rows and caps the extra credits with it, so limit=5&include=profile costs at most 6. The join adds 4 to 9 seconds on a fresh page (never more than 12; the lookups run in parallel and the call waits for the slowest) and nothing when the rows are already cached. The hydration block on the response lists the rows looked up, filled and served from cache, the credits held and kept, and the milliseconds the join added.
curl "https://www.socialcrawl.dev/v1/tiktok/search/users?query=cooking&include=profile" \
-H "x-api-key: YOUR_API_KEY"Follower counts next to search results
search and search/top rows carry the creator's follower count on post.ext.author_followers; the hashtag feed does not. Every row carries two join keys: post.author.username and post.ext.author_id, the creator's numeric user id. Either one resolves to the current follower count on author.followers through GET /v1/tiktok/profile: pass the username as handle or the id as user_id. The id survives username changes; the handle is the one that works on audience-gated accounts, so retry a 404 id lookup as a handle lookup before treating the creator as gone.
For a crawl, deduplicate creators across the whole run first. The same creators recur across keywords, and you pay once per creator rather than once per row. POST /v1/prism/profiles resolves up to 50 usernames in one call at 1 credit per resolved profile, refunds rows that fail, and never serves from cache. Match rows to your input by each row's index.
curl "https://www.socialcrawl.dev/v1/tiktok/profile?user_id=7527113447973487646" \
-H "x-api-key: YOUR_API_KEY"Trending and sounds
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/tiktok/trending | 5 | Popular videos now; feed=local keeps most in one country | region, feed |
GET /v1/tiktok/song | 1 | A sound's title, artist, duration, cover, and how many videos use it | clipId |
GET /v1/tiktok/song/videos | 1 | The videos made with that sound, paged | clipId, cursor |
trending has no keyword and no cursor, so there is nothing to page. The two sound endpoints together are how you tell a rising sound from a saturated one before you brief a creator on it.
Which country the trending videos come from
trending has two feeds, and region does something different on each.
- Default (
feed=global). TikTok's web trending feed, fetched through a connection inregion.regionsets where the request comes from, not where the videos come from, and for most countries the feed is one worldwide pool. On 13/09/2026 over 12 calls in six countries, 22% of its videos were registered in the requested country: US 65%, BR 43%, GB 9%, FR 8%, JP 6%, DE 0%. About 13 videos, 3 to 12 seconds. - Local (
feed=local). The For You feed TikTok's app serves to a phone set toregion. Most, not all, of its videos are from that country: 57% across 10 pages measured the same day (DE 52%, US 66%, GB 41%, JP 81%), and 51% were captioned in the country's main language. A median of 24 videos (8 to 27), 12 to 35 seconds. When no video comes back the call returns503and refunds the credits, which happened on 1 of 25 test calls, so retry. A local call never falls back to the global feed.
Both cost 5 credits and return each video once. Every row carries post.ext.region, the two-letter country the video is registered to, so when you need only one country, filter on it. Local rows also carry post.ext.content_language, TikTok's own caption-language tag, which is null when TikTok could not tell.
curl "https://www.socialcrawl.dev/v1/tiktok/trending?region=DE&feed=local" \
-H "x-api-key: YOUR_API_KEY"curl "https://www.socialcrawl.dev/v1/tiktok/trending?region=US" \
-H "x-api-key: YOUR_API_KEY"
curl "https://www.socialcrawl.dev/v1/tiktok/song/videos?clipId=7016187547651328773" \
-H "x-api-key: YOUR_API_KEY"TikTok's own trend boards
TikTok publishes ranked trend boards for advertisers, now part of TikTok One. Two of them are live, and you need nothing but a market and a time window to read them.
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/tiktok/hashtags/popular | 2 per hashtag, minimum 6 | TikTok's trending hashtags for a market: the overall board and 15 industry boards | countryCode (27 markets), period, industry |
GET /v1/tiktok/videos/popular | 25 per board plus 1 per video | TikTok's Top Videos board, ranked by views, engagement or 6-second views | countryCode (US, JP, VN, TH, ID), period, orderBy, limit |
- Hashtags. TikTok shows three hashtags per board to a reader who is not logged in, so one board is three rows and costs 6 credits. Pass
industry=allto read the overall board and all 15 industry boards in one call: 44 to 46 unique hashtags for Germany on 13/09/2026, for about 90 credits, in 3 to 4 seconds. Each row carries its rank, the window's post and view counts, a daily popularity curve and the top creators underpost.ext.trend. A hashtag that ranks on more than one board comes back once and is billed once;post.ext.trend.boardslists every board it ranks on with its rank there (in Germany #marvelrivals was third overall and first in Games).periodis 7, 30 or 90 days. - Videos. TikTok publishes this board for five markets only. A request for any other market, Germany included, returns
400at no charge and points you to the hashtag board.
These values from the old boards are gone and return 400 at no charge: a 120-day window, newOnBoard, a second page, and the business-services, financial-services and life-services industries. The popular songs board was removed by TikTok and the creator board shows "Coming soon", so GET /v1/tiktok/songs/popular and GET /v1/tiktok/creators/popular return 503 and never bill.
curl "https://www.socialcrawl.dev/v1/tiktok/hashtags/popular?countryCode=DE&period=7&industry=all" \
-H "x-api-key: YOUR_API_KEY"Ad Library
| Endpoint | Credits | What it returns | Key parameters |
|---|---|---|---|
GET /v1/tiktok/adlibrary/search | 5 to 17 | Ads matching a keyword or an advertiser; include=ad adds the brand, landing page and advertiser link per ad | query or advertiser_name, include, limit (1-12) |
GET /v1/tiktok/adlibrary/ad | 5 | One ad in full: creative, landing page, and brand | ad_id or url |
Same advanced tier as the Facebook, Google, and LinkedIn ad libraries.
Brand and landing page next to ad results
The library's search row names the advertiser and carries the creative, the impression estimate, the spend band and the dates, and nothing about where the ad sends people: post.ext.ad.brand_name, post.ext.ad.landing_page, post.ext.ad.profile_web_link and post.author.avatar_url are null on a plain call. Add include=ad and every ad is filled from the per-ad record in the same call: the registered brand name, the landing page, the advertiser's TikTok profile link and avatar, and the objectives, countries and source where the library has them. 5 credits for the page plus 1 per ad filled from a fresh lookup, so 17 at most for the 12-ad page; ads already in cache are free, and ads that could not be filled are refunded and named in _warnings. limit=N (1 to 12) caps the ads and the extra credits together. The join adds 5 to 20 seconds on a fresh page (never more than 20), and the search itself is often the slow part of the call. Like, comment and share counts are not published by the library on either endpoint; for the performance side of TikTok advertising use ads/top, which reads the Creative Center board.
curl "https://www.socialcrawl.dev/v1/tiktok/adlibrary/search?advertiser_name=Gymshark&include=ad" \
-H "x-api-key: YOUR_API_KEY"All endpoints
36 endpoints available.
Platform notes
Follower counts are exact where TikTok exposes them. author.followers is the unrounded integer when the source publishes one. When only the rounded public figure exists, the response sets author.ext.followers_approximate to true.
Repeat profile lookups replay a cached snapshot. A second lookup of the same account within 15 minutes is served from cache at no charge. Send Cache-Control: no-cache when you need a guaranteed-fresh read.
Age and gender do not exist on this platform. user/audience returns geography only, because TikTok publishes no public demographic breakdown for a creator's followers.
region routes a request; it does not filter results. It is accepted on profile/videos, post, search, search/top, and search/hashtag, where it sends the request through that country, which changes what TikTok is willing to serve (a video blocked in one country loads from another) but not which country the results come from. trending requires it: on the default feed it is the same routing, and with feed=local it picks the country the For You feed is built for (see Which country the trending videos come from). To keep only one country's trending videos, filter the rows on post.ext.region.
Identifiers are not interchangeable. profile, user/followers, and profile/full take handle or user_id; post, post/comments, post/transcript, and video/screen-text take the video url; the sound endpoints take clipId.
trim=true costs the same. Most list endpoints accept it and return a lighter payload at the same price.
TikTok Shop moved. Product details, reviews, shop search, and creator showcases now live under /v1/tiktokshop/.... The old /v1/tiktok/shop and /v1/tiktok/user/showcase paths still resolve for backward compatibility.
Next steps
Pagination
Cursor and page walks, and what has_more actually means.
Credits
What each tier costs and when a call is refunded.
API reference
Every parameter and response field, endpoint by endpoint.
The other short-form surface most crawls pair with TikTok.
TikTok Shop
Products, reviews, and creator showcases on their own namespace.
