TikTok API — Getting Started — FetchLayer Docs

Getting Started

Read public TikTok accounts, videos, hashtags, search results and comments as structured JSON. No TikTok account and no TikTok developer application — just your FetchLayer key.

1. Get your API key

Create an account and generate a FetchLayer API key from your dashboard. The same key works on every platform.

Create free account →

2. Read an account

Pass a handle with or without the @, or a profile URL. The counts come back exact rather than rounded.

bash
curl -X POST https://api.fetchlayer.dev/tiktok/user-profile \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"username":"@nasa"}'

Exact counts, not rounded ones

TikTok publishes both the rounded figure shown on the page and the precise one. This returns the precise pair, so a follower count comes back as 1719888 rather than 1.7M — which is the difference between being able to track week-to-week movement and not.

3. List what they posted

The account's profile comes back with the videos, so listing an account's posts does not also need a profile call. Sort by latest, popular or oldest.

bash
curl -X POST https://api.fetchlayer.dev/tiktok/user-videos \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"username":"@nasa","limit":70,"pages":2,"sort":"popular"}'

Page with the cursor, not the offset

When more results exist, hasMore is true and nextCursor carries an opaque value. Pass it back as cursor to continue. pages collects several pages inside one request instead, up to 20 — each page fetched is one credit, and the response reports how many as pagesFetched. An account's videos come at most 35 to a page, so the call above asks for two. A cursor only continues the route and account it came from; anything else is a 400.

A 503 on this route comes only after repeated attempts, so wait before trying again rather than retrying immediately. A private account returns no videos, with the note “This account is private, so its posts are not public.”

4. Search, or follow a hashtag

Search returns TikTok's own ranking for the term. A hashtag listing also returns the tag itself, with its total view count across TikTok — useful for sizing a trend before deciding how much of it to read.

bash
curl -X POST https://api.fetchlayer.dev/tiktok/search-videos \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"query":"space launch","limit":24,"pages":2}'

curl -X POST https://api.fetchlayer.dev/tiktok/hashtag-videos \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"hashtag":"nasa","limit":30}'

Search takes no sort and no date filter

TikTok offers a public caller neither, so neither is accepted. Passing one is a 400 naming the field rather than a parameter that is quietly ignored — being answered with the wrong data and no indication why is worse than being told the request is wrong.

5. Read the comments

By video id or URL. Each comment carries its text, timestamp, like and reply counts, whether it is pinned, and whether the video's author wrote or liked it.

bash
curl -X POST https://api.fetchlayer.dev/tiktok/comments \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"video":"https://www.tiktok.com/@nasa/video/7686148096895569165","limit":100,"pages":2}'

replies is not the whole thread

replies holds only the replies TikTok shows alongside a comment — usually none or a handful. replyCount reports how many exist in total, and a response where the two differ says so in notes. Separately, totalCount is the figure TikTok publishes for the video, and it can exceed what paging can reach.

6. Video files, images and covers

TikTok's asset URLs are signed and expire within hours, so every asset carries a downloadUrl pointing at GET /tiktok/media, which delivers it on demand with Range support. A video's downloadUrl gives the MP4 without the TikTok watermark; an image's gives the image.

bash
curl -L "https://api.fetchlayer.dev/tiktok/media?video=7686148096895569165" \
  -H "Authorization: Bearer ss-your-key" \
  --output 7686148096895569165.mp4

Downloads, and what they cost

The file comes back as an MP4 with Range support, so players can seek and an interrupted download can resume. When the creator has turned off downloads, the video's downloadUrl is null and /media answers 403, “The creator has turned off downloads for this video.” A photo post or a video that is not public is a 404, and an expired image is a 404, “that media is no longer available on TikTok; read the record again for a fresh link”. Each request to /media costs 1 credit, and each range request counts as one.

Resolving a pasted link

When a user hands you a TikTok URL and you do not know what it points at, /resolve-url tells you — video, account, hashtag, sound or search — plus the id, handle, tag or query it encodes. It is free.

bash
curl -X POST https://api.fetchlayer.dev/tiktok/resolve-url \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.tiktok.com/@nasa/video/7686148096895569165"}'

A short share link (vm.tiktok.com, vt.tiktok.com, tiktok.com/t/…) does not say what it points to, so it comes back as unknown with a note saying to open it and pass the full URL. The routes that take a video reject a short link for the same reason.

Errors and retries

A 400 names the field to fix; unrecognised fields are rejected rather than ignored. A 404 means there is no such public account, video or hashtag. A 503 comes only after repeated attempts, so retrying immediately is unlikely to help — wait first. A 5xx carries a generic message, over the REST API and MCP alike — "The request could not be completed. Retry shortly.", or "Something went wrong on our side. Retry shortly." for internal_error — except the 503 for your own timeoutMs, which names it. Branch on code and retryable, not on the text. Read notes on successful responses too: a private account, comments turned off, and a list that ran out early all announce themselves there rather than as an error.

Error responses

Every error, on every route, is JSON in one shape: error (what went wrong, in words), status, code, retryable and requestId, which also comes as the X-Request-Id header. Branch on code and retryable rather than on the text; on a 5xx the text is generic. Quote requestId when reporting a problem.

json
{
  "error": "Missing required parameter: url",
  "status": 400,
  "missing": ["url"],
  "code": "invalid_input",
  "retryable": false,
  "requestId": "922874e3-c0d0-40d8-8866-d198af132420"
}
codeStatusRetryableMeaning
invalid_input400noThe request needs fixing; error names the field. A missing required parameter also lists it in missing.
unauthorized401noThe API key is missing or not valid.
insufficient_credits402noNothing left to spend: no credits, a used-up free trial, or a monthly allowance and credit balance both spent. error says which. Waiting does not help; topping up does.
forbidden403noThis key or account is not allowed this request.
not_found404noWhat the request names does not exist or is not public.
expired410noWhat the request refers to is no longer available; ask for it afresh.
quota_exceeded429noA request-rate limit was reached; error says which. Slow down before sending more.
cancelled499noThe caller closed or cancelled the request before it finished. The work was stopped and nothing was charged.
time_limit503yesYour timeoutMs ran out before there was anything to return; ask again, or allow more time.
temporarily_unavailable503yesNot available right now; ask again shortly.
posts_unavailable403noTikTok user videos: TikTok is not showing this account’s posts publicly right now. Asking again at once returns the same answer; nothing is charged.
store_unreachable503yesStore routes (Shopify, WooCommerce): the store itself can’t be reached right now — it may be offline. Ask again later. Where a store route doesn’t say so yet, an offline store is temporarily_unavailable.
upstream_error502yesThe source answered in a way that could not be used; ask again later.
internal_error500yesSomething failed on our side; ask again later, and quote requestId if it persists.
tool_error—noMCP only: a tool call failed in a way no other code describes; error says what.

More codes may be added; treat one you do not recognise by its retryable flag. Over MCP, a tool error carries the same fields in structuredContent, and as a JSON text block beside the message.

Next steps

Product overviewTikTok API: what it returns, pricing and use cases