TikTok shows auto-captions on many videos, but there's no button to export them and no official transcript API. Most "TikTok transcript API" guides end with signing up for a third-party key, and many of those tools only return text for videos that already have captions.
This tutorial covers three routes: a no-code run, a few lines of Python for bulk jobs, and a do-it-yourself script that pulls TikTok's own caption file with no key at all. I'll also show what happens with videos that have no captions, which in my test was more than half of them.
Disclosure: I built the Apify actor used in options 1 and 2 (TikTok Transcript Scraper). Option 3 doesn't use it.
Captions vs. no captions: what I found
I ran 24 random public TikToks (from #dance, #photomode and #asmr) through the actor on 26 Oct 2026:
- 10 had TikTok's own auto-generated caption track, which came back as is.
- 14 had no caption track. Of those, 3 contained speech and were transcribed with Whisper AI; the other 11 were music-only dances or silent ASMR, so there was nothing to transcribe.
A captions-only tool would have returned nothing for those 3 spoken videos. That's why the actor falls back to speech-to-text.
What you get per video
One row per video. Real values from a run on 26 Oct 2026:
| Field | Example |
|---|---|
url, videoId
|
https://www.tiktok.com/@renreports/video/7307726351291960622 |
authorUsername |
renreports |
createdAt |
2023-12-01T20:30:54Z |
playCount, likeCount, commentCount, shareCount, collectCount
|
66900, 2703, 54, 228, 2927
|
durationSeconds |
139 |
transcriptSource |
tiktok_captions (or ai_whisper when the video had none) |
language, isAutoGenerated, isTranslated
|
en, true, false
|
hook |
I work in influencer marketing and I look at media kids all day long. |
transcript |
[{"start": 0.08, "end": 1.28, "text": "I work in influencer marketing"}, …] |
wordCount |
512 |
hashtags, musicTitle, isOriginalSound
|
the post's hashtags and sound |
Note "media kids" in the hook: that's TikTok's own auto-caption (it means "media kits"). Captions are returned exactly as TikTok generated them.
Option 1: No code (Apify Console)
- Open the actor page and click Try for free. Apify's free plan includes monthly platform credit; the actor itself is pay-per-event (current price on the Store page), and videos without speech aren't charged.
- Paste links into TikTok videos: full URLs (
tiktok.com/@user/video/…), mobile links or bare numeric video IDs. - Pick the Transcript source:
-
auto: TikTok's captions when present, Whisper AI otherwise, -
captions: TikTok's captions only (cheapest; no AI), -
ai: always transcribe the audio, e.g. to translate everything to English.
-
- Click Start and export the results as CSV, JSON or Excel.
Option 2: Python
pip install apify-client, then take your API token from Apify Console → Settings → Integrations.
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("axlymxp/tiktok-transcript-scraper").call(run_input={
"videoUrls": [
"https://www.tiktok.com/@renreports/video/7307726351291960622",
"7477698081920748830", # a bare video ID works too
],
"transcriptSource": "auto",
"outputFormat": "both", # segments + one joined string
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if item["error"]: # no_speech, not_available, ...
print("skip", item["videoId"], item["error"])
continue
print(item["authorUsername"], item["playCount"], item["transcriptSource"])
print(" hook:", item["hook"])
print(" text:", item["transcriptText"][:200])
outputFormat accepts segments ([{start, end, text}]), text (one string) or both (segments in transcript, the string in transcriptText).
Rank hooks by plays
rows = [i for i in client.dataset(run["defaultDatasetId"]).iterate_items() if i["hasSpeech"]]
for i in sorted(rows, key=lambda i: i["playCount"] or 0, reverse=True):
print(f'{i["playCount"]:>10,} {i["hook"]}')
Example output
One real row (trimmed to three segments; description and coverUrl removed):
{
"videoId": "7307726351291960622",
"url": "https://www.tiktok.com/@renreports/video/7307726351291960622",
"authorUsername": "renreports",
"createdAt": "2023-12-01T20:30:54Z",
"durationSeconds": 139,
"playCount": 66900,
"likeCount": 2703,
"shareCount": 228,
"hashtags": ["influencermediakit", "influencermarketingtips", "contentcreatortips", "ugccreatortips2024", "socialmediamarketingtips"],
"transcriptSource": "tiktok_captions",
"language": "en",
"isAutoGenerated": true,
"isTranslated": false,
"hook": "I work in influencer marketing and I look at media kids all day long.",
"transcript": [
{"start": 0.08, "end": 1.28, "text": "I work in influencer marketing"},
{"start": 1.28, "end": 3.36, "text": "and I look at media kids all day long."},
{"start": 3.36, "end": 5.08, "text": "And here is one mistake that I see"}
],
"wordCount": 512,
"error": null
}
Option 3: DIY, TikTok's own captions with no API key
A public TikTok video page embeds its data as JSON in a <script id="__UNIVERSAL_DATA_FOR_REHYDRATION__"> tag, including links to the caption files. No signing, cookies or login are needed to read it. You need curl_cffi (pip install curl_cffi) so the request looks like a normal browser:
import json, re
from curl_cffi import requests
url = "https://www.tiktok.com/@renreports/video/7307726351291960622"
s = requests.Session(impersonate="chrome124")
html = s.get(url, headers={"Accept-Language": "en-US,en;q=0.9"}).text
m = re.search(r'<script id="__UNIVERSAL_DATA_FOR_REHYDRATION__"[^>]*>(.*?)</script>', html, re.S)
if not m:
raise SystemExit("No page data: TikTok served a challenge page to this IP")
item = json.loads(m.group(1))["__DEFAULT_SCOPE__"]["webapp.video-detail"]["itemInfo"]["itemStruct"]
tracks = (item["video"].get("claInfo") or {}).get("captionInfos") or []
for t in tracks:
print(t["languageCode"], "auto" if t["isAutoGen"] else "manual", "original" if t["isOriginalCaption"] else "translation")
if tracks:
vtt = s.get(tracks[0]["url"], headers={"Referer": "https://www.tiktok.com/"}).text
print(vtt[:300]) # WebVTT: "00:00:00.080 --> 00:00:01.280 / I work in influencer marketing" ...
Things I ran into while building this:
-
Some IPs get a challenge page instead of the data. From Apify's cloud servers (datacenter and residential) the page came back fine. From one AWS server in Singapore, TikTok returned a "SlardarWAF" challenge every time. If
misNone, try a different network. -
Use the same session for the page and the files. The video file URL (
video.playAddr) returned HTTP 403 when downloaded without the cookies from the page request. Keep one session for the page, the caption file and the media. -
"Original sound" isn't always this video's audio.
music.playUrlis a handy audio-only MP3 whenmusic.originalis true, but in one case an 8-second video pointed to a 46-second sound from another upload. Comparemusic.durationwithvideo.durationbefore using it. -
No captions? Run Whisper on the audio. Download the audio (
music.playUrlwhen it matches, otherwisevideo.playAddr) and transcribe it with faster-whisper. Expect small differences on mumbled clips: for one video TikTok's captions said "Bagels to make him dance" and Whisper heard "Beggles to make you dance".
Scheduling and integrations
Run the actor from a schedule, Make, Zapier or the API whenever you collect new TikTok links, and send the dataset to Google Sheets, Slack or a webhook. An interrupted run resumes where it stopped when started again with the same input.
FAQ
Do I need a TikTok account? No. Only public videos are read, logged out.
What about videos with music only? They come back with error: no_speech and aren't charged. Sung lyrics, on the other hand, are transcribed like speech.
Other languages? TikTok often has caption tracks in several languages; set your preferred order with Preferred caption languages. isTranslated: true marks a track that is TikTok's translation rather than the original language. Whisper detects ~100 spoken languages and can translate to English.
Can it take a username or hashtag? Not yet: it takes video links or IDs. Listing a creator's videos needs TikTok's signed web API, which a plain HTTP client can't produce.
Is it legal? These are public videos read without logging in. How you use the data is on you: respect TikTok's terms and privacy law (GDPR/CCPA).
Questions or a video that won't work? Open an issue on the actor page and I'll take a look.
Top comments (0)