# Tiktok Video Scraper (`fertech/tiktok-video-scraper`) Actor

- **URL**: https://apify.com/fertech/tiktok-video-scraper.md
- **Developed by:** [Fertech](https://apify.com/fertech) (community)
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.49 / 1,000 results

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

## TikTok Video Scraper

Extract complete data from any TikTok video URL — play counts, likes, shares, comments, author stats, music and video metadata, **plus subtitles, TikTok's own content categories, and the search keywords TikTok associates with each video**.

**One flat rate, whatever Apify plan you are on. No plan ladder — see the
pricing section of this listing for the current rate.**

Most TikTok scrapers advertise a "from $X" headline that only applies on the
top subscription tier, and charge Free and Starter customers considerably
more. This one charges everyone the same.

### What you get

Feed it a list of TikTok video URLs, get one structured record per video.

- **Engagement**: plays, likes, shares, comments, saves, reposts
- **Author**: username, nickname, verification, bio, followers, following, total likes, video count
- **Video**: duration, dimensions, cover image
- **Music**: track name, artist, original-sound flag, playable URL
- **Content**: caption text, hashtags, mentions, language, country of creation
- **Subtitles**: per-language WebVTT download links
- **Discovery data**: TikTok's own content categories and the search keywords it associates with the video — fields most scrapers do not return
- **Flags**: ad, pinned, slideshow, AI-generated

### Familiar output format

Field names match the widely-used Clockworks TikTok scrapers (`authorMeta.name`, `diggCount`, `playCount`, `videoMeta.duration`, `createTimeISO`, and so on), so existing pipelines and spreadsheets built around that shape will recognise this output.

### Input

```json
{
  "postURLs": [
    "https://www.tiktok.com/@apifytech/video/7398101551744552225"
  ],
  "maxAttemptsPerUrl": 4
}
```

| Field | Required | Description |
|---|---|---|
| `postURLs` | Yes | TikTok video URLs. Short `vm.tiktok.com` / `vt.tiktok.com` / `tiktok.com/t/...` links work too. |
| `maxAttemptsPerUrl` | No | Total attempts per URL before giving up. Default 4. |

Unrecognised URLs are reported as error records rather than failing the run.

### Output

```json
{
  "id": "7242449293112577323",
  "text": "The struggle man lol",
  "createTimeISO": "2023-06-08T22:42:18.000Z",
  "authorMeta": {
    "name": "selenagomez",
    "nickName": "Selena Gomez",
    "verified": true,
    "fans": 58600000,
    "heart": 715400000,
    "video": 270
  },
  "musicMeta": { "musicName": "original sound", "musicAuthor": "Selena Gomez", "musicOriginal": true },
  "videoMeta": { "width": 576, "height": 1024, "duration": 13 },
  "diggCount": 15400000,
  "playCount": 100200000,
  "shareCount": 320400,
  "commentCount": 32400,
  "collectCount": 465929,
  "repostCount": 0,
  "hashtags": [],
  "mentions": [],
  "locationCreated": "US",
  "textLanguage": "en",
  "subtitleLinks": [
    { "language": "eng-US", "format": "webvtt", "source": "ASR", "downloadLink": "https://..." }
  ],
  "diversificationLabels": ["Comedy", "Performance"],
  "suggestedWords": ["Selena Gomez", "selena gomez dress merah"],
  "isSlideshow": false,
  "isPinned": false,
  "isAd": false,
  "isAigc": false,
  "webVideoUrl": "https://www.tiktok.com/@selenagomez/video/7242449293112577323",
  "submittedVideoUrl": "https://www.tiktok.com/@selenagomez/video/7242449293112577323"
}
```

### Pricing: every submitted URL is charged once

Every URL you submit is charged exactly once, whatever happens to it — delivered, or one of the error outcomes below. TikTok serves transient anti-bot challenges, and videos get deleted; when a URL cannot be scraped, you get an error record in the dataset instead of a video record, so you know exactly what happened to each one, but it is billed the same as a delivered result:

```json
{
  "submittedVideoUrl": "https://www.tiktok.com/@user/video/123",
  "error": "not-found",
  "errorDescription": "Video is deleted, private or does not exist"
}
```

Error types are:

- `invalid-url` — not a recognised TikTok video URL
- `not-found` — the video is deleted, private, or does not exist
- `blocked` — TikTok's anti-bot challenge was not passed after all retries
- `unsupported-page` — the page loaded but wasn't a recognised video layout (not a block, and not retried, since re-fetching would return the identical page)
- `network` — a connection/TLS/DNS/timeout failure, not a TikTok block; check your proxy configuration

### Requirements

**Residential proxies are required.** TikTok blocks datacenter IP addresses aggressively.

Residential proxies are **not included in the Apify Free plan**. On the Free plan this Actor will stop with a clear message rather than burning your credit on requests that cannot succeed — you will need to upgrade.

There is no proxy setting to configure. TikTok blocks datacenter IPs at close to 100%, so the Actor always uses Apify residential proxies; offering a choice would only let a run be configured in a way that cannot work.

### Limitations

Stated plainly so you can decide before you run it:

- **Video URLs only.** Profiles, hashtags, keyword search and comments are not supported in this version.
- **No watermark-free video downloads.**
- **No direct video file URLs.** TikTok's own links are IP-bound and expire within hours, so returning them would only hand you broken links.
- Subtitle download links carry an expiry — fetch them promptly.

### Reading the run summary

Each run writes a summary to the key-value store and the log:

```
requested 200 | delivered 187 (93.5%) | unpaid 0 | waf-retries 148 | notfound 8 | failed 5
```

Note that **these fields do not sum to `requested`**. Every URL ends as exactly one of `delivered`, `notfound` or `failed`. `waf-retries` counts *attempts*, not URLs — one video that is challenged twice and then succeeds adds 1 to `delivered` and 2 to `waf-retries`.

### Using the API

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("YOUR_USERNAME/tiktok-video-scraper").call(run_input={
    "postURLs": ["https://www.tiktok.com/@apifytech/video/7398101551744552225"],
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["playCount"], item["authorMeta"]["name"])
```

### Is scraping TikTok legal?

This Actor collects only publicly available data — information anyone can see without logging in. It does not access private accounts or login-walled content. You remain responsible for how you use the data, particularly regarding personal information and the GDPR. If you plan to process personal data, seek your own legal advice first.

### Issues and requests

Found a bug, or want profiles, hashtags, search or comments supported? Open an issue on the Actor's Issues tab — feature demand genuinely drives what gets built next.

# Changelog

This Actor's version history is a separate document: https://apify.com/fertech/tiktok-video-scraper/changelog.md

# Actor input Schema

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

One or more TikTok video URLs, e.g. https://www.tiktok.com/@user/video/1234567890. Short vm.tiktok.com links are supported.

## `maxAttemptsPerUrl` (type: `integer`):

TikTok serves transient anti-bot challenges. Each URL is retried on a new proxy session up to this many times. More attempts improve the success rate at the cost of proxy bandwidth.

## Actor input object example

```json
{
  "postURLs": [
    "https://www.tiktok.com/@apifytech/video/7398101551744552225"
  ],
  "maxAttemptsPerUrl": 4
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped video records and any error records, newest run first.

## `runSummary` (type: `string`):

Delivery counts and block rate for the run: requested, delivered, unpaid, waf-retries, notfound, failed.

# 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/@apifytech/video/7398101551744552225"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fertech/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/@apifytech/video/7398101551744552225"] }

# Run the Actor and wait for it to finish
run = client.actor("fertech/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/@apifytech/video/7398101551744552225"
  ]
}' |
apify call fertech/tiktok-video-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fertech/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/6EKZ3zRIu5cPsFgeF/builds/CtO65jZTaZVXoHDf3/openapi.json
