TikTok API
The TrueScrape TikTok API exposes 28 public endpoints covering One TikTok ad, Search the TikTok Ads Library and Collection videos. Every call is a GET against public, logged-out pages and returns the same unified schema as every other platform here. Calls cost 1 credit each. A cache hit is free, and a failed or empty response is never charged.
Endpoints
| Endpoint | Returns | Credits | Cacheable | Batchable |
|---|---|---|---|---|
| /v1/tiktok/ad-library/ad | One TikTok ad | 1 | yes | yes |
| /v1/tiktok/ad-library/search | Search the TikTok Ads Library | 1 | yes | no |
| /v1/tiktok/collection/videos | Collection videos | 1 | yes | yes |
| /v1/tiktok/comment-replies | Replies to a comment | 1 | yes | yes |
| /v1/tiktok/comments | Comments on a video | 1 | yes | yes |
| /v1/tiktok/creators/popular | Popular creators | 1 | yes | yes |
| /v1/tiktok/followers | Accounts following a creator | 1 | yes | yes |
| /v1/tiktok/hashtag | Hashtag details | 1 | yes | yes |
| /v1/tiktok/hashtag-videos | Videos using a hashtag | 1 | yes | yes |
| /v1/tiktok/live | Live stream info | 1 | yes | yes |
| /v1/tiktok/playlist-videos | Videos in a playlist | 1 | yes | yes |
| /v1/tiktok/playlists | A creator's playlists | 1 | yes | yes |
| /v1/tiktok/product | Product details | 1 | yes | yes |
| /v1/tiktok/profile | Profile details | 1 | yes | yes |
| /v1/tiktok/profile/region | Creator region | 1 | yes | yes |
| /v1/tiktok/search/suggestions | Search suggestions | 1 | yes | yes |
| /v1/tiktok/shop/product/reviews | Product reviews | 1 | yes | yes |
| /v1/tiktok/shop/products | Seller's product catalogue | 1 | yes | yes |
| /v1/tiktok/shop/search | Search TikTok Shop | 1 | yes | yes |
| /v1/tiktok/song | Sound / song details | 1 | yes | yes |
| /v1/tiktok/song-videos | Videos using a sound | 1 | yes | yes |
| /v1/tiktok/songs/popular | Popular songs | 1 | yes | yes |
| /v1/tiktok/transcript | Video transcript | 1 | yes | yes |
| /v1/tiktok/trending | Trending feed | 1 | yes | yes |
| /v1/tiktok/user-videos | Videos posted by a creator | 1 | yes | yes |
| /v1/tiktok/user/showcase | Creator's product showcase | 1 | yes | yes |
| /v1/tiktok/video | Video or photo-post details | 1 | yes | yes |
| /v1/tiktok/videos/popular | Popular videos | 1 | yes | yes |
Reference
/v1/tiktok/collection/videos1 creditcacheablebatchableCollection videos
Videos in a public TikTok collection, a creator-curated playlist of other people's posts as well as their own. **Required:** Pass either url or collection_id.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | no | Collection URL, e.g. https://www.tiktok.com/@nasa/collection/Artemis-7123… |
| collection_id | string | no | Numeric collection id |
| count | number | no | Items per page (default 20, max 30) |
| cursor | string | no | pagination.cursor from a previous response |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns a list of Post in data.items.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | one of 47 strings | always | |
| id | string | always | |
| type | video | short | image | carousel | text | live | story | reel | audio | album | episode | unknown | always | What kind of post this is. `short` and `reel` are short-form video formats, `live` is a live stream or a recording of one, `carousel` holds several images or videos, `audio` is a music track, `album` is an album, `episode` is a podcast episode, and `unknown` covers anything else. |
| url | string | null | always | |
| title | string | null | always | |
| text | string | null | always | The post's text: a caption, a description or the message body. |
| authorId | string | null | always | |
| authorHandle | string | null | always | |
| authorName | string | null | always | |
| thumbnailUrl | string | null | always | |
| mediaUrls | string[] | always | |
| durationSeconds | number | null | always | |
| viewCount | number | null | always | Views. Null means the platform did not expose a count, which is not the same as zero. |
| likeCount | number | null | always | Likes, or the platform's nearest equivalent. Null means not exposed, not zero. |
| commentCount | number | null | always | Comments. Null means not exposed, not zero. |
| shareCount | number | null | always | Shares, reposts, or the platform's nearest equivalent. Null means not exposed, not zero. |
| hashtags | string[] | always | Hashtags or the platform's own topic tags, without the leading #. |
| mentions | string[] | always | Handles mentioned in the post text, without the leading @. |
| taggedUsers | string[] | may be absent | Usernames tagged in the media itself, as distinct from `mentions`, which come from the caption text. Absent when the platform does not report tags; an empty array means it reports none. |
| isSponsored | boolean | null | always | Whether the platform labels the post as an ad, promoted content or a paid partnership. |
| publishedAt | string | null | always | When the post was published. Usually an ISO 8601 timestamp; some platforms expose only a date or relative text such as "3 days ago". |
| fetchedAt | string | always | When this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch. |
curl "$API/v1/tiktok/collection/videos?cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/collection/videos?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/collection/videos",
params={
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/comment-replies1 creditcacheablebatchableReplies to a comment
The reply thread under one TikTok comment. parentId on each reply points back at the comment it answers, so nested threads reconstruct cleanly.
| Parameter | Type | Required | Description |
|---|---|---|---|
| comment_id | string | yes | id of the parent comment, from tiktok.comments |
| url | string | yes | The video the comment is on: URL, share link, or numeric id |
| count | number | no | Items per page (default 20, max 50) |
| cursor | string | no | pagination.cursor from a previous response |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns a list of Comment in data.items.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | one of 47 strings | always | |
| id | string | always | |
| postId | string | null | always | |
| parentId | string | null | always | Id of the comment this one replies to. Null on a top-level comment. |
| text | string | always | |
| authorId | string | null | always | |
| authorHandle | string | null | always | |
| authorName | string | null | always | |
| authorAvatarUrl | string | null | always | |
| likeCount | number | null | always | |
| replyCount | number | null | always | |
| isPinned | boolean | null | always | |
| isAuthorReply | boolean | null | always | Whether the comment was written by the author of the post. |
| publishedAt | string | null | always | When the comment was posted. Usually an ISO 8601 timestamp; some platforms expose only relative text such as "2 days ago". |
curl "$API/v1/tiktok/comment-replies?comment_id=%3Ccomment_id%3E&url=8XkPqR2nLvE&cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
comment_id: '<comment_id>',
url: '8XkPqR2nLvE',
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/comment-replies?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/comment-replies",
params={
"comment_id": "<comment_id>",
"url": "8XkPqR2nLvE",
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/creators/popular1 creditcacheablebatchablePopular creators
Creators trending in a market, from TikTok's own Creative Center leaderboard. Filter by country, follower band and category.
| Parameter | Type | Required | Description |
|---|---|---|---|
| region | string | no | Two-letter market code (US, GB, ID…). TikTok feeds and catalogues are per-market. |
| period | 7 | 30 | 120 | no | Trailing window in days: 7, 30 or 120defaults to 7 |
| follower_band | 1k-10k | 10k-100k | 100k-1m | 1m+ | no | Restrict to creators in this follower range |
| count | number | no | Items per page (default 20, max 50) |
| cursor | string | no | pagination.cursor from a previous response |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns a list of Creator in data.items.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | one of 47 strings | always | |
| id | string | always | The platform's own id for the account, such as a channel id or a numeric user id, always as a string. |
| handle | string | null | always | The account handle without the leading @. Null when the platform has no handles. |
| displayName | string | null | always | |
| bio | string | null | always | |
| avatarUrl | string | null | always | |
| bannerUrl | string | null | always | |
| url | string | null | always | |
| verified | boolean | null | always | Whether the platform shows a verification badge on the account. Null where the platform has no public badge or does not expose it. |
| followerCount | number | null | always | Followers, subscribers, or the platform's nearest equivalent. Null when the account hides the count or the platform does not expose it. |
| followingCount | number | null | always | |
| postCount | number | null | always | Posts, videos, tracks, repositories, or the platform's nearest equivalent that the account has published. |
| viewCount | number | null | always | Lifetime views across the account, where the platform exposes a total. |
| likeCount | number | null | may be absent | Total likes received across all of the creator's posts. Distinct from `viewCount`. Absent when the platform does not expose it. |
| isPrivate | boolean | null | always | |
| isBusiness | boolean | null | always | Whether the platform classifies the account as a business or organisation rather than a person or creator. Null where the platform draws no such line or does not expose it. On Facebook every Page counts, including Pages for public figures. |
| category | string | null | always | A label the platform attaches to the account, such as a business category, a genre or an account type. What it holds varies by platform. |
| location | string | null | always | Location as the platform shows it: a city, a country, a region code or an address, depending on the platform. |
| externalLinks | string[] | always | Links the account lists on its profile, such as a website or other social accounts. |
| createdAt | string | null | always | When the account was created. Usually an ISO 8601 timestamp; some platforms expose only the date as shown on the profile. |
| fetchedAt | string | always | When this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch. |
curl "$API/v1/tiktok/creators/popular?cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/creators/popular?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/creators/popular",
params={
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/followers1 creditcacheablebatchableAccounts following a creator
The public follower list for a TikTok account, newest first. TikTok caps how deep this list can be walked regardless of paging. Requires a configured TikTok signer.
| Parameter | Type | Required | Description |
|---|---|---|---|
| handle | string | yes | Handle (@nike), or a full tiktok.com profile URL |
| count | number | no | Items per page (default 30, max 50) |
| cursor | string | no | pagination.cursor from a previous response |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns a list of Creator in data.items.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | one of 47 strings | always | |
| id | string | always | The platform's own id for the account, such as a channel id or a numeric user id, always as a string. |
| handle | string | null | always | The account handle without the leading @. Null when the platform has no handles. |
| displayName | string | null | always | |
| bio | string | null | always | |
| avatarUrl | string | null | always | |
| bannerUrl | string | null | always | |
| url | string | null | always | |
| verified | boolean | null | always | Whether the platform shows a verification badge on the account. Null where the platform has no public badge or does not expose it. |
| followerCount | number | null | always | Followers, subscribers, or the platform's nearest equivalent. Null when the account hides the count or the platform does not expose it. |
| followingCount | number | null | always | |
| postCount | number | null | always | Posts, videos, tracks, repositories, or the platform's nearest equivalent that the account has published. |
| viewCount | number | null | always | Lifetime views across the account, where the platform exposes a total. |
| likeCount | number | null | may be absent | Total likes received across all of the creator's posts. Distinct from `viewCount`. Absent when the platform does not expose it. |
| isPrivate | boolean | null | always | |
| isBusiness | boolean | null | always | Whether the platform classifies the account as a business or organisation rather than a person or creator. Null where the platform draws no such line or does not expose it. On Facebook every Page counts, including Pages for public figures. |
| category | string | null | always | A label the platform attaches to the account, such as a business category, a genre or an account type. What it holds varies by platform. |
| location | string | null | always | Location as the platform shows it: a city, a country, a region code or an address, depending on the platform. |
| externalLinks | string[] | always | Links the account lists on its profile, such as a website or other social accounts. |
| createdAt | string | null | always | When the account was created. Usually an ISO 8601 timestamp; some platforms expose only the date as shown on the profile. |
| fetchedAt | string | always | When this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch. |
curl "$API/v1/tiktok/followers?handle=%40mkbhd&cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
handle: '@mkbhd',
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/followers?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/followers",
params={
"handle": "@mkbhd",
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/hashtag1 creditcacheablebatchableHashtag details
Stats for a TikTok hashtag: total videos and total views, plus its description and cover. Read straight off the tag page, so it needs no signer.
| Parameter | Type | Required | Description |
|---|---|---|---|
| hashtag | string | yes | Tag without the # (fyp), or a full /tag/ URL |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns an object with platform, id, name, description, url, coverUrl, videoCount, viewCount, isCommerce, fetchedAt.
Fields
| Field | Type | Present |
|---|---|---|
| platform | "tiktok" | always |
| id | string | always |
| name | string | always |
| description | string | null | always |
| url | string | always |
| coverUrl | string | null | always |
| videoCount | number | null | always |
| viewCount | number | null | always |
| isCommerce | boolean | null | always |
| fetchedAt | string | always |
curl "$API/v1/tiktok/hashtag?hashtag=%3Chashtag%3E&cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
hashtag: '<hashtag>',
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/hashtag?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/hashtag",
params={
"hashtag": "<hashtag>",
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/live1 creditcacheablebatchableLive stream info
Details of a creator's stream while they are live: room id, title, cover, current viewers and start time. A creator who is not live returns empty_result, a real answer that costs nothing, so this works as a cheap "are they live?" poll. Pass room_id (from a previous response's roomId) to look the room up directly on TikTok's own webcast surface. No third-party provider is involved. On the room_id path handle may come back empty if the webcast payload omits the room owner. **Required:** Pass either handle or room_id.
| Parameter | Type | Required | Description |
|---|---|---|---|
| handle | string | no | Handle (@nike), or a full tiktok.com profile URL |
| room_id | string | no | TikTok LIVE room id, e.g. from a previous tiktok.live response's roomId |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns an object with platform, roomId, handle, title, coverUrl, viewerCount, startedAt, url, creator, fetchedAt.
Fields
| Field | Type | Present |
|---|---|---|
| platform | "tiktok" | always |
| roomId | string | always |
| handle | string | always |
| title | string | null | always |
| coverUrl | string | null | always |
| viewerCount | number | null | always |
| startedAt | string | null | always |
| url | string | always |
| creator | Creator | always |
| fetchedAt | string | always |
curl "$API/v1/tiktok/live?cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/live?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/live",
params={
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/song1 creditcacheablebatchableSound / song details
Details for a TikTok sound: title, artist, album, cover, clip length, and how many videos use it.
| Parameter | Type | Required | Description |
|---|---|---|---|
| song | string | yes | Music id, or a full tiktok.com/music/… URL |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns an object with platform, id, title, authorName, album, url, coverUrl, playUrl, durationSeconds, isOriginalSound, videoCount, fetchedAt.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | "tiktok" | always | |
| id | string | always | |
| title | string | always | |
| authorName | string | null | always | |
| album | string | null | always | |
| url | string | always | |
| coverUrl | string | null | always | |
| playUrl | string | null | always | |
| durationSeconds | number | null | always | |
| isOriginalSound | boolean | null | always | True when the sound originated on TikTok rather than a licensed track. |
| videoCount | number | null | always | |
| fetchedAt | string | always |
curl "$API/v1/tiktok/song?song=%3Csong%3E&cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
song: '<song>',
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/song?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/song",
params={
"song": "<song>",
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/song-videos1 creditcacheablebatchableVideos using a sound
TikToks built on a given sound: the endpoint behind "who is using my track", with a cursor for the next page.
| Parameter | Type | Required | Description |
|---|---|---|---|
| song | string | yes | Music id, or a full tiktok.com/music/… URL |
| count | number | no | Items per page (default 30, max 30) |
| cursor | string | no | pagination.cursor from a previous response |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns a list of Post in data.items.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | one of 47 strings | always | |
| id | string | always | |
| type | video | short | image | carousel | text | live | story | reel | audio | album | episode | unknown | always | What kind of post this is. `short` and `reel` are short-form video formats, `live` is a live stream or a recording of one, `carousel` holds several images or videos, `audio` is a music track, `album` is an album, `episode` is a podcast episode, and `unknown` covers anything else. |
| url | string | null | always | |
| title | string | null | always | |
| text | string | null | always | The post's text: a caption, a description or the message body. |
| authorId | string | null | always | |
| authorHandle | string | null | always | |
| authorName | string | null | always | |
| thumbnailUrl | string | null | always | |
| mediaUrls | string[] | always | |
| durationSeconds | number | null | always | |
| viewCount | number | null | always | Views. Null means the platform did not expose a count, which is not the same as zero. |
| likeCount | number | null | always | Likes, or the platform's nearest equivalent. Null means not exposed, not zero. |
| commentCount | number | null | always | Comments. Null means not exposed, not zero. |
| shareCount | number | null | always | Shares, reposts, or the platform's nearest equivalent. Null means not exposed, not zero. |
| hashtags | string[] | always | Hashtags or the platform's own topic tags, without the leading #. |
| mentions | string[] | always | Handles mentioned in the post text, without the leading @. |
| taggedUsers | string[] | may be absent | Usernames tagged in the media itself, as distinct from `mentions`, which come from the caption text. Absent when the platform does not report tags; an empty array means it reports none. |
| isSponsored | boolean | null | always | Whether the platform labels the post as an ad, promoted content or a paid partnership. |
| publishedAt | string | null | always | When the post was published. Usually an ISO 8601 timestamp; some platforms expose only a date or relative text such as "3 days ago". |
| fetchedAt | string | always | When this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch. |
curl "$API/v1/tiktok/song-videos?song=%3Csong%3E&cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
song: '<song>',
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/song-videos?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/song-videos",
params={
"song": "<song>",
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/songs/popular1 creditcacheablebatchablePopular songs
Sounds trending in a market, the endpoint behind music A&R and sync-licensing research. From TikTok's Creative Center.
| Parameter | Type | Required | Description |
|---|---|---|---|
| region | string | no | Two-letter market code (US, GB, ID…). TikTok feeds and catalogues are per-market. |
| period | 7 | 30 | 120 | no | Trailing window in days: 7, 30 or 120defaults to 7 |
| count | number | no | Items per page (default 20, max 50) |
| cursor | string | no | pagination.cursor from a previous response |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns a list of object in data.items.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | "tiktok" | always | |
| id | string | always | |
| title | string | always | |
| authorName | string | null | always | |
| album | string | null | always | |
| url | string | always | |
| coverUrl | string | null | always | |
| playUrl | string | null | always | |
| durationSeconds | number | null | always | |
| isOriginalSound | boolean | null | always | True when the sound originated on TikTok rather than a licensed track. |
| videoCount | number | null | always | |
| fetchedAt | string | always |
curl "$API/v1/tiktok/songs/popular?cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/songs/popular?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/songs/popular",
params={
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/transcript1 creditcacheablebatchableVideo transcript
The spoken transcript of a TikTok, as one text block plus timed cues. Returns the ORIGINAL spoken-language track by default rather than whichever track TikTok lists first, so the text reflects what was actually said. Pass language for a specific track. A miss is an error listing what is available, never a silent substitution into another language.
Or see what it returns without a key, in the free TikTok transcript.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | yes | Video URL (preferred), share link, or numeric video id |
| language | string | no | BCP-47 or ISO-639 code, e.g. "en", "es", "pt-BR". Omit for the original spoken track. |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns one Transcript.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | one of 47 strings | always | |
| postId | string | always | |
| url | string | null | always | |
| language | string | null | always | The transcript's language as the platform labels it, usually a language code such as `en`. |
| isAutoGenerated | boolean | null | always | Whether the platform generated the captions automatically, by speech recognition or machine translation, rather than a person writing them. |
| text | string | always | The full transcript as a single block of text. |
| cues | object[] | always | Timed segments of the transcript. |
| cues[].start | number | always | Seconds from the start of the media. |
| cues[].end | number | null | always | Seconds from the start of the media. Null when the platform gives no end time. |
| cues[].text | string | always | |
| durationSeconds | number | null | always | |
| source | captions | asr | null | always | Where the transcript came from. `captions`: the platform's own caption track, whether a person wrote it or the platform generated it (see `isAutoGenerated`). `asr`: produced by speech recognition on the audio. Null when this cannot be told. |
| fetchedAt | string | always | When this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch. |
curl "$API/v1/tiktok/transcript?url=8XkPqR2nLvE&cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
url: '8XkPqR2nLvE',
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/transcript?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/transcript",
params={
"url": "8XkPqR2nLvE",
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/user/showcase1 creditcacheablebatchableCreator's product showcase
Products a creator has pinned to their profile showcase. This is what they are actually promoting, as opposed to what they sell. Requires a configured TikTok signer.
| Parameter | Type | Required | Description |
|---|---|---|---|
| handle | string | yes | Creator handle, e.g. @charlidamelio |
| count | number | no | Items per page (default 20, max 50) |
| cursor | string | no | pagination.cursor from a previous response |
| region | string | no | Two-letter market code (US, GB, ID…). TikTok feeds and catalogues are per-market. |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns a list of object in data.items.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | "tiktok" | always | |
| id | string | always | |
| title | string | always | |
| url | string | null | always | |
| description | string | null | always | |
| price | object | always | |
| price.amount | number | null | always | Numeric amount in major units (12.99, not 1299). Null if unparseable. |
| price.currency | string | null | always | ISO-4217 where TikTok gives one. Null means we will not guess. |
| price.formatted | string | null | always | Exactly what TikTok displayed, e.g. "$12.99". |
| originalPrice | object | always | |
| originalPrice.amount | number | null | always | Numeric amount in major units (12.99, not 1299). Null if unparseable. |
| originalPrice.currency | string | null | always | ISO-4217 where TikTok gives one. Null means we will not guess. |
| originalPrice.formatted | string | null | always | Exactly what TikTok displayed, e.g. "$12.99". |
| imageUrls | string[] | always | |
| rating | number | null | always | Star rating out of 5. Null on a product with no reviews yet. |
| reviewCount | number | null | always | |
| soldCount | number | null | always | |
| inStock | boolean | null | always | |
| sellerId | string | null | always | |
| sellerName | string | null | always | |
| categoryName | string | null | always | |
| fetchedAt | string | always |
curl "$API/v1/tiktok/user/showcase?handle=%40mkbhd&cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
handle: '@mkbhd',
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/user/showcase?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/user/showcase",
params={
"handle": "@mkbhd",
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
/v1/tiktok/videos/popular1 creditcacheablebatchablePopular videos
Videos trending in a market over a trailing window, from TikTok's Creative Center. Distinct from /v1/tiktok/trending, which is the personalised For You feed.
| Parameter | Type | Required | Description |
|---|---|---|---|
| region | string | no | Two-letter market code (US, GB, ID…). TikTok feeds and catalogues are per-market. |
| period | 7 | 30 | 120 | no | Trailing window in days: 7, 30 or 120defaults to 7 |
| count | number | no | Items per page (default 20, max 50) |
| cursor | string | no | pagination.cursor from a previous response |
Also accepts cache_max_age (a hit costs 0 credits) and include_raw (returns the untouched upstream payload under raw).
Returns a list of Post in data.items.
Fields
| Field | Type | Present | Description |
|---|---|---|---|
| platform | one of 47 strings | always | |
| id | string | always | |
| type | video | short | image | carousel | text | live | story | reel | audio | album | episode | unknown | always | What kind of post this is. `short` and `reel` are short-form video formats, `live` is a live stream or a recording of one, `carousel` holds several images or videos, `audio` is a music track, `album` is an album, `episode` is a podcast episode, and `unknown` covers anything else. |
| url | string | null | always | |
| title | string | null | always | |
| text | string | null | always | The post's text: a caption, a description or the message body. |
| authorId | string | null | always | |
| authorHandle | string | null | always | |
| authorName | string | null | always | |
| thumbnailUrl | string | null | always | |
| mediaUrls | string[] | always | |
| durationSeconds | number | null | always | |
| viewCount | number | null | always | Views. Null means the platform did not expose a count, which is not the same as zero. |
| likeCount | number | null | always | Likes, or the platform's nearest equivalent. Null means not exposed, not zero. |
| commentCount | number | null | always | Comments. Null means not exposed, not zero. |
| shareCount | number | null | always | Shares, reposts, or the platform's nearest equivalent. Null means not exposed, not zero. |
| hashtags | string[] | always | Hashtags or the platform's own topic tags, without the leading #. |
| mentions | string[] | always | Handles mentioned in the post text, without the leading @. |
| taggedUsers | string[] | may be absent | Usernames tagged in the media itself, as distinct from `mentions`, which come from the caption text. Absent when the platform does not report tags; an empty array means it reports none. |
| isSponsored | boolean | null | always | Whether the platform labels the post as an ad, promoted content or a paid partnership. |
| publishedAt | string | null | always | When the post was published. Usually an ISO 8601 timestamp; some platforms expose only a date or relative text such as "3 days ago". |
| fetchedAt | string | always | When this record was fetched from the platform, as an ISO 8601 timestamp. A cached response keeps the time of the original fetch. |
curl "$API/v1/tiktok/videos/popular?cache_max_age=7d" \
-H "x-api-key: $KEY"const query = new URLSearchParams({
cache_max_age: '7d',
});
const response = await fetch(`${API}/v1/tiktok/videos/popular?${query}`, {
headers: { 'x-api-key': KEY },
});
const body = await response.json();
if (!body.success) throw new Error(body.error.code);
// 1 credit, 0 on a cache hit
console.log(body.data, body.meta.creditsCharged);Python
import os, httpx
r = httpx.get(
f"{os.environ['API']}/v1/tiktok/videos/popular",
params={
"cache_max_age": "7d",
},
headers={"x-api-key": os.environ["KEY"]},
timeout=30,
)
body = r.json()
if not body["success"]:
raise RuntimeError(body["error"]["code"])
print(body["data"], body["meta"]["creditsCharged"])Response codes
| 200 | Success |
| 400 | invalid_request. Not charged |
| 401 | missing_api_key / invalid_api_key / revoked_api_key. Never a billing error |
| 402 | insufficient_credits. The key is valid, the balance is not |
| 429 | daily_cap_exceeded or rate_limited (hourly calls to servers you named outside our known list). Not charged |
| 501 | not_configured. This deployment is not set up to serve this endpoint. Not charged. |
| 502 | upstream_schema_drift. Not charged |
| 503 | upstream_unavailable. We could not get this from the platform. Not charged |
| 504 | upstream_timeout. Not charged |
Questions
- How much does the TikTok API cost?
- Calls cost 1 credit each. Every endpoint is cacheable, and a cache hit costs nothing. A failed request and an empty result are both free, on every endpoint.
- Does the TikTok API need a login or cookies?
- No. Every endpoint reads public, logged-out pages only — no account, no cookies, no session. An API key identifies your own TrueScrape account and nothing else.
- Can I fetch many TikTok targets in one request?
- Yes. 27 of 28 are batchable: one POST to /v1/jobs/batch takes many targets and returns a job id to poll.
- Can I watch TikTok endpoints for changes?
- Yes, 19 of 28. A subscription polls on your schedule and fires your webhook only when the content changed; the rest carry x-subscribable: false in the spec.