YouTube Transcript API i Python: 10 linjer med requests

youtube_transcript_api._errors.IpBlocked:
Could not retrieve a transcript for the video.
YouTube is blocking requests from your IP.
Hvis den stack trace bringer deg her, velkommen. Det er standard feilmodus for det populære pip-pakken fra det øyeblikket koden din forlater bærbare datamaskinen og lander på AWS, GCP eller en annen datasenter-IP. Denne posten viser den andre ruten: en hostet YouTube-transkripsjon-API du kaller fra Python med bare requests. På slutten får du et tilinjer-skript som gjør en YouTube-URL om til transkripsjon-tekst, pluss riktige grener for rate limits og videoer uten tekst.
Hvorfor pip-biblioteket dør i produksjon
youtube-transcript-api skrapers YouTube direkte fra hvilken maskin som helst som kjører det. Lokalt er det OK. Deploy det, og YouTubes bot-deteksjon flagner skyldprovideren din IP-område innen dager, noen ganger minutter. Jeg skrev de grusomme detaljene i hvorfor YouTube-transkripsjon-APIen blir blokkert — kort versjon: du ender opp med å leie boligproxyer for å lese offentlige undertekster, noe som er absurd.
youtube2text.org gjør YouTube-delen på server-siden. Koden din gjør ett HTTPS-kall og får JSON tilbake.
De ti linjene
Ingen konto er nødvendig for å prøve det — det er et demo-nøkkel-endepunkt:
import requests
API = "https://youtube2text.org/api/transcribe"
KEY = requests.get("https://youtube2text.org/api/demo-key").json()["apiKey"]
resp = requests.get(API, params={"url": "https://youtu.be/dQw4w9WgXcQ"},
headers={"x-api-key": KEY}, timeout=60)
resp.raise_for_status()
result = resp.json()["result"]
print(result["title"])
print(result["content"][:300])
url-parameteren godtar enhver YouTube-URL-form — watch?v=, youtu.be, shorts, embeds — eller en bare 11-tegn video-id. Responsen gir deg videoId, title, pubDate, content, contentSize og et truncated-flagg.
En advarsel om den demo-nøkkelen: den deles på tvers av alle og er begrenset til 5 videoer per måned per IP. Fin for å teste dekk, ubrukelig for noe som helst virkelig.
Håndtering av 429 og 404 som en voksen
raise_for_status() er OK for en demo, men feil kommer tilbake som strukturert JSON med en code du kan forgrene på:
import time
resp = requests.get(API, params={"url": video_url},
headers={"x-api-key": KEY}, timeout=60)
body = resp.json()
if resp.status_code == 200:
transcript = body["result"]["content"]
elif resp.status_code == 429:
time.sleep(body["error"]["retryAfterSeconds"]) # then retry
elif resp.status_code == 404:
if body["error"]["code"] == "TRANSCRIPT_UNAVAILABLE":
transcript = None # video exists, but has no captions — skip it
else:
raise ValueError(f"No such video: {video_url}") # VIDEO_NOT_FOUND
else:
raise RuntimeError(body["error"]["message"])
TRANSCRIPT_UNAVAILABLE fortjener et øyeblikk av ærlighet: transkripsjoner kommer fra YouTubes undertekstspor (manuell eller auto-generert), og noen videoer har rett og slett ikke en. Ingen API kan skape tekst som ikke er der — for disse ville du trenge faktisk tale-til-tekst, som jeg sammenlignet i Whisper vs YouTube-undertekster. Forsøk på nytt hjelper ikke, så gjør det ikke.
maxChars og LLM-token-budsjetter
Hvis transkripsjonen er på vei inn i en prompt, bryr du deg om størrelse. En tre-timers podcast-transkripsjon vil glatt spise 100k tokens. maxChars-parameteren begrensar utdataene — både standard og maksimum er 150000:
resp = requests.get(API,
params={"url": video_url, "maxChars": 20000},
headers={"x-api-key": KEY}, timeout=60)
Med den grove 4-tegn-per-token-regelen lander maxChars=20000 rundt 5k tokens. Sjekk result["truncated"] for å vite om du fikk det hele. Hvis du leder dette inn i en hentings-pipeline i stedet for en enkelt prompt, dekker LangChain-integrerings-posten riktig fragmentering.
Kommer vekk fra demo-nøkkelen
Når 5 delte videoer per måned ikke lenger er nok, logg inn med Google på youtube2text.org/app/keys og lag din egen nøkkel — ingen telefonnummer, intet kredittkort. Gratis nivå er 5 videoer per måned per nøkkel; betalte nivåer starter på $5.99 for 50. De ti linjene over endres ikke, bare strengen i headeren gjør det.