TikTok Video, Audio & Cover Downloader
Download TikTok videos, cover thumbnails, background audio, and slideshow images by post URL, with quality selection and a stable download link per file.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
postUrls | array | required | TikTok post URLs or IDs. Accepts /@user/video/ID, /@user/photo/ID, /video/ID, /photo/ID, embed URLs (/embed/v2/ID), vm.tiktok.com / vt.tiktok.com / /t/ short links, m.tiktok.com/v/ID.html, and bare numeric post IDs. Duplicates are processed once. |
assetTypes | array | optional | Assets to produce for each post. 'video' = MP4; 'audio' = TikTok sound; 'cover' = cover image; 'slideshow' = ordered photo frames; 'metadata' = post/author/stats/music row. |
videoVariant | string | optional | 'clean' = no-watermark MP4 (requires a same-page browser session and is always re-hosted). 'watermarked' = logo copy, usable as an expiring CDN URL. 'both' = one row per variant. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
postUrls | array | required | TikTok post URLs or IDs. Accepts /@user/video/ID, /@user/photo/ID, /video/ID, /photo/ID, embed URLs (/embed/v2/ID), vm.tiktok.com / vt.tiktok.com / /t/ short links, m.tiktok.com/v/ID.html, and bare numeric post IDs. Duplicates are processed once. |
assetTypes | array | optional | Assets to produce for each post. 'video' = MP4; 'audio' = TikTok sound; 'cover' = cover image; 'slideshow' = ordered photo frames; 'metadata' = post/author/stats/music row. |
videoVariant | string | optional | 'clean' = no-watermark MP4 (requires a same-page browser session and is always re-hosted). 'watermarked' = logo copy, usable as an expiring CDN URL. 'both' = one row per variant. |
quality | string | optional | Target clean-video quality. 'best' sorts by resolution first, then bitrate. Watermarked copies are single-resolution. |
downloadToKv | boolean | optional | Store watermarked video, audio, cover, and slideshow files in the run's Key-Value Store and return stable kvsUrl links. Clean video is always stored because its source URL is session-bound. Set false to return expiring CDN URLs only. |
maxFileSizeMb | integer | optional | Per-file size cap. Files above the cap produce a 'too_large' status row instead of a partial download. |
maxItems | integer | optional | Maximum number of unique posts to process. Inputs beyond the cap receive a 'limit_reached' status row. |
maxConcurrency | integer | optional | Number of posts processed in parallel (1-3). Embed requests are retried with backoff when TikTok rate-limits them. |
proxyConfiguration | object | optional | Optional. The actor uses a direct connection by default and does not require a proxy. Use this only if you want to route requests through Apify Proxy. |
Download the published input schema
View complete JSON
{
"description": "Download TikTok videos (clean or watermarked), audio, covers, slideshow frames, and post metadata. Clean MP4 files are downloaded inside a browser session and re-hosted in Apify Key-Value Store; watermarked/direct assets can be re-hosted or returned as expiring CDN URLs.",
"properties": {
"assetTypes": {
"default": [
"video"
],
"description": "Assets to produce for each post. 'video' = MP4; 'audio' = TikTok sound; 'cover' = cover image; 'slideshow' = ordered photo frames; 'metadata' = post/author/stats/music row.",
"editor": "select",
"items": {
"enum": [
"video",
"audio",
"cover",
"slideshow",
"metadata"
],
"enumTitles": [
"Video (MP4)",
"Audio",
"Cover image",
"Slideshow frames",
"Metadata"
],
"type": "string"
},
"prefill": [
"video"
],
"title": "Asset Types",
"type": "array"
},
"downloadToKv": {
"default": true,
"description": "Store watermarked video, audio, cover, and slideshow files in the run's Key-Value Store and return stable kvsUrl links. Clean video is always stored because its source URL is session-bound. Set false to return expiring CDN URLs only.",
"prefill": true,
"title": "Download Files to Apify Key-Value Store",
"type": "boolean"
},
"maxConcurrency": {
"default": 3,
"description": "Number of posts processed in parallel (1-3). Embed requests are retried with backoff when TikTok rate-limits them.",
"maximum": 3,
"minimum": 1,
"prefill": 1,
"title": "Max Concurrency",
"type": "integer"
},
"maxFileSizeMb": {
"default": 100,
"description": "Per-file size cap. Files above the cap produce a 'too_large' status row instead of a partial download.",
"maximum": 100,
"minimum": 1,
"prefill": 100,
"title": "Max File Size (MB)",
"type": "integer"
},
"maxItems": {
"default": 1000,
"description": "Maximum number of unique posts to process. Inputs beyond the cap receive a 'limit_reached' status row.",
"maximum": 1000,
"minimum": 1,
"prefill": 1,
"title": "Max Posts",
"type": "integer"
},
"postUrls": {
"description": "TikTok post URLs or IDs. Accepts /@user/video/ID, /@user/photo/ID, /video/ID, /photo/ID, embed URLs (/embed/v2/ID), vm.tiktok.com / vt.tiktok.com / /t/ short links, m.tiktok.com/v/ID.html, and bare numeric post IDs. Duplicates are processed once.",
"editor": "stringList",
"items": {
"type": "string"
},
"prefill": [
"https://www.tiktok.com/@nba/video/7693329153680035102"
],
"title": "Post URLs",
"type": "array"
},
"proxyConfiguration": {
"description": "Optional. The actor uses a direct connection by default and does not require a proxy. Use this only if you want to route requests through Apify Proxy.",
"editor": "proxy",
"title": "Proxy Configuration",
"type": "object"
},
"quality": {
"default": "best",
"description": "Target clean-video quality. 'best' sorts by resolution first, then bitrate. Watermarked copies are single-resolution.",
"editor": "select",
"enum": [
"best",
"1080p",
"720p",
"540p",
"360p",
"smallest"
],
"enumTitles": [
"Best (highest resolution)",
"1080p",
"720p",
"540p",
"360p",
"Smallest file"
],
"prefill": "540p",
"title": "Video Quality",
"type": "string"
},
"videoVariant": {
"default": "clean",
"description": "'clean' = no-watermark MP4 (requires a same-page browser session and is always re-hosted). 'watermarked' = logo copy, usable as an expiring CDN URL. 'both' = one row per variant.",
"editor": "select",
"enum": [
"clean",
"watermarked",
"both"
],
"enumTitles": [
"Clean (no watermark)",
"Watermarked",
"Both variants"
],
"prefill": "clean",
"title": "Video Variant",
"type": "string"
}
},
"required": [
"postUrls"
],
"schemaVersion": 1,
"title": "TikTok Downloader API Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
posts | string | optional | Dataset containing downloaded TikTok assets, post metadata, and status rows. |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "One dataset row per asset (video clean/watermarked, audio, cover, slideshow frame, metadata) plus explicit status rows for unavailable, private, region-blocked, age-gated, duplicate, over-limit, and too-large inputs. Each row carries post identity, variant, quality, MIME type, byte size, dimensions, duration, stable Key-Value Store link, original CDN URL, expiry, and status.",
"properties": {
"posts": {
"description": "Dataset containing downloaded TikTok assets, post metadata, and status rows.",
"template": "{{links.apiDefaultDatasetUrl}}/items",
"title": "Downloaded Posts and Assets",
"type": "string"
}
},
"title": "TikTok Video, Audio & Cover Downloader Output"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
Download the published dataset schema
View complete JSON
{
"actorSpecification": 1,
"fields": {
"assetType": {
"description": "video, audio, cover, slideshow, metadata, or post for post-level status rows",
"title": "Asset Type",
"type": "string"
},
"authorId": {
"description": "TikTok user ID (metadata rows)",
"title": "Author ID",
"type": "string"
},
"authorNickname": {
"description": "Display name (metadata rows)",
"title": "Author Nickname",
"type": "string"
},
"authorUsername": {
"description": "TikTok username (metadata rows)",
"title": "Author Username",
"type": "string"
},
"authorVerified": {
"description": "Whether the author is verified (metadata rows)",
"title": "Author Verified",
"type": "boolean"
},
"byteSize": {
"description": "File size in bytes",
"title": "Byte Size",
"type": "integer"
},
"caption": {
"description": "Post caption (metadata rows)",
"title": "Caption",
"type": "string"
},
"collectCount": {
"description": "Post collect count (metadata rows)",
"title": "Collect Count",
"type": "integer"
},
"commentCount": {
"description": "Post comment count (metadata rows)",
"title": "Comment Count",
"type": "integer"
},
"createTime": {
"description": "Unix timestamp of the post (metadata rows)",
"title": "Create Time",
"type": "integer"
},
"downloadedAt": {
"description": "ISO 8601 timestamp when the asset was stored or metadata was captured",
"title": "Downloaded At",
"type": "string"
},
"duration": {
"description": "Video or audio duration in seconds",
"title": "Duration (s)",
"type": "number"
},
"error": {
"description": "Human-readable explanation for non-ok status rows",
"title": "Error",
"type": "string"
},
"expiresAt": {
"description": "ISO 8601 expiry of the source CDN URL, when signed",
"title": "Expires At",
"type": "string"
},
"height": {
"description": "Media height in pixels",
"title": "Height",
"type": "integer"
},
"imageCount": {
"description": "Number of slideshow images (metadata rows)",
"title": "Image Count",
"type": "integer"
},
"imageHeight": {
"description": "Slideshow frame height",
"title": "Image Height",
"type": "integer"
},
"imageWidth": {
"description": "Slideshow frame width",
"title": "Image Width",
"type": "integer"
},
"kvsKey": {
"description": "Key in Apify Key-Value Store",
"title": "KVS Key",
"type": "string"
},
"kvsUrl": {
"description": "Stable public Apify Key-Value Store URL for the downloaded file",
"title": "KVS URL",
"type": "string"
},
"likeCount": {
"description": "Post like/digg count (metadata rows)",
"title": "Like Count",
"type": "integer"
},
"mimeType": {
"description": "MIME type of the stored file",
"title": "MIME Type",
"type": "string"
},
"musicAuthor": {
"description": "Sound author (metadata rows)",
"title": "Music Author",
"type": "string"
},
"musicDuration": {
"description": "Sound duration in seconds (metadata rows)",
"title": "Music Duration",
"type": "integer"
},
"musicId": {
"description": "TikTok sound ID (metadata/audio rows)",
"title": "Music ID",
"type": "string"
},
"musicTitle": {
"description": "Sound title (metadata/audio rows)",
"title": "Music Title",
"type": "string"
},
"ordinal": {
"description": "0-based slideshow frame index",
"title": "Ordinal",
"type": "integer"
},
"originalUrl": {
"description": "Source TikTok CDN URL (expires in about 48 hours)",
"title": "Original URL",
"type": "string"
},
"playCount": {
"description": "Post play count (metadata rows)",
"title": "Play Count",
"type": "integer"
},
"postId": {
"description": "Unique TikTok post ID",
"title": "Post ID",
"type": "string"
},
"postType": {
"description": "video or slideshow",
"title": "Post Type",
"type": "string"
},
"postUrl": {
"description": "Canonical URL to the TikTok post",
"title": "Post URL",
"type": "string"
},
"quality": {
"description": "Selected quality label (e.g. 1080p, 720p, 540p)",
"title": "Quality",
"type": "string"
},
"requiresSession": {
"description": "True when the source URL only works with the extracting browser session (clean video)",
"title": "Requires Session",
"type": "boolean"
},
"shareCount": {
"description": "Post share count (metadata rows)",
"title": "Share Count",
"type": "integer"
},
"status": {
"description": "ok, not_found, private, region_blocked, login_required, blocked, unsupported_url, short_link_dead, no_video, no_slideshow, no_audio, no_cover, no_assets, download_failed, too_large, duplicate, or limit_reached",
"title": "Status",
"type": "string"
},
"variant": {
"description": "Video variant: clean or watermarked",
"title": "Variant",
"type": "string"
},
"width": {
"description": "Media width in pixels",
"title": "Width",
"type": "integer"
}
},
"title": "TikTok Downloads",
"views": {
"issues": {
"display": {
"component": "table",
"properties": {
"assetType": {
"format": "text",
"label": "Asset"
},
"error": {
"format": "text",
"label": "Error"
},
"postId": {
"format": "text",
"label": "Post ID"
},
"postUrl": {
"format": "link",
"label": "Post URL"
},
"status": {
"format": "text",
"label": "Status"
},
"variant": {
"format": "text",
"label": "Variant"
}
}
},
"title": "Status Rows",
"transformation": {
"fields": [
"postId",
"postUrl",
"assetType",
"variant",
"status",
"error"
]
}
},
"overview": {
"display": {
"component": "table",
"properties": {
"assetType": {
"format": "text",
"label": "Asset"
},
"byteSize": {
"format": "number",
"label": "Bytes"
},
"downloadedAt": {
"format": "text",
"label": "Downloaded At"
},
"kvsUrl": {
"format": "link",
"label": "Download"
},
"mimeType": {
"format": "text",
"label": "MIME"
},
"originalUrl": {
"format": "link",
"label": "Source"
},
"postId": {
"format": "text",
"label": "Post ID"
},
"postUrl": {
"format": "link",
"label": "Post URL"
},
"quality": {
"format": "text",
"label": "Quality"
},
"status": {
"format": "text",
"label": "Status"
},
"variant": {
"format": "text",
"label": "Variant"
}
}
},
"title": "Downloaded Assets",
"transformation": {
"fields": [
"postId",
"postUrl",
"assetType",
"variant",
"quality",
"mimeType",
"byteSize",
"status",
"kvsUrl",
"originalUrl",
"downloadedAt"
]
}
}
}
}
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
TikTok Video, Audio & Cover Downloader
Download TikTok posts as clean no-watermark MP4, watermarked MP4, sound, cover images, slideshow frames, and post metadata. Each file can be re-hosted in Apify Key-Value Store with a stable public link, and every input gets a dataset row — including posts that are deleted, private, region-blocked, age-gated, duplicated, or too large to download.
No TikTok account, cookies, API key, or proxy setup is required.
What you get
- One row per asset: video variant, audio, cover, slideshow frame, or metadata
- Explicit status rows instead of silent skips
- Clean MP4 (no watermark) downloaded inside the page session and re-hosted to Apify storage
- Watermarked MP4, covers, audio, and slideshow images returned as stable KV links and/or expiring CDN URLs
- Resolution-first quality selection (
best,1080p,720p,540p,360p,smallest) - Post and author metadata: caption, create time, stats, sound details, post type
- Short-link support (
vm.tiktok.com,vt.tiktok.com,/t/) and embed/bare-ID inputs - Duplicate detection,
maxItems,maxFileSizeMb, and 1–3 way concurrency
How it works
TikTok exposes two different media paths, and this actor uses both:
- Embed fast path — the public embed page returns the watermarked MP4, cover, sound, slideshow images, author, stats, and caption without a browser or login.
- Browser page session — the clean
playAddrURL is bound to first-party cookies from the same page load and returns403when fetched outside that session. The actor opens the post in Chromium, downloads the selected clean stream inside the session, and stores it in Apify Key-Value Store so the output link keeps working.
Watermarked and direct assets are downloaded to Key-Value Store when downloadToKv is true. When it is false, their source URLs are returned instead. Clean video is always downloaded and re-hosted because a bare source URL would not work for you.
Input
| Field | Type | Default | Description |
|---|---|---|---|
postUrls |
array of strings | required | Canonical post URLs, short links, embed URLs, or numeric post IDs. Duplicates are processed once. |
assetTypes |
multi-select | ["video"] |
video, audio, cover, slideshow, metadata. |
videoVariant |
select | clean |
clean, watermarked, or both. |
quality |
select | best |
best sorts by resolution then bitrate. 1080p, 720p, 540p, 360p, smallest target the closest available stream. Watermarked copies are single-resolution. |
downloadToKv |
boolean | true |
Store watermarked video, audio, cover, and slideshow files and return kvsUrl. Clean video is always stored. |
maxFileSizeMb |
integer | 100 |
Per-file cap (1–100 MB). Larger files produce a too_large row. |
maxItems |
integer | 1000 |
Maximum unique posts to process (1–1000). Extra inputs get a limit_reached row. |
maxConcurrency |
integer | 3 |
Posts processed in parallel (1–3). |
proxyConfiguration |
object | none | Optional. The actor uses a direct connection by default. |
Example — one clean video at 540p
{
"postUrls": ["https://www.tiktok.com/@nba/video/7693329153680035102"],
"assetTypes": ["video"],
"videoVariant": "clean",
"quality": "540p",
"downloadToKv": true,
"maxItems": 1
}
Example — both video variants, cover, and metadata
{
"postUrls": ["https://www.tiktok.com/@nba/video/7693329153680035102"],
"assetTypes": ["video", "cover", "metadata"],
"videoVariant": "both",
"quality": "720p",
"downloadToKv": true
}
Example — slideshow frames and sound
{
"postUrls": ["https://www.tiktok.com/@khaby.lame/photo/7574844184952081686"],
"assetTypes": ["slideshow", "audio", "metadata"],
"downloadToKv": true
}
Example — URL-only mode for direct assets
{
"postUrls": ["https://www.tiktok.com/@nba/video/7693329153680035102"],
"assetTypes": ["audio", "cover"],
"downloadToKv": false
}
Output
Each dataset row contains the non-empty fields that apply:
postId,postUrl,postTypeassetType:video,audio,cover,slideshow,metadata, orpostfor post-level status rowsvariant:cleanorwatermarkedfor videoquality: selected quality labelordinal: slideshow frame indexmimeType,byteSize,width,height,durationkvsKey,kvsUrlfor stored filesoriginalUrl,expiresAtfor the source CDN URLdownloadedAt,requiresSessionstatus,error- Metadata rows add
caption,createTime, author fields, play/like/comment/share/collect counts, music fields, andimageCount
Status values
| Status | Meaning |
|---|---|
ok |
At least one requested asset or metadata row delivered |
not_found |
Deleted, removed, or unavailable post |
private |
Private account or friends-only post |
region_blocked |
Not available in the current region |
login_required |
Age-gated or login-gated post |
blocked |
TikTok bot wall or rate limit after retries |
unsupported_url |
Input is not a recognizable TikTok post |
short_link_dead |
Short link redirected to a homepage/profile, not a post |
no_video |
Video requested for a slideshow post |
no_slideshow |
Slideshow requested for a video post |
no_audio |
No sound track exposed |
no_cover |
No cover image exposed |
no_assets |
Post resolved but none of the requested assets were available |
download_failed |
CDN download or Key-Value Store write failed |
too_large |
File exceeds maxFileSizeMb |
duplicate |
Same post ID appeared earlier in the input |
limit_reached |
maxItems cap reached before this input was processed |
URL expiry and storage
- TikTok CDN URLs (
originalUrl) are signed and expire in about 48 hours (some music URLs expire within 24 hours). They are useful for traceability, not long-term hosting. kvsUrlpoints to the run's Apify Key-Value Store and lasts for that store's retention period. Download important files to your own storage.- Clean video source URLs only work with the extracting page session (
requiresSession: true). That is why the actor always downloads and re-hosts clean video instead of selling a bare URL.
Limits and behaviour
- Caps: 1–1000 posts per run, 3 concurrent posts, 100 MB per file.
- Deduplication: the first occurrence of a post ID is processed; later occurrences get a
duplicaterow. - Clean video: requires a browser session; if the page is blocked, the actor returns a status row for the clean asset while still delivering metadata/watermarked assets when the embed fast path succeeded.
- Region, private, and age-gated posts: no login and no user cookies are used. Posts that require either are reported with a status row rather than silently omitted.
- Field drift: the parser accepts both
imagePost.images(modern page data) andimagePostInfo.displayImages(embed/legacy), and MIME types are taken from the CDN response rather than assumed. - Watermarked resolution: TikTok exposes one watermarked copy per post, so
qualityaffects clean video only.
FAQ
Do I need a TikTok account or cookies? No. The actor uses public pages and browser-generated first-party cookies only.
Why can't I get a no-watermark URL without downloading?
TikTok's clean playAddr returns 403 text/html unless it is fetched with cookies from the same page session. The actor downloads the file in that session and re-hosts it.
How long do links stay valid?
kvsUrl lasts for the Key-Value Store retention period. originalUrl is a signed CDN URL that expires in about 48 hours.
What happens to posts that cannot be downloaded?
They produce a status row with status and error, so you can audit the run instead of diffing post IDs.
Are short links supported?
Yes. vm.tiktok.com, vt.tiktok.com, and /t/ links are resolved with an HTTP redirect. A dead link gets short_link_dead.
Can I use a proxy? It is optional. The actor is direct by default. If you want to route requests through Apify Proxy, select a proxy configuration in the input.
Why is a clean video row missing on a slideshow?
Slideshows have no video file. Request slideshow or audio instead; a no_video status row is emitted when video is requested.
Is this affiliated with TikTok? No. It is an independent tool for publicly available TikTok media. Only download content you have the right to use.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build 0qqLJHUzNNtm8LCyD. Documentation is regenerated from the default published build; private drafts are not included.