Chargeur de transcription YouTube LangChain qui survit à la production

Chaque tutoriel de transcription YouTube LangChain montre les mêmes trois lignes : YoutubeLoader.from_youtube_url(), charger, c'est fait. Puis vous déployez dans une boîte cloud et ça retourne silencieusement rien. Ci-dessous se trouve un chargeur qui retourne les objets Document appropriés à partir d'une API hébergée à la place — environ 20 lignes, plus une chaîne de résumé pour prouver que ça fonctionne de bout en bout.
Pourquoi le YoutubeLoader intégré meurt sur les serveurs
YoutubeLoader n'appelle pas une API officielle. Il racle les points de terminaison de sous-titres internes de YouTube à partir de n'importe quelle machine sur laquelle votre code s'exécute. À partir d'une IP résidentielle cela fonctionne généralement. À partir d'une IP AWS, GCP ou Hetzner, ça obtient du blocage ou des réponses vides, parce que YouTube traite le trafic de centre de données comme le trafic de bot. J'ai écrit les détails dans pourquoi le grattage de transcription YouTube casse sur les IP cloud — la version courte est que l'échec est environnemental, donc il passe chaque test local et meurt exactement là où ça compte.
youtube2text.org fait la récupération de son côté et vous donne du JSON, ce qui transforme votre chargeur en un simple appel HTTP.
Le chargeur
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,
},
)
L'API retourne videoId, title, pubDate, content, contentSize, et un drapeau truncated. Le texte de transcription va dans page_content, tout le reste en métadonnées — exactement la forme que les chaînes en aval et les magasins de vecteurs attendent.
Pas encore de clé ? Attrapez la clé démo partagée :
curl https://youtube2text.org/api/demo-key
Elle est limitée à 5 vidéos par mois par IP, ce qui est assez pour tester le chargeur.
Une chaîne de résumé
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 insère la transcription entière dans un prompt, ce qui fonctionne parce que maxChars limite la charge utile à 150 000 caractères par défaut. Pour les podcasts d'une heure sur les petits modèles de contexte, passez un maxChars inférieur au chargeur ou basculez vers un motif map-reduce.
Ce qu'il faut gérer dans le code réel
Deux modes de défaillance valent la peine d'être capturés explicitement. Une vidéo avec sous-titres désactivés retourne HTTP 404 avec le code d'erreur TRANSCRIPT_UNAVAILABLE — aucune nouvelle tentative ou changement de bibliothèque ne répare cela, le texte n'existe simplement pas. Et RATE_LIMIT_EXCEEDED (429) inclut retryAfterSeconds dans le JSON d'erreur, donc lisez-le et reculez au lieu de frapper :
if resp.status_code == 429:
wait = resp.json()["error"].get("retryAfterSeconds", 60)
Une honnête limitation : content est du texte simple sans horodatages. C'est bien pour la résumé et la récupération, mais si vous avez besoin de liens profonds "sauter à 12:34", vous devrez vous contenter d'une attribution au niveau vidéo via le videoId en métadonnées.
Où aller à partir d'ici
Les mêmes objets Document se glissent directement dans un magasin de vecteurs — je vous guide dans le pipeline complet dans RAG sur un canal YouTube entier. Si votre pile est LlamaIndex au lieu de LangChain, la configuration équivalente est tout aussi courte.
Quand les 5 vidéos de la clé démo ne suffisent plus, obtenez votre propre clé — plan gratuit inclus, plans payants de $5.99 pour 50 vidéos par mois.