Cómo obtener transcripciones de YouTube con Python (sin cuotas de API)
La YouTube Data API no da transcripciones, y youtube-transcript-api se bloquea por IP en la nube. Solución que cuesta 1 crédito por vídeo, agosto 2026.

Quieres el texto hablado de un vídeo de YouTube: para un dataset, para una búsqueda, para subtítulos. Resulta que esto es más difícil de lo que debería, por dos motivos que no tienen nada que ver con tu código.
¿Por qué la YouTube Data API no te da esto directamente?
La YouTube Data API oficial tiene un recurso captions, pero captions.download requiere autenticación OAuth como el propio dueño del vídeo. Se construyó para que los dueños de canales gestionen sus propias pistas de subtítulos, no para que terceros obtengan transcripciones de vídeos que no les pertenecen. Para todos los demás, no hay una forma oficial, medida por cuota, de sacar una transcripción a través de la Data API. Ese hueco es la razón por la que existe toda una categoría de librerías no oficiales.
¿Por qué youtube-transcript-api se rompe en un servidor?
youtube-transcript-api funciona hablando con un endpoint interno y no oficial de YouTube, y va bien en un portátil. Despliega el mismo código en AWS, GCP o Azure y empieza a lanzar errores RequestBlocked o IpBlocked, porque YouTube bloquea los rangos de IP conocidos por pertenecer a proveedores cloud. Esto no es un bug de la librería: es un comportamiento documentado, discutido en los propios issues de GitHub del proyecto como un problema continuo durante 2026 (issue #593, issue #511). Los proxies estáticos se banean tras un uso moderado; solo los proxies residenciales rotativos aguantan, y mantener esa infraestructura tú mismo es un proyecto en sí mismo si las transcripciones no son tu producto.
Así que el panorama honesto es este: la API oficial no hace esto, y la librería gratuita que llena el hueco no sobrevive al contacto con un servidor de producción.
La versión que funciona
Envía el ID del vídeo a una API que ya resuelve el problema de la IP, y recibe la transcripción de vuelta como JSON:
import requests
API_KEY = "faf_live_..." # tu clave de 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 solicitud cuesta 1 crédito, sin importar la duración del vídeo. v es el ID de vídeo de YouTube (la parte después de v= en la URL), language solicita una pista de subtítulos específica cuando el vídeo tiene más de una, y country acota la solicitud a una región cuando la disponibilidad de un vídeo o sus subtítulos por defecto difieren según el mercado.
¿Qué puedes construir con esto?
Datasets para fine-tuning de LLM o RAG. Las transcripciones son lenguaje natural limpio y con marca de tiempo a una fracción del coste de una transcripción manual. Descarga el catálogo histórico de un canal una vez, guarda el texto, divídelo en chunks como espere tu pipeline.
Subtítulos para tu propio pipeline de vídeo. Si republicas, recortas o doblas contenido de vídeo, una transcripción con temporización es el punto de partida para subtítulos incrustados o una pasada de traducción, sin pedirle a un humano que transcriba desde el audio.
Buscar dentro del contenido de vídeo. Demos de producto, podcasts, series de clases: las transcripciones te permiten hacer grep de vídeo igual que haces grep de texto. Indexa la transcripción, y “qué episodio mencionó X” se convierte en una consulta de búsqueda en lugar de una sesión de scrubbing.
Un batch job mínimo para el primer 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"] # tu 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 procesados así son 1.000 créditos: el plan gratuito (500 créditos, sin tarjeta) cubre los primeros 500 para una prueba, y cualquier cosa más grande es un redondeo insignificante en el pack Starter.
En resumen
La API oficial no se construyó para esto y la librería gratuita se rompe en cuanto sale de tu portátil. Obtener el contenido por ID de vídeo mediante una solicitud HTTPS normal evita ambos problemas, cuesta 1 crédito por vídeo sin importar la duración, y te da texto con marca de tiempo que puedes meter directamente en un dataset, una pista de subtítulos o un índice de búsqueda. Consulta la documentación para la forma completa de la respuesta.