HomeBlogTranscribePlansAPI Keys

Cargador de Transcripción de YouTube de LangChain que Sobrevive a Producción

Pipeline de LangChain convirtiendo un video de YouTube en objetos Document y una cadena de resumen

Cada tutorial de transcripción de YouTube de LangChain muestra las mismas tres líneas: YoutubeLoader.from_youtube_url(), carga, hecho. Luego lo implementas en un servidor en la nube y no devuelve nada silenciosamente. A continuación hay un cargador que devuelve objetos Document propios desde una API alojada — aproximadamente 20 líneas, más una cadena de resumen para probar que funciona de principio a fin.

Por qué YoutubeLoader muere en servidores

YoutubeLoader no llama a una API oficial. Raspa los puntos finales de subtítulos internos de YouTube desde cualquier máquina en la que se ejecute tu código. Desde una IP residencial que en su mayoría funciona. Desde una IP de AWS, GCP o Hetzner, se bloquea o entrega respuestas vacías, porque YouTube trata el tráfico del centro de datos como tráfico de bot. Escribí los detalles en por qué el raspado de transcripción de YouTube se rompe en IPs en la nube — la versión corta es que el fallo es ambiental, así que pasa cada prueba local y muere exactamente donde importa.

youtube2text.org hace la obtención en su lado y te entrega JSON, lo que convierte tu cargador en una simple llamada HTTP.

El cargador

import requests
from langchain_core.documents import Document

API_KEY = "yt_your_key_here"

def load_youtube_transcript(url: str, max_chars: int = 150000) -> Document:
    resp = requests.get(
        "https://youtube2text.org/api/transcribe",
        params={"url": url, "maxChars": max_chars},
        headers={"x-api-key": API_KEY},
        timeout=90,
    )
    resp.raise_for_status()
    r = resp.json()["result"]
    return Document(
        page_content=r["content"],
        metadata={
            "videoId": r["videoId"],
            "title": r["title"],
            "pubDate": r["pubDate"],
            "source": url,
        },
    )

La API devuelve videoId, title, pubDate, content, contentSize, y una bandera truncated. El texto de la transcripción va a page_content, todo lo demás a metadatos — exactamente la forma en que las cadenas posteriores y los almacenes de vectores esperan.

¿Sin clave aún? Agarra la clave demo compartida:

curl https://youtube2text.org/api/demo-key

Se limita a 5 videos por mes por IP, que es suficiente para probar el cargador.

Una cadena de resumen

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains.combine_documents import create_stuff_documents_chain

llm = ChatOpenAI(model="gpt-4o-mini")
prompt = ChatPromptTemplate.from_template(
    "Summarize this video transcript in five bullet points:\n\n{context}"
)
chain = create_stuff_documents_chain(llm, prompt)

doc = load_youtube_transcript("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(chain.invoke({"context": [doc]}))

create_stuff_documents_chain empaqueta toda la transcripción en un solo prompt, lo que funciona porque maxChars limita la carga a 150,000 caracteres por defecto. Para podcasts de una hora en modelos de contexto pequeño, pasa un maxChars más bajo al cargador o cambia a un patrón de mapeo-reducción.

Qué manejar en código real

Hay dos modos de fallo que vale la pena captar explícitamente. Un video con subtítulos desactivados devuelve HTTP 404 con código de error TRANSCRIPT_UNAVAILABLE — ningún reintento o cambio de biblioteca lo arregla, el texto simplemente no existe. Y RATE_LIMIT_EXCEEDED (429) incluye retryAfterSeconds en el JSON de error, así que léelo y retrocede en lugar de martillear:

if resp.status_code == 429:
    wait = resp.json()["error"].get("retryAfterSeconds", 60)

Una limitación honesta: content es texto plano sin marcas de tiempo. Está bien para resumir y recuperar, pero si necesitas enlaces profundos "salta a 12:34", tendrás que conformarte con atribución a nivel de video a través del videoId en metadatos.

A dónde ir desde aquí

Los mismos objetos Document caen directamente en un almacén de vectores — paso por la tubería completa en RAG sobre un canal completo de YouTube. Si tu stack es LlamaIndex en lugar de LangChain, la configuración equivalente es igualmente corta.

Cuando los 5 videos de la clave demo dejan de ser suficientes, obtén tu propia clave — nivel gratuito incluido, planes pagados desde $5.99 por 50 videos al mes.