Como obter transcrições do YouTube com Python (sem cotas de API)
A YouTube Data API não cobre transcrições, e o youtube-transcript-api tem o IP bloqueado na nuvem. Uma configuração que custa 1 crédito por vídeo.

Você quer o texto falado de um vídeo do YouTube: para um dataset, para busca, para legendas. Isso acaba sendo mais difícil do que deveria, por dois motivos que não têm nada a ver com o seu código.
Por que a YouTube Data API simplesmente não te dá isso?
A YouTube Data API oficial tem um recurso captions, mas captions.download exige autenticação OAuth como o próprio dono do vídeo. Ela foi construída para que donos de canal gerenciem suas próprias faixas de legenda, não para que terceiros busquem transcrições de vídeos que não possuem. Para todo mundo mais, não existe uma forma oficial e medida por cota de puxar uma transcrição pela Data API. Essa lacuna é o motivo de existir uma categoria inteira de bibliotecas não oficiais.
Por que o youtube-transcript-api quebra em um servidor?
O youtube-transcript-api funciona conversando com um endpoint interno e não oficial do YouTube, e roda bem em um laptop. Faça o deploy do mesmo código na AWS, GCP ou Azure e ele começa a lançar erros RequestBlocked ou IpBlocked, porque o YouTube bloqueia as faixas de IP conhecidas por pertencerem a provedores de nuvem. Isso não é um bug da biblioteca: é comportamento documentado, discutido nas próprias issues do GitHub do projeto como um problema contínuo ao longo de 2026 (issue #593, issue #511). Proxies estáticos são banidos depois de uso moderado; só proxies residenciais rotativos aguentam, e manter essa infraestrutura você mesmo é um projeto à parte se transcrições não são o seu produto.
Então o estado real das coisas: a API oficial não faz isso, e a biblioteca gratuita que preenche a lacuna não sobrevive ao contato com um servidor de produção.
A versão que funciona
Mande o ID do vídeo para uma API que já lida com o problema de IP, e receba a transcrição de volta como JSON:
import requests
API_KEY = "faf_live_..." # sua chave 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])
Uma requisição custa 1 crédito, independente da duração do vídeo. v é o ID do vídeo no YouTube (a parte depois de v= na URL), language pede uma faixa de legenda específica quando o vídeo tem mais de uma, e country restringe a requisição a uma região quando a disponibilidade ou as legendas padrão de um vídeo variam por mercado.
O que dá para construir com isso?
Datasets para fine-tuning de LLM ou RAG. Transcrições são linguagem natural limpa e com timestamp por uma fração do custo de transcrição manual. Puxe o catálogo de um canal de uma vez, guarde o texto, faça o chunking do jeito que sua pipeline espera.
Legendas para a sua própria pipeline de vídeo. Se você republica, corta ou dubla conteúdo de vídeo, uma transcrição com timing é o ponto de partida para legendas embutidas ou uma passagem de tradução, sem pedir para um humano transcrever a partir do áudio.
Buscar dentro de conteúdo de vídeo. Demos de produto, podcasts, séries de aula: transcrições permitem fazer grep em vídeo do mesmo jeito que você faz grep em texto. Indexe a transcrição, e “qual episódio mencionou X” vira uma busca em vez de uma sessão de rebobinar.
Um job em lote mínimo para o primeiro 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"] # sua 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()
Mil vídeos processados desse jeito são 1.000 créditos: o plano gratuito (500 créditos, sem cartão) cobre os primeiros 500 para um teste, e qualquer coisa maior é um arredondamento no pacote Starter.
Resumindo
A API oficial não foi feita para isso e a biblioteca gratuita quebra assim que sai do seu laptop. Buscar por ID de vídeo com uma requisição HTTPS normal contorna os dois problemas, custa 1 crédito por vídeo independente da duração, e te dá texto com timestamp que você pode colocar direto em um dataset, uma faixa de legendas ou um índice de busca. Veja a documentação para o formato completo da resposta.