Come ottenere trascrizioni YouTube con Python (senza quote API)

La YouTube Data API salta le trascrizioni, e youtube-transcript-api viene bloccata per IP sui server cloud. Una configurazione che costa 1 credito a video, agosto 2026.

Vuoi il testo parlato di un video YouTube: per un dataset, per la ricerca, per i sottotitoli. Si scopre che è più difficile di quanto dovrebbe essere, per due motivi che non c’entrano niente col tuo codice.

Perché la YouTube Data API non te lo dà e basta?

La YouTube Data API ufficiale ha una risorsa captions, ma captions.download richiede autenticazione OAuth come proprietario del video stesso. È stata costruita perché i proprietari di canali possano gestire le proprie tracce sottotitoli, non perché terze parti possano recuperare trascrizioni di video che non possiedono. Per chiunque altro, non c’è un modo ufficiale e a quota misurata per estrarre una trascrizione tramite la Data API. Questo vuoto è il motivo per cui esiste un’intera categoria di librerie non ufficiali.

Perché youtube-transcript-api si rompe su un server?

youtube-transcript-api funziona parlando con un endpoint YouTube interno e non ufficiale, e funziona bene su un laptop. Distribuisci lo stesso codice su AWS, GCP o Azure e comincia a lanciare errori RequestBlocked o IpBlocked, perché YouTube blocca le fasce IP note per appartenere a provider cloud. Non è un bug della libreria: è comportamento documentato, discusso nelle issue GitHub del progetto stesso come problema in corso per tutto il 2026 (issue #593, issue #511). I proxy statici vengono bannati dopo un uso moderato; solo i proxy residenziali rotanti reggono, e gestire quell’infrastruttura da solo è un progetto a sé se le trascrizioni non sono il tuo prodotto.

Quindi lo stato onesto delle cose: l’API ufficiale non lo fa, e la libreria gratuita che riempie il vuoto non sopravvive al contatto con un server di produzione.

La versione che funziona

Manda l’ID del video a un’API che gestisce già il problema IP, e ricevi indietro la trascrizione come JSON:

import requests

API_KEY = "faf_live_..."  # la tua chiave FastAF

resp = requests.get(
    "https://api.fastaf.co/v1/youtube",
    params={"v": "dQw4w9WgXcQ", "language": "en"},
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=60,
)
resp.raise_for_status()
data = resp.json()

full_text = " ".join(segment["text"] for segment in data["transcripts"])
print(full_text[:500])

Una richiesta costa 1 credito, indipendentemente dalla durata del video. v è l’ID video di YouTube (la parte dopo v= nell’URL), language richiede una traccia sottotitoli specifica quando il video ne ha più di una, e country limita la richiesta a una regione quando la disponibilità di un video o i sottotitoli di default cambiano per mercato.

Cosa puoi costruire con questo?

Dataset per fine-tuning LLM o RAG. Le trascrizioni sono linguaggio naturale pulito e con timestamp a una frazione del costo di una trascrizione manuale. Estrai una volta il catalogo di un canale, salva il testo, dividilo in chunk come si aspetta la tua pipeline.

Sottotitoli per la tua pipeline video. Se ripubblichi, tagli o doppi contenuti video, una trascrizione con timing è il punto di partenza per sottotitoli incorporati o una traduzione, senza chiedere a un umano di trascrivere dall’audio.

Cercare dentro i contenuti video. Demo di prodotto, podcast, serie di lezioni: le trascrizioni ti permettono di fare grep sul video come fai grep sul testo. Indicizza la trascrizione, e “in quale episodio si parlava di X” diventa una query di ricerca invece che una sessione di scrubbing.

Un job batch minimale per il primo caso:

import sqlite3
import time

conn = sqlite3.connect("transcripts.db")
conn.execute("""
    CREATE TABLE IF NOT EXISTS transcripts (
        video_id TEXT PRIMARY KEY,
        text TEXT,
        fetched_at INTEGER
    )
""")

def fetch_transcript(video_id, language="en"):
    resp = requests.get(
        "https://api.fastaf.co/v1/youtube",
        params={"v": video_id, "language": language},
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=60,
    )
    resp.raise_for_status()
    return " ".join(seg["text"] for seg in resp.json()["transcripts"])

video_ids = ["dQw4w9WgXcQ", "9bZkp7q19f0"]  # la tua lista

for vid in video_ids:
    text = fetch_transcript(vid)
    conn.execute(
        "INSERT OR REPLACE INTO transcripts VALUES (?, ?, ?)",
        (vid, text, int(time.time())),
    )
    conn.commit()

Mille video processati così sono 1.000 crediti: il piano gratuito (500 crediti, senza carta) copre i primi 500 per un test, e qualcosa di più grande è un arrotondamento sul pacchetto Starter.

La versione breve

L’API ufficiale non è stata costruita per questo e la libreria gratuita si rompe nel momento in cui lascia il tuo laptop. Recuperare per ID video con una normale richiesta HTTPS aggira entrambi i problemi, costa 1 credito a video indipendentemente dalla durata, e ti dà testo con timestamp che puoi mettere direttamente in un dataset, una traccia sottotitoli o un indice di ricerca. Vedi la documentazione per la forma completa della risposta.