← Blog

How to Get a TikTok Video Transcript in Python

Send a GET request to https://api.scrapersocial.com/v1/tiktok/transcript with the video URL and an API key in the Authorization header. The JSON response contains text (the full spoken track as one string), language, source, and segments. In Python that is about six lines with requests, and no TikTok developer account is involved.

What’s the fastest way to get a TikTok transcript in Python?

Get a key from the quickstart — signup gives you 100 trial credits with no card. Then:

import requests
API_KEY = "sk_live_..."
VIDEO_URL = "https://www.tiktok.com/@tiktok/video/7231338487075638570"
resp = requests.get(
"https://api.scrapersocial.com/v1/tiktok/transcript",
params={"url": VIDEO_URL},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=120,
)
resp.raise_for_status()
data = resp.json()["data"]
print(data["language"])
print(data["text"])

Output:

en
Today I want to show you the three settings everyone ignores when they set this up for the first time...

Short links from the share sheet work too. vm.tiktok.com and vt.tiktok.com URLs are resolved for you, so you can pass whatever the app produced without cleaning it up first.

One URL per call. There is no batch parameter.

What does the response actually look like?

Every endpoint returns the same envelope: a data object and a request_id.

{
"data": {
"text": "Today I want to show you the three settings everyone ignores when they set this up for the first time...",
"segments": null,
"language": "en",
"source": "upstream"
},
"request_id": "req_01JZX4M8Q2TE9W"
}

The four fields:

FieldTypeWhat it holds
textstringThe whole transcript as one continuous string, punctuation and casing as the caption track had them. Never empty on a 200.
segmentsobject[] | nullTimestamped chunks as { start, end, text }, seconds from the start. Null on TikTok today — see below.
languagestring | nullThe language code attached to the caption track. Null when the track carries no language tag.
sourcestringWhich leg produced the text. "upstream" means a platform caption track.

Log request_id. If something looks wrong, quoting it lets support trace the exact call.

Most TikToks run under a minute, so text is usually a few hundred words. That fits in a prompt, a search index, or a TEXT column without chunking.

Why is segments null in my TikTok transcript response?

Because there is no speech-to-text step. The endpoint reads the caption track TikTok already publishes for the video, and those tracks reach the pipeline as flat text with the per-cue timings already stripped out. So the field exists in the schema, it is typed as an array, and on TikTok it comes back null every time.

segments is populated by the audio-recognition leg of the pipeline, which is off in the current subtitles-only configuration. If your feature needs to jump to the 14-second mark, this endpoint will not get you there right now.

Write your code to handle None and nothing breaks if that changes:

segments = data.get("segments") or []
for seg in segments:
print(f"[{seg['start']:.1f}] {seg['text']}")

There is more on this in why your video transcript has no timestamps.

What happens when a TikTok video has no transcript?

You get a 404 with code not_found, not a blank string and not a guess. Captions-only means captions-or-nothing: if TikTok never generated a caption track for the video, there is nothing to read.

In practice TikTok auto-captions a large share of spoken-word video, so coverage is good. Music-led and wordless clips are where you should expect misses.

Errors are not charged. Credits come off the balance only when data comes back, so a 404 costs you a request and zero credits.

import time
import requests
BASE = "https://api.scrapersocial.com/v1"
H = {"Authorization": f"Bearer {API_KEY}"}
def get_transcript(video_url, attempts=3):
for attempt in range(attempts):
r = requests.get(f"{BASE}/tiktok/transcript",
params={"url": video_url},
headers=H, timeout=120)
if r.status_code == 429: # rate_limited
time.sleep(2 ** attempt)
continue
if r.status_code >= 500: # transient, safe to retry
time.sleep(2 ** attempt)
continue
if r.status_code in (404, 422): # no captions / not transcribable
return None
r.raise_for_status()
return r.json()["data"]
return None

The codes worth branching on:

The full table is in the errors reference.

Do repeat calls for the same video cost less?

They come back faster, not cheaper. Transcripts are in the immutable cache class: a published video’s spoken words do not change, so once it has been transcribed it is kept permanently and never re-fetched. Repeat calls are served from that stored corpus and return in milliseconds. fresh=true will not force a re-transcription of a video already held.

The billing side is the part people get wrong. A cache hit costs the same 8 credits as a fresh pull. Check the header to see which you got:

print(resp.headers["x-cache"]) # hit | miss
print(resp.headers["X-Credits-Remaining"]) # your balance after this call

So if you re-read the same videos in a loop, store text on your side after the first call. Paying to be handed back a string you already have is the most common way to waste credits here.

Cost, concretely: 8 credits per video. Credits are $0.005 each on the monthly plan and $0.0045 on annual, so one transcript is about 4 cents and $5 covers roughly 125 videos. The 100 trial credits are worth 12 transcripts.

How do I transcribe a lot of videos at once?

Send calls concurrently. Each request is independent and the API is built for parallel traffic. Rate limits are per account, in requests per minute: 20 on the free trial, 200 on monthly, 300 on annual.

from concurrent.futures import ThreadPoolExecutor
urls = [...] # video URLs
with ThreadPoolExecutor(max_workers=8) as pool:
results = list(pool.map(get_transcript, urls))
texts = {u: r["text"] for u, r in zip(urls, results) if r}
print(f"{len(texts)}/{len(urls)} transcribed")

Limits count requests, not credits. Keep your worker count under the per-minute ceiling for your plan and the retry helper above absorbs the occasional 429.

If you are starting from a handle rather than a list of URLs, the transcribe-a-channel recipe shows the enumerate-then-transcribe loop with the pagination and credit math worked out.

What should I know about transcript quality?

The text comes back in whatever language was spoken. There is no translation step and no cleanup pass. Auto-generated captions carry the usual artifacts: mis-heard proper nouns, no speaker labels, missing punctuation on fast delivery, and brand names spelled the way they sound.

That is fine for search, classification, and LLM input. If you are publishing the words verbatim, budget for a human edit.

Is there a way to test this without writing code?

Paste a URL into the free TikTok transcript generator. It runs in the browser, no signup for the first video, and it is the quickest way to check whether a specific video has a usable caption track before you wire anything up. It is rate-limited per visitor; the API is not.

Next step

If the shape above is what you needed, the TikTok Transcript API page has the field explorer and the full FAQ, and 100 trial credits are enough to run your real inputs through it before deciding anything.

Keep reading

Try it on your own URLs

100 trial credits on signup, no card. Enough to run a real batch before you decide.