Python · requests

Get YouTube transcripts in Python

The shortest way to get a YouTube transcript in Python that also works on a server: one HTTP request, no proxies, no scraping code to maintain.

1. Get an API key

Enter your email on the sign-up form. The key arrives by email with 100 free credits. Keep it in an environment variable:

export TRANSKRIO_API_KEY="tk_live_..."

2. Get a transcript

import os
import requests

API = "https://api.transkrio.com/v1"
HEADERS = {"x-api-key": os.environ["TRANSKRIO_API_KEY"]}


def youtube_transcript(url: str, lang: str | None = None) -> dict:
    r = requests.get(f"{API}/transcript", params={"url": url, "lang": lang},
                     headers=HEADERS, timeout=60)
    body = r.json()
    if r.status_code != 200:
        raise RuntimeError(body.get("error", {}).get("message") or body.get("detail"))
    return body


t = youtube_transcript("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(t["language_code"], len(t["segments"]), "segments")
print(t["text"][:300])

Each segment has text, start and duration in seconds. text is the whole transcript joined, which is what you usually want to send to an LLM.

3. Plain text or subtitles

Ask for format=text, srt or vtt and the response body is the file itself:

srt = requests.get(f"{API}/transcript", headers=HEADERS, timeout=60,
                   params={"url": "dQw4w9WgXcQ", "format": "srt"}).text
open("video.srt", "w", encoding="utf-8").write(srt)

4. Handle errors

Errors come back as JSON with a stable error.code (or a detail message for 402 and 429), and none of them cost credits:

StatuscodeMeaning
400invalid_urlNot a YouTube, TikTok, Instagram or podcast URL
404no_transcriptThe video has no captions
404video_unavailablePrivate, deleted or unavailable video
403video_restrictedAge-restricted or region-blocked video
402-Out of credits
429-More than 60 requests a minute

5. A whole playlist or channel

def channel_transcripts(channel: str, limit: int = 50) -> list[dict]:
    videos = requests.get(f"{API}/videos", headers=HEADERS, timeout=60,
                          params={"url": channel, "limit": limit}).json()["videos"]
    urls = [v["url"] for v in videos]
    out = []
    for i in range(0, len(urls), 50):
        r = requests.post(f"{API}/batch", headers=HEADERS, timeout=300,
                          json={"urls": urls[i:i + 50], "format": "text"})
        out += [item for item in r.json()["results"] if item["ok"]]
    return out


for item in channel_transcripts("@veritasium", limit=10):
    print(item["video_id"], item["text"][:80])

TikTok, Reels and podcasts too

The same function works with TikTok links, Instagram Reels and podcast episodes. Those may need to be transcribed from the audio; a long episode can answer 202 with "status": "processing" the first time. Wait retry_after seconds and send the same request again:

import time

def transcript(url: str) -> dict:
    while True:
        r = requests.get(f"{API}/transcript", params={"url": url}, headers=HEADERS, timeout=90)
        if r.status_code == 202:
            time.sleep(r.json()["retry_after"])
            continue
        r.raise_for_status()
        return r.json()

See also: the same in Node.js, building RAG over YouTube videos, and why the free library gets RequestBlocked on servers.

Try it with 100 free credits No card needed. Failed requests are free and unused credits roll over.
Get free API key

FAQ

Do I need the youtube-transcript-api package?

No. You only need requests (or httpx). Transkrio fetches the transcript on its side, so there are no proxies to configure.

How do I get a transcript in a specific language?

Add "lang": "es" (or any ISO code) to the params.

How do I transcribe a whole channel?

Call /v1/videos with the channel URL or @handle to list its videos, then send the URLs to /v1/batch in groups of 50.