# TikTok Transcript Scraper (`apihq/tiktok-transcript-scraper`) Actor

Export the captions TikTok already has on public videos as timestamped segments plus full text, by video URL. Original spoken language by default, or pick one. Videos without captions return success:false, cost $0, and never break the batch. No AI transcription. $0.90 per 1,000 transcripts.

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

## Pricing

$0.90 / 1,000 transcripts

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 Transcript Scraper

Export the captions that already exist on public TikTok videos as clean JSON from their video URLs: timestamped segments, the full text in one string, the caption language and source, and the video's author and counts. **Existing TikTok captions only: this Actor does no AI transcription.** A video without captions returns a free `success: false` row with `code: TIKTOK_NO_CAPTIONS`. No TikTok account, no API key, no browser.

The contract is simple: **you only pay for transcripts delivered.** A video with no captions, a deleted video, or a bad URL comes back as a `success: false` row while the rest of the batch keeps going. $0.90 per 1,000 transcripts. Failed videos cost $0, and there is no start fee.

### See one run

Input:

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

What comes back:

- 1 `success: true` transcript for the `apifytech` video, billed: TikTok's English auto captions as 12 timestamped segments (the first is "Are you doing research in HR" at 0.02 s), the same text joined in `full_text`, and the video's author, duration, and counts
- 1 `success: false` row, `code: TIKTOK_NO_CAPTIONS`, for the `scout2015` video, which has no captions on TikTok, not billed
- Run status: **SUCCEEDED**
- Charge: 1 delivered transcript x $0.0009 = $0.0009

Videos with captions return billed transcripts, videos without captions return free error rows in the same dataset, and the run still succeeds, so one captionless video never costs you a batch.

### Why this Actor

- **You pay only for transcripts.** Billing fires on an explicit per-transcript event, not on dataset writes. One delivered transcript is one charge, however many segments it has. Rows for videos without captions are free, and there is no per-run start fee.
- **Parsed, not raw.** You get `transcript` segments with `text`, `start`, and `duration` in seconds, plus `full_text`, instead of a WebVTT file you have to parse yourself.
- **You know where the text came from.** Every transcript says its `language`, its `source` (`ASR` for TikTok's own auto captions, `MT` for TikTok's machine translation, `creator` for captions the creator added), and whether it is auto-generated or translated. `available_tracks` lists every caption track the video has.
- **Every miss is machine-readable.** Each failure carries a stable `code` (`TIKTOK_NO_CAPTIONS`, `TIKTOK_LANGUAGE_UNAVAILABLE`, `TIKTOK_VIDEO_UNAVAILABLE`, `VALIDATION_FAILED`, ...), so your pipeline branches on the reason without parsing English.
- **Same shape as our YouTube transcripts.** Field names (`video_id`, `language`, `transcript`, `is_auto_generated`, `available_tracks`, `code`, `request_id`) match the [YouTube Transcript Scraper](https://apify.com/apihq/youtube-transcript-scraper), so one pipeline can read both.

### Best for

AI and data workflows that turn TikTok videos into text: RAG pipelines and LLM datasets, search over what creators actually say, sentiment, topic, and brand-safety analysis, content research on known videos, and subtitle and accessibility tooling. The free no-caption rows and stable error codes matter most when transcripts feed an automated pipeline, where one captionless video should never take down the batch. It works fine for one-off manual pulls too.

### Input example

A single video with TikTok captions:

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

A batch mixing videos with and without captions, copied from the browser with tracking parameters (they are ignored, and copies of the same video run once). The `scout2015` video has no captions and returns a free `TIKTOK_NO_CAPTIONS` row:

```json
{
  "postURLs": [
    "https://www.tiktok.com/@apifytech/video/7398101551744552225?is_from_webapp=1&sender_device=pc",
    "https://www.tiktok.com/@renreports/video/7307726351291960622",
    "https://www.tiktok.com/@scout2015/video/6718335390845095173",
    "https://m.tiktok.com/@apifytech/video/7398101551744552225"
  ]
}
```

A Spanish video in English. The video is spoken in Spanish and TikTok offers an English machine translation of its captions; `"language": "en"` returns that translation (`source: "MT"`, `is_translated: true`). Without `language` you get the original Spanish captions:

```json
{
  "postURLs": ["https://www.tiktok.com/@blandon_esther/video/7498938601602813227"],
  "language": "en"
}
```

| Field | Type | Default | What it does |
|---|---|---|---|
| `postURLs` | array of strings | | TikTok video URLs, up to 50 unique videos per run. Copies of the same video (with `www.`, `m.`, a query string, or a trailing slash) are run and billed once. |
| `language` | string | blank | Caption language to return, such as `en`, `en-US`, or `es`. Blank returns the captions in the video's original spoken language. When set, an exact match wins, then the same language in another region (`en` matches `en-US`). A TikTok translation is returned only when you ask for its language. Another language is never substituted. |

### Output example

One record per video. The transcript for the `apifytech` video (TikTok's English auto captions):

```json
{
  "success": true,
  "video_id": "7398101551744552225",
  "video_url": "https://www.tiktok.com/@apifytech/video/7398101551744552225",
  "language": "en-US",
  "language_raw": "eng-US",
  "source": "ASR",
  "is_auto_generated": true,
  "is_translated": false,
  "transcript": [
    { "text": "Are you doing research in HR", "start": 0.02, "duration": 1.4 },
    { "text": "or do you want to get a structured list of job listings", "start": 1.421, "duration": 2.32 },
    { "text": "without endlessly scrolling through?", "start": 3.742, "duration": 1.359 }
  ],
  "full_text": "Are you doing research in HR or do you want to get a structured list of job listings without endlessly scrolling through? Indeed. What if I told you that you can get a vast amount of job data from any specific location without any coding experience? The tool is called India Scraper. All you have to do is type in a job title and a location and you'll get your data. Try it out now on appify. a slash. Indeed! Oh, and don't forget to follow us for more tips on how to automate mundane tasks!",
  "available_tracks": [
    {
      "language_code": "en-US",
      "language_raw": "eng-US",
      "language_name": "English (United States)",
      "source": "ASR",
      "is_auto_generated": true,
      "is_original": true
    }
  ],
  "metadata": {
    "description": "Yes, you can scrape job listings #data in a minute!!   #webscraping #techtiktok #job #automation ",
    "create_time": 1722504752,
    "create_time_iso": "2024-08-01T09:32:32.000Z",
    "duration_seconds": 23,
    "author_handle": "apifytech",
    "author_name": "Apify",
    "author_url": "https://www.tiktok.com/@apifytech",
    "thumbnail_url": null,
    "play_count": 595,
    "digg_count": 21,
    "comment_count": 0,
    "share_count": 1
  },
  "submitted_video_url": "https://www.tiktok.com/@apifytech/video/7398101551744552225"
}
```

(3 of 12 segments shown; `full_text` is complete. The leading emoji of the description is dropped here. The captions are TikTok's own, mistakes included: "India Scraper" and "appify" are what TikTok's auto captions heard.)

A video without captions lands in the same dataset and is not charged:

```json
{
  "success": false,
  "video_id": "6718335390845095173",
  "submitted_video_url": "https://www.tiktok.com/@scout2015/video/6718335390845095173",
  "code": "TIKTOK_NO_CAPTIONS",
  "error": "TikTok exposes no captions for this video",
  "request_id": "req_4c7e2a9f0b1d4e8a9c3f5b62",
  "status_code": 422
}
```

So does a video that has captions, just not in the language you asked for (here `"language": "fr"` on the English `apifytech` video). The message lists the languages that do exist:

```json
{
  "success": false,
  "video_id": "7398101551744552225",
  "submitted_video_url": "https://www.tiktok.com/@apifytech/video/7398101551744552225",
  "code": "TIKTOK_LANGUAGE_UNAVAILABLE",
  "error": "No TikTok captions in the requested language. Available: en-US",
  "request_id": "req_9a1f3e5c7b2d4f60a8e1c3d5",
  "status_code": 422
}
```

Split hits from misses on the `success` field. Quote `request_id` in any support issue and we can trace the exact request.

### What you get

Every transcript is one record with `success: true`. Any value TikTok does not report is `null`:

| Field | Type | What it is |
|---|---|---|
| `success` | boolean | `true` for a delivered transcript (billed), `false` for a video without one (not billed). |
| `video_id` | string | The video ID. |
| `video_url` | string | The canonical video URL, with the author's current handle. |
| `language` | string | Language of the returned captions as a standard tag, such as `en-US` or `es-ES`. |
| `language_raw` | string | The same language as TikTok labels it, such as `eng-US`. |
| `source` | string or null | Where the captions come from: `ASR` (TikTok's auto captions), `MT` (TikTok's machine translation), `creator` (captions the creator added), another TikTok label, or `null` when TikTok does not say. |
| `is_auto_generated` | boolean or null | `true` for TikTok's auto captions and translations, `false` for creator captions. |
| `is_translated` | boolean or null | `true` when the captions are a TikTok translation rather than the spoken language. |
| `transcript` | array | Timestamped segments: `text`, `start`, and `duration`, both in seconds. |
| `full_text` | string | Every segment's `text` joined with one space. |
| `available_tracks` | array | Every caption track the video has: `language_code`, `language_raw`, `language_name`, `source`, `is_auto_generated`, `is_original` (`true` for the spoken language). |
| `metadata` | object | `description` (the post caption, not spoken text), `create_time` / `create_time_iso` (Unix seconds and ISO 8601 UTC), `duration_seconds`, `author_handle`, `author_name`, `author_url`, `thumbnail_url`, `play_count`, `digg_count` (likes), `comment_count`, `share_count`. |
| `submitted_video_url` | string | The input URL this record answers, as you gave it. |

IDs are strings because TikTok's 19-digit IDs do not fit in a JavaScript number.

Videos without a transcript carry `success: false`, `video_id` (when the URL could be read), `submitted_video_url` (the URL you gave), a machine-readable `code`, a human-readable `error`, the service `request_id`, and the HTTP `status_code`. They are not charged:

| Code | Meaning |
|---|---|
| `TIKTOK_NO_CAPTIONS` | TikTok has no captions for this video. Common for music-only clips and photo posts. |
| `TIKTOK_LANGUAGE_UNAVAILABLE` | The video has captions, but none in the requested language. The `error` lists the available languages. Also returned when blank `language` meets a video whose only captions are TikTok translations. |
| `TIKTOK_EMPTY_TRANSCRIPT` | TikTok's caption track exists but contains no text. |
| `TIKTOK_VIDEO_UNAVAILABLE` | The video is deleted, private, or otherwise unavailable. |
| `TIKTOK_UNAVAILABLE` | TikTok did not serve this video and gave no reason. |
| `TIKTOK_BLOCKED` | TikTok refused the request after an automatic retry. |
| `TIKTOK_RATE_LIMITED` | TikTok rate-limited the request after an automatic retry. |
| `TIKTOK_TIMEOUT` | TikTok did not answer in time after an automatic retry. |
| `DEADLINE_EXCEEDED` | The video request hit its 30-second deadline. |
| `VALIDATION_FAILED` | The URL is not a supported TikTok video URL (including short links and `/photo/` URLs), the `language` value is not a language code, or TikTok rejected the video ID. |
| `NO_INPUT` | No usable `postURLs` were given. The run ends successfully with this single row. |

### Pricing

Pay per event. One `transcript` charge per transcript delivered: `$0.0009` each, which is `$0.90 per 1,000 transcripts`. The charge fires only after the transcript record lands in your dataset.

- One delivered transcript is one charge, whether it has 3 segments or 300.
- 1,000 delivered transcripts cost $0.90.
- Videos without captions, language misses, unavailable videos, and bad URLs cost $0.
- There is no per-run start fee, so a run that delivers nothing costs nothing.

Platform compute is included in the per-transcript price. Cap the maximum spend of a single run from Apify's Run Limits panel; the Actor checks the cap before each video and stops cleanly when the budget runs out.

### What this Actor does not do

Honest scope, so you know before you run it:

- **Existing TikTok captions only.** It returns the captions TikTok already has on the video. It does no AI transcription, does not download audio, and does not read on-screen text (no OCR). A video without captions returns a free `success: false` row with `code: TIKTOK_NO_CAPTIONS`; many TikTok videos, especially music-only clips and photo posts, have none.
- **No other languages invented.** It returns a translation only when TikTok itself offers one in the language you asked for. It never translates captions on its own and never substitutes another language.
- **Video URLs only.** It reads the videos you give it. It does not find videos from a profile, hashtag, sound, or search query.
- **Full video URLs only.** Short share links (`vm.tiktok.com/...`, `vt.tiktok.com/...`) and `/photo/` URLs come back as a free `success: false` row with `code: VALIDATION_FAILED`. Open a short link in a browser and paste the full `https://www.tiktok.com/@user/video/<id>` URL instead.
- **No downloads.** It does not return video, audio, or caption files, or links to them. The transcript is in the record.
- **No login-only content.** Private videos and anything that needs a TikTok account return `success: false`.

### How to use this Actor

1. Open the Actor in the Apify Console.
2. Paste one or more TikTok video URLs into `postURLs`. Up to 50 unique videos run per job. Optionally set `language` (such as `en` or `es`).
3. Click Start. Each video produces one record: `success: true` with the transcript, or `success: false` with the reason. Only delivered transcripts are charged.

The Actor is also callable from the Apify API and every official integration (Make, Zapier, n8n, Slack, webhooks). The API tab in the Console has ready-to-paste JavaScript, Python, and curl snippets.

### Reliability

**A video without captions never fails the batch.** No captions, a language miss, a deleted video, or a bad URL becomes a `success: false` record with a specific `code`. The run keeps going and finishes successfully, even when no video in it had captions.

**You never pay for a miss.** Billing fires on an explicit per-transcript charge event, not on dataset writes, so `success: false` records are free. There is no per-run start fee either.

**Empty transcripts are failures.** A caption track that contains no text comes back as `success: false` with `code: TIKTOK_EMPTY_TRANSCRIPT` and is not billed. You never pay for a blank transcript.

**Automatic retry, then a clear answer.** A blocked, rate-limited, or slow request is retried once from a fresh network identity before the video is reported as failed. Each video runs under a 30-second deadline, so a stalled page returns a `success: false` record with `code: DEADLINE_EXCEEDED` instead of hanging your run.

**No duplicate charges.** The same video pasted several times in one input (with `m.`, a query string, or a trailing slash) is fetched and charged once per run.

**Genuine faults are not hidden.** An outage of our backend, a caption file TikTok changed in a way we cannot read, or a failure to write or bill results fails the run without a charge for the failed video, so a broken run never looks like a clean one with missing data.

### FAQ

**Does it transcribe videos without captions?**

No. The Actor returns only the captions TikTok already has on the video. A video without them comes back as a free `success: false` row with `code: TIKTOK_NO_CAPTIONS`. There is no AI speech-to-text fallback, so you never pay for a transcript TikTok did not make.

**Is it the same as TikTok auto captions?**

Yes. `source: "ASR"` means TikTok's own automatic captions, the ones viewers can turn on in the TikTok app. TikTok generated them; this Actor runs no speech recognition. Captions TikTok marks as not auto-generated come back as `source: "creator"`, TikTok's translations as `source: "MT"`, and `null` means TikTok did not say.

**Which languages?**

Whatever TikTok has for the video. By default you get the captions in the video's original spoken language. Set `language` (such as `en`, `en-US`, or `es`) to choose: an exact match wins, then the same language in another region. If TikTok offers a machine translation in that language, you get it with `is_translated: true`. If the video has no captions in that language, you get a free `TIKTOK_LANGUAGE_UNAVAILABLE` row that lists the languages it does have. `available_tracks` on every transcript shows all of them.

**Can I get plain text only?**

Use `full_text`: every segment joined into one string with single spaces. Ignore `transcript` if you do not need the timestamps.

**Do short links, photo posts, or profiles work?**

No. Use full video URLs: `https://www.tiktok.com/@user/video/<id>`, also with `m.tiktok.com`, without `www.`, with a query string, or with a trailing slash. Short links (`vm.tiktok.com`, `vt.tiktok.com`) and `/photo/` URLs return a free `VALIDATION_FAILED` row. The Actor does not list a profile's videos.

**Do I need a TikTok account or API key?**

No. The Actor reads public video pages directly. There is nothing to log into and no TikTok developer key to provision. (You run it with your normal Apify account, like any Actor.)

**How much does it cost?**

$0.90 per 1,000 delivered transcripts, one charge per transcript regardless of its length. Videos without captions, other failed videos, and empty runs cost $0, and there is no start fee.

**Why is a field `null`?**

TikTok did not report it for that video. The Actor never fills a gap with a guessed `0` or `false`, so `null` always means "unknown", not "none".

**Can I also get the video stats or comments?**

Yes, with the sibling Actors [TikTok Video Scraper](https://apify.com/apihq/tiktok-video-scraper) and [TikTok Comments Scraper](https://apify.com/apihq/tiktok-comments-scraper), which take the same `postURLs` and use the same failure-free billing. For YouTube, use the [YouTube Transcript Scraper](https://apify.com/apihq/youtube-transcript-scraper).

**Can I use this Actor from my own code?**

Yes. Use the Apify API or one of the official SDKs (Node.js: `apify-client`, Python: `apify-client`). The Console shows ready-to-paste code samples on the API tab.

### Found a bug or want a feature?

Open an issue on this Actor's Issues tab and include the `request_id` from any error record you saw. We respond within one business day.

# Actor input Schema

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

TikTok video URLs in the form https://www.tiktok.com/@user/video/<id>. Aliases of the same video are de-duplicated; up to 50 unique videos are processed per run. Existing TikTok captions only, no AI transcription: a video without captions comes back as a free success:false record with code TIKTOK_NO_CAPTIONS. Short vm.tiktok.com / vt.tiktok.com links and /photo/ posts are not supported and come back as a free record with code VALIDATION_FAILED. One bad video never blocks the batch.

## `language` (type: `string`):

Optional language code such as en, en-US or es. Leave empty for the video's original spoken-language captions. When set, the exact language, else the same language in another region, is returned; a TikTok-provided translation only when it matches. A video without captions in that language comes back as a free record with code TIKTOK_LANGUAGE_UNAVAILABLE listing the available languages.

## Actor input object example

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

# Actor output Schema

## `transcripts` (type: `string`):

One record per input video. success:true carries transcript segments, full_text, the track's language and source, available_tracks and metadata; success:false carries code and error.

# 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("apihq/tiktok-transcript-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("apihq/tiktok-transcript-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 apihq/tiktok-transcript-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,apihq/tiktok-transcript-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/ZatecMOXwx8nG4CbX/builds/xSZGJXKqX5iF5DpKO/openapi.json
