# TikTok Transcript Scraper: Captions & Speech to Text (`cliqtomedia/tiktok-transcript`) Actor

Get TikTok transcripts in bulk, no login: captions with timestamps, speech-to-text when a video has none, a profile's newest videos, SRT/VTT files and metadata. Repurpose videos, feed AI tools, research trends.

- **URL**: https://apify.com/cliqtomedia/tiktok-transcript.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (community)
- **Categories:** AI, Videos
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.63 / 1,000 videos

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

Get the transcript of any public TikTok video: timestamped text from TikTok captions, or speech-to-text when a video has no captions. No TikTok login, no cookies.

Paste one or more TikTok video links (or a profile name) and get one row per video with clean text, segments with start and end times, AI-ready chunks and video metadata. The smallest run is one video link and takes a few seconds.

![TikTok Transcript Scraper: TikTok links in, captions or speech-to-text, export to JSON, CSV or SRT](https://api.apify.com/v2/key-value-stores/iH7vlEbk8Qq5zkeGp/records/hero-20261101.png)

### Table of contents

- [What it does](#what-it-does)
- [How it works](#how-it-works)
- [Quick start](#quick-start)
- [Use cases](#use-cases)
- [Input](#input)
- [Output](#output)
- [Pricing](#pricing)
- [Limits](#limits)
- [API, export and integrations](#api-export-and-integrations)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Related Actors](#related-actors)
- [Support](#support)

### What it does

The Actor turns public TikTok videos into text. For each video it returns:

- the full **transcript** as plain text;
- **segments** with start and end times in seconds;
- **chunks** of about 1,000 characters with times, ready for AI tools;
- **metadata**: author, description, hashtags, date, duration, views, likes, comments, shares, saves and sound;
- a clear **status** and reason when a video has no text (photo post, library sound only, silence, deleted video and more).

Text comes from TikTok's own captions when the video has them. When it has none, the Actor listens to the audio and writes the text with Whisper speech-to-text (you can turn this off). It also can take the newest videos of a public profile and save SRT, VTT and TXT files.

#### Good fit

- Repurpose TikTok videos into blog posts, captions, newsletters or scripts.
- Feed video text to ChatGPT, Claude or a RAG pipeline for summaries and research.
- Track what creators or competitors say in their newest videos, on a schedule.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### How it works

1. The Actor opens the public page of each TikTok video, the same page anyone can see without an account. For a profile, it reads the list of its newest public videos.
2. If TikTok has captions for the video (written by the creator, made by TikTok's speech recognition, or TikTok's translation), the Actor uses them. Otherwise it downloads the audio for a moment and turns the speech into text. The video is not stored.
3. It saves one row per video in the dataset, with a status for every input link, and a short run summary (`RUN_SUMMARY`) in the key-value store.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### Quick start

[![Input form: box 1 is the TikTok video links field, box 3 is Transcript options](https://api.apify.com/v2/key-value-stores/iH7vlEbk8Qq5zkeGp/records/quick-start-form-20261101.png)](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)

1. Open the **Input** tab. The field **TikTok video links** already has one example video (box 1 in the picture).
2. Replace it with your own links, one per line. You can also add usernames in **Profiles**.
3. Keep **Transcript options** as they are (box 3): captions first, speech-to-text when there are none.
4. Click **Start**.
5. When the run ends, open the **Output** tab. The **Overview** table shows the status, where the text came from and the transcript (box 5).

   [![Output table: status, source and transcript columns of a finished run](https://api.apify.com/v2/key-value-stores/iH7vlEbk8Qq5zkeGp/records/output-table-20261101.png)](https://api.apify.com/v2/key-value-stores/iH7vlEbk8Qq5zkeGp/records/output-table-20261101.png)

Expected result: one row with status `complete`, source `captions` and about 100 words of English text with 12 timed segments.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### Use cases

| Buyer job | Input path | Output entity | Key fields | Next integration |
| --- | --- | --- | --- | --- |
| Turn a video into a blog post or script | `videoUrls` | One row per video | `transcript`, `description`, `hashtags` | Copy to a doc, or send to an LLM |
| Give an AI tool clean video text | `videoUrls` | Row with chunks | `chunks` (`text`, `start`, `end`), `videoUrl` | RAG / vector database, MCP agent |
| Make subtitles | `videoUrls` + `saveSubtitleFiles` | SRT / VTT / TXT files | `files.srt`, `files.vtt` | Video editor |
| Watch what creators say | `profiles` + `skipProcessed` | Rows for new videos only | `transcript`, `createTimeISO`, `stats` | Schedule, Slack or Notion summary |
| Research topics or trends | `profiles` or `videoUrls` | Rows with metadata | `transcript`, `hashtags`, `stats.playCount` | CSV / Excel |

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### Input

#### Input fields

| Field | Plain name | Type | Required | Default / prefill | Allowed values | Effect | Example | Recommendation |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `videoUrls` | TikTok video links | list of text | one of `videoUrls` / `profiles` | prefill: one example video | up to 1,000; video, short, photo and profile links, or video IDs | Which videos to transcribe | `https://www.tiktok.com/@apifytech/video/7398101551744552225` | Main input |
| `profiles` | Profiles | list of text | one of `videoUrls` / `profiles` | empty | up to 50 usernames or profile links | Takes the newest videos of each profile | `@apifytech` | For monitoring creators |
| `maxVideosPerProfile` | Videos per profile | number | no | 10 | 1–500 | How many newest videos per profile | `20` | Keep low for a first test |
| `language` | Caption language | text | no | empty (original language) | two-letter code | Uses TikTok captions in this language when they exist | `es` | Leave empty for the spoken language |
| `fallbackToSpeechToText` | Speech-to-text when no captions | yes/no | no | yes | yes / no | Transcribes audio of videos without captions | `false` | Turn off for the cheapest, fastest run |
| `translateToEnglish` | Translate to English | yes/no | no | no | yes / no | English captions, or Whisper translates the speech | `true` | When you need English for every video |
| `maxSpeechMinutesPerVideo` | Max speech minutes per video | number | no | 10 | 1–60 | Longer videos without captions get no text and no speech charge | `5` | Protects your budget |
| `chunkCharacters` | Chunk size | number | no | 1000 | 0–10,000 (0 = no chunks) | Size of AI-ready chunks | `2000` | Match your AI tool's context |
| `saveSubtitleFiles` | Save subtitle files | yes/no | no | no | yes / no | Saves SRT, VTT and TXT files, links in `files` | `true` | For video editors |
| `outputFormat` | Output format | choice | no | `nested` | `nested`, `flat` | `flat` makes columns like `authorUsername`, `playCount` | `flat` | `flat` for CSV / Excel |
| `skipProcessed` | Skip videos from earlier runs | yes/no | no | no | yes / no | Skips videos already returned ($0.0001 each instead of a full row) | `true` | For scheduled runs |
| `stateName` | State name | text | no | `tiktok-transcript-state` | letters, digits, `-` | Store that remembers returned videos | `brand-watch` | One name per monitoring task |
| `proxyConfiguration` | Proxy | proxy | no | none | Apify Proxy or your own | Direct by default; switches to Apify Proxy by itself if TikTok refuses | — | Usually leave empty |
| `deliveryConnector` | Send summary to | connector | no | none | your Slack, Notion or other MCP connector | Posts a short summary after the run | — | Optional |
| `deliveryTarget` | Where to post | text | no | empty | Notion page link or Slack channel ID | Target page or channel | — | Optional |

Give at least one link or profile; an empty input stops with a clear message. The same video given twice (for example with and without `?is_from_webapp=1`) is processed once. Text that is not a TikTok link gets its own row with `errorCode` `INVALID_URL`. A profile link in `videoUrls` works like a `profiles` entry.

#### Interacting settings

| When | Input A | Input B | Effective behavior | Example | Cost/result effect |
| --- | --- | --- | --- | --- | --- |
| A video has captions in several languages | `language` | `translateToEnglish` | `translateToEnglish` wins: English captions first, else Whisper translates | `language: es`, `translateToEnglish: true` | Whisper translation is charged like speech-to-text |
| A video has no captions | `fallbackToSpeechToText` | `maxSpeechMinutesPerVideo` | Transcribed only when on and the video is not longer than the limit | 15-minute video, limit 10 | No text (`TOO_LONG`), no speech charge |
| Scheduled monitoring | `skipProcessed` | `stateName` | Videos returned before under the same name are skipped | daily run of `@brand` | Skipped videos cost $0.0001 each (`video-checked`) |
| You set a maximum cost per run | Apify run option | `fallbackToSpeechToText` | Speech-to-text stops when the next unit does not fit; the last text may be cut (`TRUNCATED`) | max $0.05 | You never pay more than you set |

#### Input examples

##### One video

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

Expected behavior: reads TikTok's captions of the video.
Expected output: one row, `status` `complete`, `source` `captions`, `transcript`, 12 `segments`.
Limit or cost note: the start fee ($0.005 at the default 2048 MB) and one `video` event, no speech charge.

##### Newest videos of creators, captions only

```json
{
  "profiles": ["@apifytech"],
  "maxVideosPerProfile": 20,
  "fallbackToSpeechToText": false,
  "outputFormat": "flat"
}
```

Expected behavior: takes the 20 newest videos and uses only TikTok captions.
Expected output: 20 rows; videos without captions have `errorCode` `NO_CAPTIONS`.
Limit or cost note: only `video` events and a $0.0025 start fee, because the run uses 1 GB of memory; it is fast.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### Output

#### Output fields

| Field | Meaning | Type | Can be empty? | Example / enum | Source / notes |
| --- | --- | --- | --- | --- | --- |
| `videoUrl` | Full link of the video | text | no | `https://www.tiktok.com/@apifytech/video/7398101551744552225` | Built from the author and video ID |
| `inputUrl` | The link as you gave it | text | no | `https://vm.tiktok.com/…` | Your input |
| `videoId` | TikTok video ID | text | when the link is invalid | `7398101551744552225` | |
| `status` | Result of this row | text | no | `complete`, `partial`, `not_found`, `error` | `partial` = video found, no or cut text |
| `source` | Where the text came from | text | no | `captions`, `whisper`, `none` | |
| `captionType` | Kind of text | text | yes | `tiktok-creator`, `tiktok-asr`, `tiktok-translation`, `whisper-base` | |
| `confidence` | How sure the text is, 0–1 | number | yes | `0.85` | Fixed for TikTok captions (creator 0.95, TikTok speech recognition 0.85, translation 0.75); measured for Whisper |
| `language` | Language of the text | text | yes | `en` | |
| `availableCaptionLanguages` | Caption languages TikTok has | list | can be `[]` | `["en", "es"]` | |
| `transcript` | Full text | text | yes | `Are you doing research in HR or …` | |
| `segments` | Timed lines | list of `{start, end, text}` | can be `[]` | `{"start": 0.02, "end": 1.42, "text": "Are you doing research in HR"}` | Seconds |
| `chunks` | AI-ready pieces | list of `{index, start, end, text}` | can be `[]` | | Size from `chunkCharacters` |
| `wordCount` | Words in the transcript | number | 0 when no text | `96` | |
| `noTranscriptReason` | Why there is no or only part of the text | text | yes | `The video uses only a sound from the TikTok library …` | Plain English |
| `errorCode` | Short code of the reason | text | yes | see Troubleshooting | |
| `description`, `hashtags` | Video caption text and hashtags | text, list | yes | `#webscraping` | |
| `createTimeISO`, `durationSeconds` | Upload time (UTC) and length | text, number | yes | `2024-07-31T…Z`, `23` | |
| `isPhotoPost` | Photo post (slideshow) | yes/no | yes | `false` | |
| `author` | `username`, `nickname`, `id`, `verified`, `followerCount` | group | yes | `apifytech` | Flat: `authorUsername` … |
| `stats` | `playCount`, `diggCount` (likes), `commentCount`, `shareCount`, `collectCount` (saves) | group | yes | `783` | Flat: `playCount` … |
| `music` | Sound `title`, `author`, `isOriginal` | group | yes | `original sound - Apify` | Flat: `musicTitle` … |
| `files` | Links to `srt`, `vtt`, `txt` files | group | yes | | Only with `saveSubtitleFiles` |
| `speechSecondsCharged` | Seconds of audio charged for speech-to-text | number | 0 | `60` | Rounded up to 10 s |
| `profile` | Profile this video came from | text | yes | `apifytech` | |
| `fetchedAt` | When the row was made (UTC) | text | yes | `2026-11-01T07:40:07Z` | |

#### Output entities and surfaces

| Situation | Dataset/API/KVS/live view | Result shape | User action |
| --- | --- | --- | --- |
| Normal item | Dataset, views **Overview** and **Transcripts with timestamps** | `status` `complete`, text and metadata | Open, export or use `transcript` / `segments` |
| Video has no speech | Dataset | `status` `partial`, metadata, `errorCode` + `noTranscriptReason` | Read the reason; nothing to retry |
| Deleted or private video, bad link | Dataset | `status` `not_found` or `error`, `errorCode` | Check the link |
| Technical failure or retry | Dataset + `RUN_SUMMARY` in the key-value store | `errorCode` `BLOCKED` / `SKIPPED_BLOCKED`; summary `reason` `SOURCE_ERROR` | Run again later with the same input |
| Subtitle files | Key-value store, `transcript-<videoId>.srt` / `.vtt` / `.txt` | Files, links in `files` | Download or link |

#### Output example

```json
{
  "videoUrl": "https://www.tiktok.com/@apifytech/video/7698373675267001622",
  "videoId": "7698373675267001622",
  "status": "complete",
  "source": "captions",
  "captionType": "tiktok-asr",
  "confidence": 0.85,
  "language": "en",
  "transcript": "If I just makes it super easy to unlock that, because going to try and get a Google Maps API …",
  "segments": [
    {"start": 0.1, "end": 2.78, "text": "If I just makes it super easy to unlock that,"},
    {"start": 2.781, "end": 3.581, "text": "because"}
  ],
  "wordCount": 155,
  "noTranscriptReason": null,
  "errorCode": null,
  "hashtags": ["apify", "aiagents", "vibecoding", "mcp"],
  "createTimeISO": "2026-10-21T14:00:00Z",
  "durationSeconds": 57,
  "author": {"username": "apifytech", "nickname": "Apify", "verified": false, "followerCount": 3589},
  "stats": {"playCount": 783, "diggCount": 5, "commentCount": 0, "shareCount": 1, "collectCount": 4},
  "music": {"title": "original sound - Apify", "author": "Apify", "isOriginal": true},
  "files": null,
  "speechSecondsCharged": 0
}
```

This example is trimmed: it shows 2 of the segments and leaves out `chunks` and a few fields. `null` means the value does not apply (no error, no files asked). Text from TikTok's speech recognition or from Whisper can have small mistakes, like "If I" in place of "Apify" above.

#### Send results to Slack or Notion

Pick your connector in the optional `deliveryConnector` field. After the run saves its data, the Actor posts a short summary (up to 25 rows and a link to the full dataset) there. Put a Notion page link or a Slack channel ID in `deliveryTarget`: in Notion every run is added on top of that one page, so your workspace does not fill up with new pages. A delivery error never fails the run and costs nothing extra. Connect a service once in Apify Console → MCP connectors.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### Pricing

#### Pricing unit

You pay per event. Apify platform usage (compute, proxy, storage) is included: the developer pays it.

| Event | When it is charged | FREE | BRONZE | SILVER | GOLD and higher |
| --- | --- | ---: | ---: | ---: | ---: |
| `apify-actor-start` | Once per run, one event per GB of run memory (minimum one) | $0.0025 | $0.0025 | $0.0025 | $0.0025 |
| `video` | A video was found and its row has metadata, with or without text | $0.0009 | $0.00081 | $0.00072 | $0.00063 |
| `speech-10-seconds` | Each started 10 seconds of audio sent to speech-to-text | $0.002 | $0.0018 | $0.0016 | $0.0014 |
| `video-checked` | TikTok was checked but there is no paid row: a video not found, a profile not found, private or without videos, a video skipped by `skipProcessed` after TikTok listed it | $0.0001 | $0.0001 | $0.0001 | $0.0001 |

- The start fee grows with memory: $0.005 at the default 2048 MB, $0.0025 at 1024 MB (the Actor picks 1024 MB itself when `fallbackToSpeechToText` is off and `translateToEnglish` is off), $0.01 if you raise memory to 4096 MB. It covers starting the run, so a run that finds nothing is not free.
- Videos with TikTok captions cost the `video` event: $0.90 per 1,000 videos on the FREE plan, plus one start fee per run. Put many videos in one run to share it.
- Speech-to-text is charged for the audio it processed, also when the result is `NO_SPEECH` (music or noise). One minute of audio is 6 units: $0.012 on FREE.
- A found video without text (photo post, library sound only, silent video, `NO_CAPTIONS`, `TOO_LONG`) is charged as one `video` event, because you get its metadata and the reason. Speech-to-text is not started for these, so there is no speech charge.
- A deleted or private video, a profile that is not found, private or empty, and a profile video skipped by `skipProcessed` cost one `video-checked` event ($0.0001): TikTok still has to be asked. That is $0.10 per 1,000.
- Invalid links, blocked requests (`SKIPPED_BLOCKED`) and links skipped by `skipProcessed` before any request cost nothing.
- A run where nothing is found costs only the start fee ($0.005 at 2048 MB). A run where every link is invalid ends as Failed and also pays the start fee, because the run was started.
- If TikTok refuses 3 videos in a row on every route, or stops answering for over 45 seconds, the Actor stops sending requests: the remaining rows are `SKIPPED_BLOCKED` and cost nothing, so such a run costs about the start fee.
- If you set a maximum cost per run, the Actor stops before it. The last speech-to-text text may then be cut (`TRUNCATED`).

#### Cost examples

Measured runs on the FREE plan prices:

| Scenario | Input scope | Expected item/event count | Base charge | Optional charge | Stop/cap behavior |
| --- | --- | ---: | ---: | ---: | --- |
| One video with captions (prefill) | 1 link, 2048 MB | 2 `apify-actor-start`, 1 `video` | $0.0059 | none | — |
| All links invalid | 1 invalid link, 2048 MB | 2 `apify-actor-start` | $0.005 | none | run ends Failed |
| 1,000 videos that are not found | 1,000 links, 2048 MB | 2 `apify-actor-start`, 1,000 `video-checked` | $0.105 | none | took 140 s |
| 1,000 videos, captions only | 20 profiles × 50 videos, `fallbackToSpeechToText: false`, 1024 MB | 1 `apify-actor-start`, 1,000 `video` | $0.9025 | none | took 133 s |
| 60 newest videos with speech-to-text | 3 profiles × 20 videos, 2048 MB | 2 `apify-actor-start`, 60 `video`, 154 `speech-10-seconds` | $0.059 | $0.308 | took 533 s |
| A 1-minute video without captions | 1 link, 2048 MB | 2 `apify-actor-start`, 1 `video`, 6 `speech-10-seconds` | $0.0059 | $0.012 | — |

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### Limits

- Only public videos and public profiles. Private accounts and private videos return `PRIVATE_ACCOUNT` or `not_found`. No login is used, so the Actor cannot see what only followers see.
- Up to 1,000 links and 50 profiles (up to 500 newest videos each) per run, but at most 1,000 videos in total per run (links and profile videos together). Profiles after that limit get a row with `RUN_VIDEO_LIMIT`; a profile not reached before the run timeout gets `RUN_TIME_LIMIT`. Split bigger jobs into several runs.
- Speech-to-text works on one video at a time: about 1 hour of audio takes about 22 minutes of run time. The default run timeout is 1 hour, which fits about 2.5 hours of audio. For more, raise the timeout or split the input.
- Speech-to-text uses the Whisper base model. It is good for clear speech; songs, strong accents and noise give weaker text (look at `confidence`).
- A video with only a sound from the TikTok library (a song, a popular sound) has no original speech: it gets `NO_ORIGINAL_AUDIO` and no text.
- Short links (`vm.tiktok.com/…`, `tiktok.com/t/…`) are supported; a short link that no longer leads to a video gets `NOT_FOUND`. We checked the expired case live; a working short link was checked only in tests.
- The output fields are our own. They are not the same as other TikTok transcript Actors, so a switch may need small changes in your code.
- Comments, video files and downloads are not part of the output. The video is not stored.
- TikTok can change its pages or refuse requests. The Actor retries through Apify Proxy; rows it still cannot get are marked `SKIPPED_BLOCKED` and cost nothing.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### API, export and integrations

- **API:** start a run with the same JSON as the input form and read the dataset items. See the [Apify API docs](https://docs.apify.com/api/v2).
- **Export:** download the dataset as JSON, CSV, Excel, XML or HTML. Use `outputFormat: "flat"` for tables.
- **Scheduling:** run on a schedule with `profiles` and `skipProcessed: true` to get only new videos each time.
- **Webhooks / no-code:** use [Apify integrations](https://docs.apify.com/platform/integrations) (Make, Zapier, n8n, webhooks) to send each new row on; the next system reads `transcript` and `videoUrl`.
- **Subtitles:** with `saveSubtitleFiles: true` the SRT, VTT and TXT links are in `files`.

#### Use with AI agents (MCP)

- MCP server URL with only this Actor: `https://mcp.apify.com?tools=cliqtomedia/tiktok-transcript`.

- Smallest useful input:

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

- Key output fields: `transcript` (full text), `segments` (timed lines), `chunks` (pieces with times for long context), `status` and `noTranscriptReason` (why a video has no text), `videoUrl`.

- One video with captions takes a few seconds and costs $0.0059 on FREE ($0.005 start fee at 2048 MB + $0.0009 per video). Videos without captions take about 3 seconds of run time per 10 seconds of audio and add $0.002 per 10 seconds.

- Limits: up to 1,000 videos per run, public videos only, speech-to-text up to `maxSpeechMinutesPerVideo` (10 min by default) per video.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### Troubleshooting

#### A video has no transcript

Symptom: `status` is `partial` and `transcript` is empty.
Likely reason: see `errorCode` and `noTranscriptReason`:

| `errorCode` | Meaning |
| --- | --- |
| `NO_CAPTIONS` | TikTok has no captions and `fallbackToSpeechToText` is off |
| `NO_ORIGINAL_AUDIO` | Only a TikTok library sound, no original speech |
| `NO_SPEECH` | Silent video, or only music or noise |
| `NO_AUDIO` | The video file has no sound |
| `PHOTO_POST` | Photo slideshow, no spoken audio |
| `TOO_LONG` | Longer than `maxSpeechMinutesPerVideo` |
| `CHARGE_LIMIT` | Your maximum cost per run left no room for speech-to-text |
| `RUN_TIME_LIMIT` | The run reached its timeout first |
| `ABORTED` | You aborted the run first |
| `TRUNCATED` | Speech-to-text stopped at your maximum cost per run; the text is cut |
| `DOWNLOAD_ERROR` | The video file did not download (larger than 300 MB, empty or a download error) |
| `LOGIN_REQUIRED` | TikTok gives this video's file only after login |
| `NETWORK_ERROR`, `BLOCKED` | TikTok did not answer or refused the request for the video file |

Check: the reason text. Action: turn on `fallbackToSpeechToText`, raise `maxSpeechMinutesPerVideo`, the run timeout or the maximum cost, as the reason says. For download, login or network errors, run the video again later; these rows cost one `video` event and no speech charge.

#### A link returns not_found or error

Symptom: `status` is `not_found` (`NOT_FOUND`, `PRIVATE_ACCOUNT`, `NO_VIDEOS`) or `error` (`INVALID_URL`, `RUN_VIDEO_LIMIT`).
Likely reason: the video was deleted, the account is private, the text is not a TikTok link, or the run already took 1,000 videos.
Check: open the link in a private browser window.
Action: fix or remove the link, or move extra profiles to another run. `not_found` rows cost one `video-checked` ($0.0001); `error` rows cost nothing. If every link is invalid, the run ends as Failed and pays only the start fee.

#### The run ends with SOURCE_ERROR

Symptom: some rows have `SKIPPED_BLOCKED` and `RUN_SUMMARY` shows `reason` `SOURCE_ERROR`.
Likely reason: TikTok refused requests even through Apify Proxy, refused 3 videos in a row, or did not answer for over 45 seconds (`NETWORK_ERROR`, then `SKIPPED_BLOCKED`).
Check: the rows that are `complete` are saved and charged as normal.
Action: run again later with the same input; use `skipProcessed` to avoid the videos you already have.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### FAQ

#### Do I need a TikTok account or cookies?

No. The Actor reads only public pages, without login, cookies or your own proxy.

#### Why is speech-to-text charged when the result is NO_SPEECH?

Speech-to-text has to process the whole audio to learn that it is only music or noise. That work is charged per 10 seconds. Videos that clearly have no speech (library sound only, silent, photo posts) are not sent to speech-to-text.

#### Is the transcript complete and correct?

It is as good as TikTok's captions or the Whisper base model. TikTok's own speech recognition and Whisper can mishear names and words. `confidence` and `captionType` show where the text came from.

#### Can I get the text in another language?

Set `language` to use TikTok's captions in that language when TikTok has them, or `translateToEnglish` to get English for every video.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### Related Actors

- [YouTube Transcript Scraper: Subtitles & Timed Text](https://apify.com/cliqtomedia/youtube-transcript-scraper) — the same kind of timed text for YouTube videos.
- [Audio & Podcast Transcriber (Speech to Text)](https://apify.com/cliqtomedia/audio-video-transcriber) — speech to text for audio and video files from other sources.
- [Social Video Downloader: TikTok, Instagram, X - No Login](https://apify.com/cliqtomedia/social-video-downloader) — when you need the TikTok video file itself.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

### Support

For a bug, question or feature request, open the Actor's **Issues** tab on Apify. Include the run ID, the input links that failed and the `errorCode`. Never send tokens, cookies or private data.

> **👉 [Try it now: open the input form](https://console.apify.com/actors/J2bHBDDa5QIBjM4kb/input)**

# Changelog

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

# Actor input Schema

## `videoUrls` (type: `array`):

Up to 1,000 public TikTok links: video links (tiktok.com/@user/video/…), short links (vm.tiktok.com/…, tiktok.com/t/…), photo posts, video IDs, or profile links (@user — then the newest videos are taken). Duplicates are processed once.

## `profiles` (type: `array`):

Up to 50 usernames (@name) or profile links. The Actor takes the newest videos of each public profile, up to the number below.

## `maxVideosPerProfile` (type: `integer`):

How many of the newest videos to take from each profile. One run takes at most 1,000 videos in total (links and profile videos together); profiles after that get a RUN_VIDEO_LIMIT row.

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

Two-letter code such as en, es, de. When TikTok has captions in this language (including TikTok's own translations), they are used; otherwise the video's original language. Empty = original language.

## `fallbackToSpeechToText` (type: `boolean`):

When TikTok has no captions for a video, transcribe its audio with Whisper (charged per 10 seconds of audio). Videos that use only a library sound (no original audio) are not transcribed.

## `translateToEnglish` (type: `boolean`):

Return English text: TikTok's English captions when they exist, otherwise Whisper translates the speech into English (charged like speech-to-text).

## `maxSpeechMinutesPerVideo` (type: `integer`):

Longer videos without captions are returned without text (and without the speech charge).

## `chunkCharacters` (type: `integer`):

The transcript is also split into chunks of about this many characters with start and end times, ready for RAG and LLM tools. 0 = no chunks.

## `saveSubtitleFiles` (type: `boolean`):

Save subtitle files of each transcript in the run's key-value store; links are in the files field.

## `outputFormat` (type: `string`):

Nested keeps author, stats and music as groups; flat turns them into columns such as authorUsername and playCount.

## `skipProcessed` (type: `boolean`):

For scheduled runs: videos returned by earlier runs with the same state name are skipped: no full row, only a $0.0001 check when TikTok listed them.

## `stateName` (type: `string`):

Name of the key-value store that remembers processed videos. Use different names for different monitoring tasks.

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

Not needed: by default the Actor connects directly and switches to Apify Proxy by itself if TikTok refuses. Set your own proxy only if you need a specific one.

## `deliveryConnector` (type: `string`):

Your Slack, Notion or other MCP connector. After the run the Actor posts a short summary there. Connect services once in Apify Console → MCP connectors.

## `deliveryTarget` (type: `string`):

Where to post the summary: a Notion page link (each run is added on top of that page) or a Slack channel ID. Leave empty to let the connector decide.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.tiktok.com/@apifytech/video/7398101551744552225"
  ],
  "maxVideosPerProfile": 10,
  "language": "",
  "fallbackToSpeechToText": true,
  "translateToEnglish": false,
  "maxSpeechMinutesPerVideo": 10,
  "chunkCharacters": 1000,
  "saveSubtitleFiles": false,
  "outputFormat": "nested",
  "skipProcessed": false,
  "stateName": "tiktok-transcript-state"
}
```

# Actor output Schema

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

One row per video: transcript, timestamps, source, metadata.

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

Counts, charged events, reasons without text.

# 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 = {
    "videoUrls": [
        "https://www.tiktok.com/@apifytech/video/7398101551744552225"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/tiktok-transcript").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 = { "videoUrls": ["https://www.tiktok.com/@apifytech/video/7398101551744552225"] }

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/tiktok-transcript").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 '{
  "videoUrls": [
    "https://www.tiktok.com/@apifytech/video/7398101551744552225"
  ]
}' |
apify call cliqtomedia/tiktok-transcript --silent --output-dataset

```

## MCP server setup

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

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/J2bHBDDa5QIBjM4kb/builds/sHPueRKNhjzp5aJ1O/openapi.json
