# TikTok Video Scraper - Stats, Transcript & Download by URL (`topapi/tiktok-video-scraper`) Actor

Scrape TikTok videos and photo posts from a list of URLs, short links or ids: plays, likes, comments, shares, saves, engagement rate, publish date, author stats, sound, every video quality, download links, subtitles and transcript. Monitor mode re-checks the same posts, returns only changed counts.

- **URL**: https://apify.com/topapi/tiktok-video-scraper.md
- **Developed by:** [Top Api](https://apify.com/topapi) (community)
- **Categories:** Social media, Videos, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 video scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does TikTok Video Scraper do?

**TikTok Video Scraper** extracts TikTok videos from [tiktok.com](https://www.tiktok.com) and turns a list of post links into structured rows: one video or photo post per row with its plays, likes, comments, shares, saves, publish date, author stats, sound, every video quality TikTok encodes, subtitle files and an optional transcript. Built for social-media analysts, influencer-marketing teams, brand and campaign monitors and research pipelines that already know *which* TikTok videos they care about and need the numbers behind them, refreshed on a schedule, without opening the app.

TikTok Video Scraper - TikTok Video Details API - TikTok URL Scraper - TikTok Video Stats Scraper - TikTok Video Downloader

<a href="https://console.apify.com/actors/WQ6cnBWaLJT5acuTE"><img src="https://api.apify.com/v2/key-value-stores/qNQ9zP0xaj2yxGeMY/records/tiktok-video-scraper-source.png" alt="TikTok video page with its play, like, comment, save and share counts that TikTok Video Scraper turns into a row" style="width:70%"></a>

- **Any link shape you copied:** canonical `tiktok.com/@user/video/<id>` and `/photo/<id>` URLs, `vm.tiktok.com` / `vt.tiktok.com` / `tiktok.com/t/` short links, `m.tiktok.com/v/<id>.html` mobile links, `embed/v2/<id>` embeds, share links and bare numeric ids, mixed in one list. Duplicates are fetched once.
- **Every number the post shows:** plays, likes, comments, shares, saves and reposts, a computed `engagementRate`, exact `createTimeISO`, duration, resolution, the country the post was made in and TikTok's own content labels.
- **The author on every row:** username, nickname, verified badge, bio, followers, following, total likes, video count and account creation date.
- **Files, not just links:** every encoding TikTok lists (`videoQualityLadder` with bitrate, codec, size and URLs), all cover images, the subtitle files per language, and the `downloadCookie` that makes TikTok's signed media URL fetchable from anywhere. The download add-on stores the MP4 or the photo-post images in a key-value store for you.
- **Transcript add-on:** the spoken text from TikTok's own caption files (creator captions or TikTok's speech recognition), no AI cost. On the 25-post test run, 18 posts had a caption file and all 18 came back with a transcript.
- **Monitor mode for schedules:** re-run the same list hourly or daily and get only posts that are new or whose counts moved; each delivered row carries `previousStats`, `statsDelta` and `isNew`.
- **Filters:** date window, minimum plays, videos-only or photo-posts-only, subtitles-only, exclude ads and paid partnerships.
- **No login, cookies or TikTok API key:** paste links and nothing else. Export as JSON, CSV, Excel, XML or RSS; schedules, webhooks, Make, Zapier, n8n, Google Sheets, Slack; Apify API and MCP server, with a `compact` mode for AI agents.

### What data can you get from a TikTok video?

<table>
<tr>
<td>🆔 Post id, canonical URL and the URL your link resolved to (`id`, `webVideoUrl`, `resolvedUrl`)</td>
<td>📝 Caption and its language (`text`, `textLanguage`)</td>
<td>📅 Publish time (`createTime`, `createTimeISO`) and country (`locationCreated`)</td>
</tr>
<tr>
<td>▶️ Plays (`playCount`)</td>
<td>❤️ Likes (`diggCount`)</td>
<td>💬 Comments (`commentCount`)</td>
</tr>
<tr>
<td>🔁 Shares and reposts (`shareCount`, `repostCount`)</td>
<td>🔖 Saves (`collectCount`)</td>
<td>📈 Engagement rate (`engagementRate`, percent of plays)</td>
</tr>
<tr>
<td>👤 Author: username, nickname, verified, bio, avatar, account created (`authorMeta`)</td>
<td>👥 Author stats: followers, following, total likes, videos (`authorMeta.fans`, `.heart`, `.video`)</td>
<td>🎵 Sound: name, artist, original flag, sound id, duration, play URL (`musicMeta`)</td>
</tr>
<tr>
<td>🎬 Video: duration, width, height, definition, format, bitrate, codec, file size, play and download URLs (`videoMeta`)</td>
<td>🪜 Every encoding TikTok lists: gear, bitrate, codec, resolution, bytes, URLs (`videoQualityLadder`)</td>
<td>🖼️ All cover and thumbnail URLs (`coverUrls`), photo-post images (`slideshowImageLinks`)</td>
</tr>
<tr>
<td>🔤 Subtitle files per language (`videoMeta.subtitleLinks`) and the transcript text (`transcript`, add-on)</td>
<td>#️⃣ Hashtags with ids (`hashtags`), mentions (`mentions`, `detailedMentions`), TikTok's search keywords for the post (`suggestedWords`)</td>
<td>📍 Tagged place (`poi`, `locationMeta`)</td>
</tr>
<tr>
<td>📢 Ad and paid-partnership flags (`isAd`, `isSponsored`)</td>
<td>🏷️ Content category labels (`diversificationLabels`, `categoryType`), AI-generated label (`isAIGC`)</td>
<td>⚙️ Duet, stitch, share and comment settings; on-screen text stickers (`textStickers`)</td>
</tr>
<tr>
<td>⬇️ Stored file from the download add-on (`mediaFile`: URL, bytes, image count)</td>
<td>🔁 Monitor mode: `previousStats`, `statsDelta`, `isNew`, `previousScrapedAt`</td>
<td>🚦 `status` on every row: ok, not_found, private, removed, restricted, unavailable</td>
</tr>
</table>

On a 25-post run on the Apify platform (23 videos and 2 photo posts from 17 creators) every row had the id, caption, publish time, country, all six counts, the engagement rate, the author's name and stats, the sound and the cover image; 23 of 25 (the videos) carried the file links, quality ladder and resolution; 18 of 25 carried a subtitle file and all 18 a transcript; 11 had hashtags, 13 mentions, 10 TikTok search keywords, 6 were ads or paid partnerships and 4 had a tagged place.

### How to scrape TikTok videos with TikTok Video Scraper

1. [Create a free Apify account](https://console.apify.com/sign-up) - $5 of monthly credit, no card needed.
2. Open [TikTok Video Scraper](https://apify.com/topapi/tiktok-video-scraper) and click **Try for free**.
3. Paste your links into **Video URLs or ids**, one per line, and tick **Include transcript** or **Download the video files** if you want them.
4. Click **Start**. Rows stream into the dataset as each page is read; 25 posts take about 20 seconds without downloads.
5. Open the **Storage** tab and **Export** as JSON, CSV or Excel - or save the input as a task, switch on **Deliver only new posts and changed counts**, and let a schedule run it for you.

### ⬇️ Input

| Parameter | Type | Description |
|---|---|---|
| **🔍 What to scrape** | | |
| `postURLs` | List | Post links or ids, one per line: `https://www.tiktok.com/@zachking/video/7095025543627705643`, `https://vt.tiktok.com/ZSjSKrdXn/`, `7172183875538488582`, `https://www.tiktok.com/@tiktok/photo/7666214645006421278`, `https://m.tiktok.com/v/<id>.html`, `https://www.tiktok.com/embed/v2/<id>`. Profiles, hashtags and search URLs are reported as invalid (they belong to TopApi/tiktok-scraper) |
| **✂️ Refine results** | | |
| `postedAfter` | String | Only posts published after: `2026-09-01`, `7 days`, `3 weeks`, or a day count (`1` = today) |
| `postedBefore` | String | Only posts published before: `2026-06-30`, `30 days` |
| `minPlays` | Integer | Minimum plays, e.g. `100000`. Default `0` |
| `postType` | Select | `all`, `video` (skip photo slideshows) or `photo` (slideshows only). Default `all` |
| `onlyWithSubtitles` | Boolean | Keep only posts that have a TikTok caption file. Default `false` |
| `excludeAds` | Boolean | Skip ads and paid partnerships (`isAd`, `isSponsored`). Default `false` |
| **➕ Extra data** | | |
| `includeTranscript` | Boolean | Adds `transcript`, `transcriptLanguage`, `transcriptSource` from TikTok's caption file - charged as the **transcript** event only for posts that receive one. Default `false` |
| `downloadVideos` | Boolean | Stores the MP4 (or a photo post's images) in a key-value store and puts the file URL in `mediaFile` and `videoMeta.downloadAddr` - charged as the **video-downloaded** event only for posts whose file was stored. Default `false` |
| `mediaStoreName` | String | Named key-value store for the files, e.g. `tiktok-videos` (keeps them past the run's data retention). Empty = the run's own store |
| `includeUnavailable` | Boolean | Write a row (`status`, `errorCode`, `error`, `input`) for private, removed, restricted and not-found posts so a bulk list comes back complete. Default `false` |
| **🔁 Monitoring** | | |
| `monitorMode` | Boolean | Deliver only posts that are new to the store or whose counts changed; rows carry `previousStats`, `statsDelta`, `isNew`. Pair with a schedule. Default `false` |
| `monitorStoreName` | String | Key-value store that keeps the per-post memory. Default `tiktok-video-monitor` |
| **📦 Output** | | |
| `maxItems` | Integer | Stop after this many delivered posts. `0` = every link in the list. Default `0` |
| `compact` | Boolean | Core fields only (id, status, text, date, URL, author, counts, engagement rate, duration, hashtags, sound, cover, transcript and statsDelta when requested), sized for AI agents and MCP. Default `false` |
| **⚙️ Advanced** | | |
| `proxyConfiguration` | Proxy | The fallback egress. Pages are first fetched from Apify's own IPs (measured 3x faster); the first wall switches the run to this proxy. Default: Apify Proxy, datacenter group |
| `maxConcurrency` | Integer | Posts fetched in parallel, 1-10. Default `5` |

#### Input JSON

```json
{
  "postURLs": [
    "https://www.tiktok.com/@zachking/video/7095025543627705643",
    "https://vt.tiktok.com/ZSjSKrdXn/",
    "7172183875538488582"
  ],
  "includeTranscript": true,
  "excludeAds": true,
  "monitorMode": true,
  "monitorStoreName": "campaign-q4"
}
```

#### Paste TikTok video links, not profile links

This actor reads one post per line. **Do:** `https://www.tiktok.com/@zachking/video/7095025543627705643` or `7095025543627705643` · **Don't:** `https://www.tiktok.com/@zachking` (a profile - the run lists it as invalid and points you to TopApi/tiktok-scraper, which walks a creator's videos). A `/photo/` link works exactly like a `/video/` link.

#### TikTok video short links are followed, dead ones are reported

`vm.tiktok.com`, `vt.tiktok.com` and `tiktok.com/t/` codes are resolved by following TikTok's redirects. A code that leads to the TikTok home page instead of a post (expired or removed) comes back as `not_found` with `errorCode: SHORT_LINK_DEAD` - only when `includeUnavailable` is on.

#### Monitor TikTok video stats with a named store per campaign

With `monitorMode` on, the first run delivers every post with `isNew: true` and remembers its six counts under `monitorStoreName`. Every later run re-reads the same links and delivers only the posts whose counts moved, with `statsDelta` showing the change and `previousScrapedAt` the last check. Give each client or campaign its own `monitorStoreName`, otherwise one list hides changes from another.

### ⬆️ Output

Every TikTok post becomes one dataset item. Here is the table view of a platform run (23 video and 2 photo links, transcript and download add-ons on):

<a href="https://console.apify.com/actors/WQ6cnBWaLJT5acuTE"><img src="https://api.apify.com/v2/key-value-stores/qNQ9zP0xaj2yxGeMY/records/tiktok-video-scraper-output.png" alt="TikTok Video Scraper output table with plays, likes, comments, shares, saves, engagement rate, transcript and stored file per video" style="width:70%"></a>

And one row as JSON (long URL fields shortened):

```json
{
  "id": "7650573331304860958",
  "status": "ok",
  "text": "it’s not every day your favorite #MusicTok and #FilmTok corners collide. watch @Lykke Li unpack her David Lynch collab...",
  "textLanguage": "en",
  "createTimeISO": "2026-06-12T18:12:17.000Z",
  "locationCreated": "US",
  "isAd": true, "isSponsored": true, "isSlideshow": false,
  "authorMeta": { "name": "tiktok", "nickName": "TikTok", "verified": true, "fans": 96000000, "heart": 464900000, "video": 1509, "createTimeISO": "2015-02-28T17:22:29.000Z", "profileUrl": "https://www.tiktok.com/@tiktok" },
  "musicMeta": { "musicName": "original sound", "musicAuthor": "TikTok", "musicOriginal": true, "musicId": "7650573341283126047", "duration": 71 },
  "webVideoUrl": "https://www.tiktok.com/@tiktok/video/7650573331304860958",
  "videoMeta": { "height": 1280, "width": 720, "duration": 71, "definition": "720p", "format": "mp4", "bitrate": 628434, "codecType": "h264", "size": 5579713, "coverUrl": "https://p16-common-sign.tiktokcdn-us.com/...", "downloadAddr": "https://api.apify.com/v2/key-value-stores/.../records/7650573331304860958.mp4", "originalDownloadAddr": "https://v16-webapp-prime.us.tiktok.com/...", "downloadCookie": "ttwid=...; tt_csrf_token=...; tt_chain_token=...", "subtitleLinks": [{ "language": "eng-US", "source": "ASR", "downloadLink": "https://v16-webapp.tiktokcdn-eu.com/..." }] },
  "videoQualityLadder": [{ "gear": "normal_720_0", "bitrate": 628434, "codec": "h264", "width": 720, "height": 1280, "sizeBytes": 5579713, "urls": ["..."] }],
  "playCount": 27500000, "diggCount": 133600, "commentCount": 2663, "shareCount": 567, "collectCount": 9679, "repostCount": 0,
  "engagementRate": 0.53,
  "hashtags": [{ "id": "1598392089630725", "name": "musictok", "title": "Music on TikTok." }, { "id": "1639367069624326", "name": "filmtok", "title": "Celebrate the world of cinema with TikTok." }],
  "mentions": ["@lykkeli", "@tmobile"],
  "diversificationLabels": ["Celebrity Clips & Variety Show", "Entertainment Culture", "Entertainment"],
  "transcript": "You've collaborated with an amazing list of artists.\nWhich one of those calls was the most surprising to get?\nDavid Lynch...",
  "transcriptLanguage": "eng-US", "transcriptSource": "automatic speech recognition",
  "mediaFile": { "url": "https://api.apify.com/v2/key-value-stores/.../records/7650573331304860958.mp4", "bytes": 4847592, "contentType": "video/mp4" },
  "submittedVideoUrl": "https://www.tiktok.com/@tiktok/video/7650573331304860958"
}
```

#### Fields

| Field | Type | What it is | Fill on the 25-post platform run |
|---|---|---|---|
| `id`, `webVideoUrl`, `resolvedUrl`, `submittedVideoUrl`, `input` | string | Post id, canonical URL (`/photo/` for slideshows), the URL the input resolved to, the input line | 100% |
| `status` | string | `ok` on delivered rows; `not_found`, `private`, `removed`, `restricted`, `unavailable`, `invalid` on the status rows | 100% |
| `text`, `textLanguage`, `createTime`, `createTimeISO`, `locationCreated` | string, integer | Caption, its language, publish time (Unix and ISO 8601), country code the post was created in | 100% |
| `playCount`, `diggCount`, `commentCount`, `shareCount`, `collectCount`, `repostCount` | integer | Plays, likes, comments, shares, saves, reposts | 100% |
| `engagementRate` | number | (likes + comments + shares + saves) / plays, in percent | 100% |
| `authorMeta` | object | `id`, `name`, `nickName`, `profileUrl`, `verified`, `signature`, `avatar`, `secUid`, `privateAccount`, `createTime`, `createTimeISO`, `downloadSetting`, `following`, `friends`, `fans`, `heart`, `video`, `digg` (`bioLink`, `region`, `ttSeller`, `commerceUserInfo` when TikTok prints them on the video page) | 100% (verified 56%) |
| `musicMeta` | object | `musicName`, `musicAuthor`, `musicOriginal`, `musicAlbum`, `musicId`, `duration`, `playUrl`, `coverMediumUrl`, `isCopyrighted`, `isCommerceMusic` | 100% (artist and cover 92%) |
| `videoMeta` | object | `height`, `width`, `duration`, `ratio`, `coverUrl`, `originalCoverUrl`, `dynamicCoverUrl`, `definition`, `format`, `bitrate`, `codecType`, `size`, `videoQuality`, `vqScore`, `loudness`, `peak`, `playAddr`, `downloadAddr`, `originalDownloadAddr`, `downloadCookie`, `subtitleLinks[]`, `captionInfo` | 100% (file links and resolution 92% = every video; photo posts have no video file) |
| `durationSec` | integer | Video length in seconds, 0 for photo posts | 100% |
| `videoQualityLadder` | array | Every encoding TikTok lists: `gear`, `qualityType`, `bitrate`, `codec`, `width`, `height`, `sizeBytes`, `urls[]`, largest first (2-5 per video) | 92% |
| `coverUrls` | array | Every static, animated and zoom-size cover URL (6-7 per video) | 100% |
| `mediaUrls` | array | TikTok's signed download and play URLs (fetch them with `videoMeta.downloadCookie` as the Cookie header) | 92% |
| `hashtags`, `mentions`, `detailedMentions`, `suggestedWords` | array | Hashtags with ids and descriptions; mentioned accounts with id and profile URL; the search keywords TikTok attaches to the post | 44% / 52% / 52% / 40% |
| `isSlideshow`, `slideshowImageLinks` | boolean, array | Photo posts and their images with width and height | 2 of 25 |
| `isAd`, `isSponsored` | boolean | TikTok's ad flag and paid-partnership flag | 6 of 25 flagged |
| `poi`, `locationMeta` | object | The tagged place (id, name, address, city, country, category), also in the `locationMeta` shape other TikTok scrapers use | 16% |
| `subtitleLanguages`, `transcript`, `transcriptLanguage`, `transcriptSource` | array, string | Caption languages; the transcript text (add-on) with its language and source (creator caption, speech recognition, machine translation) | 72% |
| `mediaFile` | object | Download add-on: `kvStoreId`, `key`, `url`, `bytes`, `contentType`, `imageCount` (and `urls[]` for photo posts) | 100% with the add-on on |
| `diversificationLabels`, `categoryType`, `isAIGC`, `aigcLabel` | array, integer, boolean, string | TikTok's content category labels and the AI-generated-content label | 88% / 100% / 0 of 25 labelled |
| `duetEnabled`, `stitchEnabled`, `shareEnabled`, `commentsDisabled`, `playlistId` | boolean, string | Interaction settings and the playlist the post sits in | 88% / 88% / 100% / 100% / 12% |
| `textStickers`, `effectStickers` | array | On-screen text stickers; effects used | 20% / 0 of 25 |
| `previousStats`, `previousScrapedAt`, `statsDelta`, `isNew` | object, string, boolean | Monitor mode only: the counts stored by the previous run, when, the difference, and whether the post is new to the store | monitor runs |
| `errorCode`, `error`, `url` | string | Status rows only: `POST_NOT_FOUND`, `POST_PRIVATE`, `POST_REMOVED`, `POST_RESTRICTED`, `POST_UNAVAILABLE`, `SHORT_LINK_DEAD`, `FETCH_BLOCKED`, `INVALID_URL` and TikTok's message | status rows |

Not on this surface, so not shipped: `isPinned`, attached links (`anchors`) and the TikTok Shop flag are properties of a creator's feed, and TikTok's video page carried none of them on the 34 posts captured for this build; TopApi/tiktok-scraper delivers them from profile and hashtag feeds. Related videos and comments are separate products.

### How can I use TikTok video data?

- 📊 **Campaign and sponsorship reporting** - paste the 40 creator posts of a campaign, run daily with `monitorMode`, and chart `statsDelta.playCount` per post in Google Sheets.
- 🎯 **Influencer vetting** - check a creator's last five posts you were sent: `engagementRate`, `authorMeta.fans`, `isSponsored` and whether `transcript` matches the brief.
- 🔎 **Social listening** - feed `text`, `hashtags`, `suggestedWords` and `transcript` of the posts your team collects into a classifier or an LLM; `compact` keeps a thousand posts inside a context window.
- 🗂️ **Archiving and evidence** - switch on `downloadVideos` with a named `mediaStoreName` to keep the MP4 and the counts as they were on a given day.
- 🎬 **Media and trend research** - compare duration, `videoQualityLadder`, `diversificationLabels` and `musicMeta` across the posts of a trend.
- 🤖 **AI agents** - an agent that is handed a TikTok link calls the actor through MCP and answers with the caption, counts and transcript.

### How much does it cost to scrape TikTok videos?

TikTok Video Scraper is pay-per-result: **$1.40 per 1,000 videos**, no start fee (the Pricing tab always shows the live rate). The transcript add-on is **$1.00 per 1,000 posts that receive one**, the download add-on **$1.20 per 1,000 posts whose file was stored**.

| Videos | Cost | With transcripts | With transcripts and files |
|---:|---:|---:|---:|
| 10 | $0.014 | $0.024 | $0.036 |
| 100 | $0.14 | $0.24 | $0.36 |
| 1,000 | $1.40 | $2.40 | $3.60 |
| 10,000 | $14.00 | $24.00 | $36.00 |

There is nothing else to pay: Apify platform usage (compute, storage, transfer) is included in these prices. Measured on the platform on 2026-10-03 with the default 256 MB: 2 posts in 5.7 seconds; 25 posts with 18 transcripts and 25 stored files (255 MB of video) in 315 seconds (most of it storing the files); without downloads a page takes 0.5-1.5 seconds and 5 are read in parallel.

### Integrations, API, scheduling & MCP

- **Schedules:** run TikTok Video Scraper hourly, daily or weekly from the **Schedules** tab; combine with `monitorMode` so each run returns only the posts whose counts moved.
- **Webhooks:** fire a URL when a run finishes - push rows to your CRM, Slack or a sheet.
- **No-code:** Make, Zapier, n8n, Google Sheets, Airtable, Slack via [Apify integrations](https://docs.apify.com/integrations).
- **API:** call it from Node.js or Python with `apify-client` - code on the [API tab](https://apify.com/topapi/tiktok-video-scraper/api/python) and the [Apify API reference](https://docs.apify.com/api/v2).
- **MCP / AI agents:** connect Claude, Cursor or any MCP client through the [MCP tab](https://apify.com/topapi/tiktok-video-scraper/api/mcp) and ask for the details of a TikTok video in plain language; switch on `compact` to keep the answer small.

### More TikTok video and social scrapers from TopApi

<table>
<tr>
<td>🎵 <a href="https://apify.com/topapi/tiktok-scraper">TikTok Scraper</a> - a creator's videos, a hashtag feed or a search, when you do not have the links yet</td>
<td>🎬 <a href="https://apify.com/topapi/tiktok-video-scraper">TikTok Video Scraper</a> - this actor: full detail of the posts you already have links for</td>
<td>📸 <a href="https://apify.com/topapi/instagram-scraper">Instagram Scraper</a> - posts, reels and profiles</td>
</tr>
<tr>
<td>📘 <a href="https://apify.com/topapi/facebook-search-posts-scraper">Facebook Search Posts Scraper</a> - public posts by keyword</td>
<td>⭐ <a href="https://apify.com/topapi/google-maps-reviews-scraper">Google Maps Reviews Scraper</a> - every review of a place with owner replies</td>
<td>💼 <a href="https://apify.com/topapi/indeed-jobs-scraper">Indeed Jobs Scraper</a> - job postings by keyword and location</td>
</tr>
<tr>
<td colspan="3">🧭 <a href="https://apify.com/topapi">All TopApi social-media and lead-gen actors</a></td>
</tr>
</table>

### TikTok video limits & empty runs

- **One page per post, no per-run ceiling:** every link is its own fetch, so a list of 5,000 links is 5,000 pages; at the default 5 parallel fetches that is roughly 20-30 minutes without downloads. Raise `maxConcurrency` to 10 for big lists.
- **Anti-bot interstitials:** about 1 request in 10 is answered with TikTok's "Please wait" page; the actor retries on a fresh proxy session up to three times per post. A post that is still blocked after that is reported as `blocked` in the run summary (and as a status row with `includeUnavailable`).
- **Downloads are slow by nature:** a 10 MB video takes about 12 seconds to fetch and store on 256 MB; two files are stored at a time. Files larger than about 30% of the run's memory (76 MB at 256 MB, 153 MB at 512 MB) are skipped - give long videos more memory.
- **Media URLs expire** after a few hours and `downloadCookie` with them; `mediaFile.url` from the download add-on is permanent for as long as the key-value store exists.
- **Empty dataset:** means every link was private, removed or invalid, your filters removed everything, or monitor mode found no changes - the status message names which.
- **Not available:** comments and related videos (separate products), the pinned flag and attached links (feed properties), an author's email or phone (TikTok publishes none), view history over time (each run is a snapshot; monitor mode keeps the last one).

### FAQ

#### How many TikTok videos can TikTok Video Scraper collect in one run?

As many links as you paste: there is no TikTok-side ceiling, because every post is its own page. On the platform 25 posts with transcripts and downloads took 315 seconds and 2 posts without add-ons 5.7 seconds; a list of a few thousand links fits one run with the default one-hour timeout.

#### Can I run TikTok Video Scraper on a schedule to track TikTok video stats automatically?

Yes. Save your input as a task, open **Schedules**, pick the cadence, and switch on `monitorMode`. Each run re-reads the same links and delivers only the posts whose plays, likes, comments, shares, saves or reposts moved, with `statsDelta` on the row. A run that finds no change delivers nothing.

#### Can I integrate TikTok Video Scraper with other apps?

Yes - Make, Zapier, n8n, Google Sheets, Slack and more via [Apify integrations](https://docs.apify.com/integrations), or use [webhooks](https://docs.apify.com/integrations/webhooks) to trigger an action when a run finishes.

#### Can I use TikTok Video Scraper with the Apify API?

Yes. The API lets you start runs, fetch datasets and manage schedules. Use `apify-client` for [Node.js](https://apify.com/topapi/tiktok-video-scraper/api/javascript) or [Python](https://apify.com/topapi/tiktok-video-scraper/api/python); the API tab has ready-to-paste code.

#### Can I use TikTok Video Scraper through an MCP server?

Yes. Open the [MCP tab](https://apify.com/topapi/tiktok-video-scraper/api/mcp), connect your MCP client (Claude Desktop, Cursor, LibreChat...), hand it a TikTok link and ask for the counts or the transcript in plain language. Set `compact: true` so a batch of posts fits the model's context.

#### Do I need a TikTok account, cookies or an API key?

No. TikTok Video Scraper reads only what TikTok shows a signed-out visitor on the video page; you pass links or ids and nothing else. The `downloadCookie` on each row is a visitor cookie TikTok handed the run, included so the signed media URL works from your own machine.

#### Why did my run return no TikTok videos?

In order of frequency on test runs: (1) the links were profiles, hashtags or search pages rather than posts - they are listed as invalid in the log and the run summary; (2) the posts are private, removed or restricted - switch on `includeUnavailable` to get a status row for each; (3) your filters removed everything - the run summary lists how many posts were read and why they were dropped; (4) `monitorMode` found no count changes since the last run; (5) TikTok answered every fetch with its interstitial after three fresh sessions - retry in a few minutes.

#### Does TikTok Video Scraper download the video files?

Yes, with `downloadVideos: true`: the MP4 (or every image of a photo post) is stored in the run's key-value store or in the named `mediaStoreName`, and the row carries `mediaFile` with the stored URL and size. Without the add-on you still get TikTok's own signed URLs plus the `downloadCookie` that makes them fetchable from anywhere for a few hours.

#### Can TikTok Video Scraper give me the transcript of a TikTok video?

Yes, with `includeTranscript: true`: it fetches TikTok's own caption file (the creator's captions or TikTok's automatic speech recognition) and delivers the plain text in `transcript`, with the language and source. Videos without a caption file return `null`; on the test run 18 of 25 posts had one.

#### Does TikTok Video Scraper work with short links and photo posts?

Yes. `vm.tiktok.com`, `vt.tiktok.com` and `tiktok.com/t/` links are resolved first (the resolved URL is on the row), and `/photo/` posts come back as the same row shape with `isSlideshow: true`, their images in `slideshowImageLinks` and a duration of 0.

#### How is TikTok Video Scraper different from other TikTok video scrapers?

It delivers the same post fields as the lane's leading TikTok Video Scraper plus `engagementRate`, the full `videoQualityLadder`, every cover URL, the author's account age, TikTok's search keywords and content labels, the transcript text from TikTok's own captions, and a `downloadCookie` that makes the media URLs usable off-platform; it filters by date, plays, type, subtitles and ads, adds no start fee, resolves short links, and ships a monitor mode that returns only changed counts. On the 25-post test run every delivered row carried all six counts and the author's stats.

#### Is it legal to scrape TikTok video data?

TikTok Video Scraper extracts only publicly available TikTok posts; it does not log in or collect private data. Results may still contain personal data (creator names, captions), which is regulated by laws such as GDPR. Make sure you have a legitimate purpose before you use it.

#### Your feedback

We keep improving TikTok Video Scraper. Found a bug or missing a field? Open an issue on the [Issues tab](https://apify.com/topapi/tiktok-video-scraper/issues/open) - we answer within two working days.

# Actor input Schema

## `postURLs` (type: `array`):

TikTok post links or ids, one per line, e.g. "https://www.tiktok.com/@zachking/video/7095025543627705643", "https://vt.tiktok.com/ZSjSKrdXn/", "7172183875538488582". Photo posts (/photo/), m.tiktok.com/v/, embed/v2/ and share links work too. Duplicates are fetched once.

## `postedAfter` (type: `string`):

Absolute date (2026-09-01, 2026-09-01T12:00:00Z), relative to now (7 days, 3 weeks, 2 months, 1 year) or a day count (1 = today's posts, 2 = yesterday and today). Leave empty for no lower bound.

## `postedBefore` (type: `string`):

Absolute date or relative (e.g. 30 days). Combine with the field above for a window such as postedAfter "2026-01-01" + postedBefore "2026-06-30".

## `minPlays` (type: `integer`):

Keep only posts with at least this many plays (playCount), e.g. 100000. 0 = no minimum.

## `postType` (type: `string`):

Photo posts (slideshows) come back with isSlideshow true and their image links in slideshowImageLinks; videoMeta.duration is 0 for them.

## `onlyWithSubtitles` (type: `boolean`):

Keep only posts for which TikTok publishes a caption file (videoMeta.subtitleLinks). Useful with the transcript add-on: on the test set every video post had at least one caption file, photo posts never do.

## `excludeAds` (type: `boolean`):

Skip posts TikTok marks as ads (isAd) or as paid partnership / branded content (isSponsored). Skipped posts are listed in the run summary.

## `includeTranscript` (type: `boolean`):

Adds the spoken text of the video as plain text (transcript, transcriptLanguage, transcriptSource) from TikTok's own caption files - the creator's captions or TikTok's automatic speech recognition, no AI cost. Charged as the transcript event only for posts that receive one.

## `downloadVideos` (type: `boolean`):

Saves each video (MP4) or a photo post's images to a key-value store and puts the stored file's URL on the row (mediaFile, videoMeta.downloadAddr); TikTok's own link stays in originalDownloadAddr. Charged as video-downloaded only for posts whose file was stored; files over ~75 MB are skipped.

## `mediaStoreName` (type: `string`):

Name of a key-value store to keep the downloaded files in, e.g. "tiktok-videos". Leave empty to use the run's own store (files then expire with the run's data retention). Letters, digits and hyphens.

## `includeUnavailable` (type: `boolean`):

Write a row (status, errorCode, error, input) for posts that are private, removed, restricted or not found, so a bulk list comes back complete. Off: unavailable posts appear only in the run summary.

## `monitorMode` (type: `boolean`):

Remembers each post's last plays, likes, comments, shares, saves and reposts in the named key-value store below. A re-run delivers a post only if it is new to the store or any count changed, with previousStats, statsDelta and isNew on the row.

## `monitorStoreName` (type: `string`):

Name of the key-value store that keeps the per-post memory for monitor mode. Give each campaign or client its own name so they do not hide changes from each other. Letters, digits and hyphens.

## `maxItems` (type: `integer`):

Stop after this many delivered posts (your cost cap; you pay per delivered post). 0 = every URL in the list. One run handles thousands of URLs; TikTok has no per-run ceiling here because every post is its own page.

## `compact` (type: `boolean`):

Core fields only (id, status, text, date, URL, author, authorFans, plays, likes, comments, shares, saves, reposts, engagementRate, duration, hashtags, musicName, coverUrl, transcript and statsDelta when requested): about a tenth of the bytes, sized for AI agents and MCP clients.

## `proxyConfiguration` (type: `object`):

The fallback egress. Each page is first fetched from Apify's own IPs (served, measured 3x faster); the first wall switches the run to this proxy. Datacenter is enough for TikTok video pages, residential is not needed. Files come straight from TikTok's CDN.

## `maxConcurrency` (type: `integer`):

How many posts are fetched at the same time. 10 parallel fetches of 400 KB pages ran in under 2 s on the test; the default 5 is safe for lists of thousands.

## Actor input object example

```json
{
  "postURLs": [
    "https://www.tiktok.com/@zachking/video/7095025543627705643",
    "https://www.tiktok.com/@guinnessworldrecords/video/7172183875538488582"
  ],
  "minPlays": 0,
  "postType": "all",
  "onlyWithSubtitles": false,
  "excludeAds": false,
  "includeTranscript": false,
  "downloadVideos": false,
  "includeUnavailable": false,
  "monitorMode": false,
  "monitorStoreName": "tiktok-video-monitor",
  "maxItems": 0,
  "compact": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 5
}
```

# Actor output Schema

## `videos` (type: `string`):

The dataset of TikTok posts (one item per video or photo post).

## `summary` (type: `string`):

Per-URL outcome (delivered, filtered, unchanged, not found, private, blocked), transcripts and files stored, bytes moved.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "postURLs": [
        "https://www.tiktok.com/@zachking/video/7095025543627705643",
        "https://www.tiktok.com/@guinnessworldrecords/video/7172183875538488582"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("topapi/tiktok-video-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "postURLs": [
        "https://www.tiktok.com/@zachking/video/7095025543627705643",
        "https://www.tiktok.com/@guinnessworldrecords/video/7172183875538488582",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("topapi/tiktok-video-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "postURLs": [
    "https://www.tiktok.com/@zachking/video/7095025543627705643",
    "https://www.tiktok.com/@guinnessworldrecords/video/7172183875538488582"
  ]
}' |
apify call topapi/tiktok-video-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,topapi/tiktok-video-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/WQ6cnBWaLJT5acuTE/builds/AG1c8wimeer7jjwBf/openapi.json
