Try it
Run a live TikTok profile request
No signup and no key. The box sends a real profile request and prints exactly what the endpoint returns.
TikTok is in prelaunch, so the box answers the standard unavailable message rather than a profile. That is the correct behaviour for an endpoint that is not served yet, not a fault in the box.
Endpoints & Pricing
Five TikTok Endpoints, One API Key
Profile and video are the flat basic rate. Hashtag, search and music are list calls, and a list call is priced by the depth you ask for.
Profile
GET /api/tiktok/profileby handle or URL- Bio, region, avatar, stats, privacy settings
verified,private_account,is_organization- Optional posts, followers, following and liked groups
Video record
GET /api/tiktok/videoby id or URL- The true author, not a re-post handle
- Caption, music, hashtags, mentions, labels, stats
- Optional
commentsandrelatedgroups;include_mediais priced separately and is not a group
Hashtag
limit=30 (Scale)GET /api/tiktok/hashtagby tag or id- Accepts
football,#footballor a tag URL - Up to 180 items in one synchronous call
- Priced by depth — see the ladder below
Search
limit=30 (Scale)GET /api/tiktok/searchby keyword- The same
items[]envelope as every other list - Up to 180 items in one synchronous call
- Priced by depth — see the ladder below
Music
limit=30 (Scale)GET /api/tiktok/musicby id or sound URL- Every item that used the sound, in one list
- Up to 180 items in one synchronous call
- Priced by depth — see the ladder below
The depth price, in full — $3.00 is the limit=30 price, not a flat rate
An enriched call is any call carrying an include_* group, plus every hashtag, search and music call. It is charged as a fixed part plus a per-item part, and the limit you pass is what moves it. At Default the charge is $3.00/1K × (0.7 + 0.01 × limit).
Read that both ways. A limit=180 call bills $7.50 per 1,000 calls — two and a half times the advertised $3.00. Per item it goes the other way: $0.100 falls to $0.042 per 1,000 items, and to $0.029 on Scale. Both statements are true and the second is the one we win on, so the first is printed first. Two groups are billed to less depth than you ask for: include_following is billed to a depth of 50 and include_related to the default depth of 30, so at limit=180 they bill $3.60 and $3.00 rather than $7.50. You are never billed for depth a list cannot return. Every other group follows the ladder above. Growth and Scale scale the whole ladder by the same 10% and 30%. Deeper than 180 items is the async form, which holds no client socket and is not bound by the synchronous cap.
include_media is a surcharge, charged on top of the call. Media is $10.00 per 1,000 on Default, $9.00 on Growth and $7.00 on Scale, so a basic video call with media is $0.50 + $10.00 = $10.50 per 1,000 at Default — not $10.00. It is opt-in and never free-eligible, because it is the one part of this API whose cost is bytes rather than requests. When a hosted copy exists the media block carries a link that needs no headers plus an expires_at; when one cannot be produced the block says unavailable.
Prelaunch rate card. The rates above are target rates, unverified until launch, and are what you will pay when the endpoints ship — they are not served yet. Growth and Scale are earned by a single deposit of $100 and $500; both are one-time, and a tier once earned never downgrades. A list that genuinely comes back empty, or a profile or video that does not exist, is an answer and is charged; a failure on our side is refunded.
One call, one charge, whole list. An enriched call returns a list up to the 180-item synchronous cap for a single charge, where per-row vendors bill every item in it. Growth and Scale are earned by a single $100 and $500 deposit; both are one-time and never downgrade. A list that genuinely comes back empty, or a profile or video that does not exist, is an answer and is charged; a failure on our side is refunded.
Quick Start
TikTok Data in One Request
One header, one GET. Every list — a profile’s posts, a hashtag, a keyword search, a song — comes back in the same envelope, so your parser is written once.
# A basic profile — one record, the flat basic rate, no depth to set curl "https://apiserpent.com/api/tiktok/profile?username=zachking&country=us" \ -H "X-API-Key: YOUR_API_KEY" # A video with its true author, music, hashtags and stats curl "https://apiserpent.com/api/tiktok/video?url=https://www.tiktok.com/@zachking/video/7231" \ -H "X-API-Key: YOUR_API_KEY" # A hashtag list. limit sets BOTH the depth and the price — 180 is the sync cap curl "https://apiserpent.com/api/tiktok/hashtag?tag=cottagecore&limit=180&country=us" \ -H "X-API-Key: YOUR_API_KEY"
import os, requests h = {"X-API-Key": os.environ["SERPENT_API_KEY"]} p = {"tag": "cottagecore", "limit": 180, "country": "us"} data = requests.get("https://apiserpent.com/api/tiktok/hashtag", params=p, headers=h, timeout=120).json() # Check the envelope BEFORE the items. total_count is None when TikTok # never stated one — it is not the same fact as result_count. print(data["result_count"], data["total_count"], data["complete"]) for it in data["items"]: print(it["video_id"], it["author"]["username"], it["stats"]["play_count"])
const qs = new URLSearchParams({ username: "zachking", country: "us" }); const res = await fetch(`https://apiserpent.com/api/tiktok/profile?${qs}`, { headers: { "X-API-Key": process.env.SERPENT_API_KEY } }); const profile = await res.json(); // A basic profile is a single record spread at the top level — no include_* set, // so this is the flat basic rate and there is no depth to pay for. console.log(profile.username, profile.verified, profile.stats.follower_count);
{
"success": true,
"items": [
{
"video_id": "7301234567890123456",
"video_url": "https://www.tiktok.com/@example/video/7301234567890123456",
"description": "three weeks of sourdough in 40 seconds #cottagecore",
"created_at": "2026-08-19T09:14:02.000Z",
"author": {
"username": "example",
"nickname": "Example Creator",
"verified": false
},
"music": {
"music_id": "6987654321098765432",
"title": "original sound",
"original": true
},
"stats": {
"play_count": 418200,
"digg_count": 61400,
"comment_count": 812,
"share_count": 2140
},
"hashtags": [ { "name": "cottagecore" } ],
"mentions": []
}
],
"result_count": 180,
"total_count": null,
"has_more": true,
"next_cursor": "180",
"truncated": false,
"restricted": false,
"complete": false,
"meta": {
"elapsed": "41830ms",
"timestamp": "2026-09-08T11:04:31.522Z"
}
}
Read the envelope before the items. total_count is null here because TikTok did not state a total for this tag — it is never back-filled from result_count, because “180 arrived” and “there are 180” are different facts. truncated (TikTok cut the list short), restricted (the list is not public) and complete (we reached the natural end) are three separate flags for three separate situations, and a collection that treats them as one will quietly record a private account as an empty one.
Data Fields
Every Field a TikTok Call Returns
Named JSON fields, not HTML you have to select against. Every key is always present, so an absent value arrives as null and never as a missing key.
Profile record
- username, user_id, sec_uid
- nickname, biography, bio_link
- profile_url, avatar.thumb/medium/large
- verified, private_account, is_organization
- commerce_user, tt_seller, is_ad_virtual
- region, language, created_at
- stats (followers, likes, videos)
- settings.comment/duet/stitch/download
- settings.liked_videos_public
- tabs.music, tabs.question, tabs.playlist
Video record
- video_id, video_url, description
- created_at, created_region, language
- author (the true author, not a re-post)
- music.music_id, title, author_name
- music.original, music.duration
- stats.play_count, digg_count
- stats.comment_count, share_count
- hashtags[], mentions[], labels[]
- image_post, is_ad
- duet_enabled, stitch_enabled, share_enabled
- media.play_url, media.download_url (every video call)
- media.video_link_headers, media.link_expires_at
- media.hosted (only with include_media)
The list envelope
- items[]
- result_count
- total_count (null when unstated)
- has_more
- next_cursor
- truncated
- restricted
- complete
- partialResults.requested
- partialResults.returned
- partialResults.reason, .note
Request parameters
- username — profile
- video_id or url — video
- tag, q, music_id — the list calls
- limit (1–180 sync; sets the price)
- country (two-letter code)
- include_posts, include_followers
- include_following, include_liked
- include_comments, include_related
- include_media (surcharge, never free)
The playback links ship on every video call — and one field has to travel with them. media.play_url and media.download_url come back on every /video call at no extra charge, with no flag to set. They are time-limited, and a request for one has to carry the exact header set returned beside it in media.video_link_headers — send it as returned, unchanged. Without it the request does not get the file. media.link_expires_at is that link’s own expiry, so read it off the response rather than assuming a window. When we cannot produce a link that will actually serve, all four come back null instead of a link that would fail on you later. media.hosted is the separate, paid part: our own copy of the file, on a link that needs no headers, and the only piece include_media adds.
Why TikTok data
What is TikTok data, and why teams use it
It is the record behind a profile, video, hashtag or song: author, bio, stats, caption, music and the list itself. Four things decide whether it is usable.
The author has to be the real one
A TikTok video can surface under a handle that did not make it. author on a video record is the creator of that video, carrying username, user_id, sec_uid, nickname and verified.
If you are building a creator database, an attribution report or an outreach list, this is the field the whole dataset rests on. Getting it wrong does not throw an error — it quietly credits the wrong person for a year.
A list has to say what kind of list it is
An empty list is three different situations. TikTok cut it short (truncated). It is not public (restricted). Or you reached the end and there is genuinely nothing more (complete).
All three are on the envelope, and total_count stays null when TikTok never stated a total rather than being back-filled from what arrived. A collector that cannot tell those apart records a private account as an empty one.
Depth is a price, and it should be visible
One enriched call returns a whole list — up to 180 items — for one charge, where per-row vendors bill every item. That is the structural saving, and it is also why the depth cannot be free.
So the formula is printed on this page rather than discovered on an invoice: $3.00/1K × (0.7 + 0.01 × limit). limit=180 bills $7.50, which is 2.5× the headline — and $0.042 per 1,000 items, which is the number that matters. Two groups are billed to less depth than you ask for: include_following is billed to a depth of 50 and include_related to the default depth of 30, so at limit=180 they bill $3.60 and $3.00. You are never billed for depth a list cannot return.
Media costs bytes, so it is priced apart
include_media is the one option here whose cost is the file rather than the request, and it is billed as a surcharge on top of the call rather than folded into a band that hides it. The playback links are not what it buys — those come back on every video call at no extra charge.
It is opt-in and never free-eligible. When a hosted copy exists, the media block carries a link that needs no headers plus an expires_at; when one cannot be produced it says unavailable rather than returning a link that will fail later.
Price comparison
TikTok API Pricing, Side by Side
Every figure is the vendor’s own published rate. Units differ wildly here, so the table names each one and normalises to a comparable figure below.
| Provider | What one unit is | Cost per 1,000 — entry | Cost per 1,000 — lowest published | What the lowest rate requires | Free tier |
|---|---|---|---|---|---|
| Serpent API | Per call. A profile or a video flat; a list call returns up to 180 items for one charge. | $0.50 basic $3.00 list call at limit=30 |
$0.35 basic $2.10 list call at limit=30 |
A single $500 deposit, spendable as balance. No monthly plan and no daily bucket; the tier never expires or downgrades. | 10 free API calls |
| Apify — clockworks/tiktok-scraper | Per result (one post) | $3.70 | $0.50 | Diamond plan, contact sales. Bronze $19/month is $3.00; Silver $199 is $2.30; Gold $999 is $1.70. | $5 platform credit |
| Apify — xmolodtsov/tiktok-search-scraper | Per post | $0.30 | $0.15 | Diamond plan, contact sales. Gold and Business at $999/month are $0.20. | $5 platform credit |
| Apify — pro100chok/tiktok-profile-pro | Per profile | $4.00 | $3.00 | Gold or above; Business is $999 per month | $5 platform credit |
| Apify — get-leads/all-in-one-tiktok-scraper | Per profile | $2.40 | $1.50 | Gold or above; Business is $999 per month | $5 platform credit |
| ScrapingDog | Per request (5 credits) | $1.00 (LITE, $40/month) | $0.136 | $30,000 per month | 200 credits |
| Bright Data | Per record | $1.50 (pay as you go) | $1.30 | Scale plan, $499 per month | 5,000 records/month |
| EnsembleData | Per unit, drawn from a daily bucket that does not roll over | about $2.22 per 1,000 units (Wood, $100/month, 1,500 units/day) | about $0.93 per 1,000 units | Platinum, $1,400 per month — and only if you spend the full daily allowance every day. The bucket resets at 00:00 UTC. | 50 units/day |
Normalised per item, which is the only fair way to read the rows above. Rates checked against each vendor’s own live pricing page on 2026-09-08. An enriched list call here returns 30 items at the default depth and 180 at the cap, so per item it is $0.100 per 1,000 items at limit=30 and $0.042 at limit=180 on Default, or $0.029 at limit=180 on Scale. Against the cheapest Apify TikTok actor that is 3× below its $0.30 free-tier rate and 1.5× below its $0.15 enterprise floor at the default depth, widening to 3.6× at limit=180. Two rows genuinely beat us and they are in the table above. ScrapingDog’s floor of $0.136 per 1,000 requests is below our $0.35 basic call — it needs $30,000 a month. EnsembleData’s Platinum plan normalises to roughly $0.047 per 1,000 posts on Search Hashtag, where one unit returns 20 posts: about twice as cheap as our default-depth $0.100, and slightly dearer than our $0.042 at limit=180. It costs $1,400 a month and holds that rate only if you spend the full daily allowance every single day.
Use cases
What Teams Actually Pull TikTok Data For
Four jobs that account for most TikTok data traffic, and the endpoint, depth and parameters each one needs.
Creator research and outreach lists
A basic /api/tiktok/profile call is the flat rate and carries bio, bio_link, region, verified, private_account, commerce_user and the follower and like counts — enough to qualify a creator without paying for depth.
Add include_posts only for the handles that survive the first filter. That keeps the expensive band on the shortlist rather than on the whole scan.
Trend and hashtag tracking
Poll /api/tiktok/hashtag on a tag list and diff the item set day over day. One call returns the whole page for one charge, and has_more plus next_cursor tell you whether there was more behind it.
Set limit deliberately: it is the parameter that moves the invoice. A daily limit=30 sweep and a weekly limit=180 deep pull is usually cheaper than either alone.
Sound and campaign monitoring
/api/tiktok/music takes a music id or a sound URL and returns the items that used it, in the same envelope. That is how you measure whether a brand sound is actually being picked up rather than just posted.
Each item carries the true author, so a spike resolves to the creators who caused it instead of to whoever reposted it hardest.
Audit-safe bulk collection
truncated, restricted and complete, plus a partialResults block naming requested and returned, keep a bulk run honest: a short list is labelled short rather than filed as a finding.
That matters most when the output is a report someone else will act on. A silently truncated feed and a genuinely quiet week look identical in a chart, and only one of them is real.
# One line per tag: date, tag, items returned, and whether the list was complete. # limit=30 is the default depth and the cheapest per call — deepen only when it pays. while IFS= read -r tag; do curl -s -G "https://apiserpent.com/api/tiktok/hashtag" \ --data-urlencode "tag=$tag" -d "limit=30" -d "country=us" \ -H "X-API-Key: $SERPENT_API_KEY" \ | jq -r --arg t "$tag" \ '[$t, (.result_count|tostring), (.complete|tostring), (.restricted|tostring)] | @csv' \ | sed "s|^|$(date -u +%F),|" >> tiktok-tags.csv done < tags.txt
FAQ
TikTok API Questions
/api/tiktok/profile, /video, /hashtag, /search and /music are in prelaunch and are not served. Everything on this page — the field lists, the parameters, the list envelope and the rates — describes the contract they ship with. The rates are target rates and are unverified until launch. Join the list and we will mail you the day the endpoints start answering; there is no card and no commitment.
include_* groups; hashtag, search and music are list calls. Every list on every endpoint — a profile’s posts, a hashtag feed, a keyword search, the videos using one song — comes back in the same items[] envelope, so a parser written for one reads all of them.
include_* groups: $0.50 per 1,000 calls on Default, $0.45 on Growth, $0.35 on Scale. Enriched is any call with an include_* group, plus every hashtag, search and music call: $3.00 per 1,000 calls on Default, $2.70 on Growth and $2.10 on Scale at the default limit=30. Enriched is priced by depth, so those figures are the limit=30 price and not a flat rate — see the depth question below. Growth unlocks at a single $100 deposit and Scale at $500; both are one-time and neither ever downgrades.
$3.00/1K × (0.7 + 0.01 × limit) at Default. So limit=30, the default, is exactly the published $3.00; limit=100 is $5.10; and limit=180, the synchronous cap, is $7.50 — two and a half times the advertised rate. Per item it falls the other way: $0.100 per 1,000 items at limit=30, $0.051 at limit=100 and $0.042 at limit=180 ($0.029 on Scale). Two groups are billed to less depth than you ask for: include_following is billed to a depth of 50 and include_related to the default depth of 30, so at limit=180 they bill $3.60 and $3.00 rather than $7.50 — you are never billed for depth a list cannot return. Set limit deliberately. It is the one parameter on this API that changes your invoice.
include_media is $0.50 + $10.00 = $10.50 per 1,000 at Default — not $10.00. It is opt-in and never free-eligible, because it is the only part of this API whose cost is the bytes rather than the request. When a hosted copy exists the media block carries a link that needs no headers plus an expires_at; when one cannot be produced the block says unavailable.
items[] and result_count are what arrived. total_count is TikTok’s own stated total and is null when TikTok did not state one — it is never back-filled from result_count, because that would turn “we do not know” into a number. has_more and next_cursor are what TikTok said about the page after this one. truncated means TikTok cut the list short, restricted means the list is not public, and complete means we reached the natural end and there is genuinely nothing more. Those three are different facts and the envelope keeps them apart.
partialResults block with requested and returned, plus a reason only when it is short by more than 40%, and a note. A list that genuinely comes back empty, or a profile or video that does not exist, is an answer and is charged; a failure on our side is refunded. Nothing is silently truncated to make a response look complete.
limit=180, against that $0.15 enterprise floor. And there is no monthly plan and no daily bucket here at all.
Join the TikTok API list
Start using the TikTok API
The endpoints are not served yet. Join and we’ll mail you the day they start answering. No card, no commitment, and the 10 free API calls are waiting on every other endpoint in the meantime.
10 free API calls, shared across every free-eligible endpoint. No card, no subscription, no monthly plan and no daily bucket. TikTok profile and video data from $0.35/1K calls.
Get your free API key

