Comment obtenir des transcriptions YouTube avec Python (sans quotas d'API)

La YouTube Data API n'inclut pas les transcriptions, youtube-transcript-api est bloquée par IP sur le cloud. Solution à 1 crédit par vidéo, août 2026.

Vous voulez le texte parlé d’une vidéo YouTube : pour un jeu de données, pour la recherche, pour des sous-titres. Cela s’avère plus difficile que ça ne devrait l’être, pour deux raisons qui n’ont rien à voir avec votre code.

Pourquoi la YouTube Data API ne vous donne-t-elle pas simplement ça ?

La YouTube Data API officielle a une ressource captions, mais captions.download nécessite une authentification OAuth en tant que propriétaire de la vidéo. Elle a été conçue pour que les propriétaires de chaîne gèrent leurs propres pistes de sous-titres, pas pour que des tiers récupèrent des transcriptions de vidéos qu’ils ne possèdent pas. Pour tous les autres, il n’existe aucun moyen officiel, mesuré par quota, de récupérer une transcription via la Data API. Cette lacune explique pourquoi toute une catégorie de bibliothèques non officielles existe.

Pourquoi youtube-transcript-api casse-t-elle sur un serveur ?

youtube-transcript-api fonctionne en parlant à un endpoint YouTube interne et non officiel, et elle tourne bien sur un ordinateur portable. Déployez le même code sur AWS, GCP ou Azure et elle se met à lever des erreurs RequestBlocked ou IpBlocked, parce que YouTube bloque les plages d’IP connues pour appartenir à des fournisseurs cloud. Ce n’est pas un bug de la bibliothèque : c’est un comportement documenté, évoqué dans les propres issues GitHub du projet comme un problème persistant jusqu’en 2026 (issue #593, issue #511). Les proxies statiques se font bannir après un usage modéré ; seuls les proxies résidentiels rotatifs tiennent, et faire tourner cette infrastructure vous-même est un projet à part entière si les transcriptions ne sont pas votre produit.

Voici donc l’état honnête des choses : l’API officielle ne fait pas ça, et la bibliothèque gratuite qui comble le vide ne survit pas au contact d’un serveur de production.

La version qui fonctionne

Envoyez l’ID de la vidéo à une API qui gère déjà le problème d’IP, et récupérez la transcription en JSON en retour :

import requests

API_KEY = "faf_live_..."  # votre clé 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])

Une requête coûte 1 crédit, quelle que soit la longueur de la vidéo. v est l’ID de la vidéo YouTube (la partie après v= dans l’URL), language demande une piste de sous-titres spécifique quand la vidéo en a plus d’une, et country limite la requête à une région quand la disponibilité ou les sous-titres par défaut d’une vidéo diffèrent selon le marché.

Que pouvez-vous construire avec ça ?

Jeux de données pour le fine-tuning de LLM ou le RAG. Les transcriptions sont du langage naturel propre et horodaté, pour une fraction du coût d’une transcription manuelle. Récupérez une fois le catalogue arrière d’une chaîne, stockez le texte, découpez-le comme votre pipeline l’attend.

Sous-titres pour votre propre pipeline vidéo. Si vous republiez, découpez ou doublez du contenu vidéo, une transcription horodatée est le point de départ pour des sous-titres incrustés ou une passe de traduction, sans demander à un humain de transcrire depuis l’audio.

Rechercher à l’intérieur du contenu vidéo. Démos produit, podcasts, séries de cours : les transcriptions permettent de grep la vidéo comme on grep du texte. Indexez la transcription, et « quel épisode a mentionné X » devient une requête de recherche au lieu d’une session de défilement.

Un job de traitement par lot minimal pour le premier cas :

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"]  # votre liste

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 vidéos traitées de cette façon, c’est 1 000 crédits : l’offre gratuite (500 crédits, sans carte) couvre les 500 premières pour un test, et tout ce qui est plus gros est une erreur d’arrondi sur le pack Starter.

En bref

L’API officielle n’a pas été conçue pour ça et la bibliothèque gratuite casse dès qu’elle quitte votre ordinateur portable. Récupérer par ID de vidéo via une requête HTTPS normale contourne les deux problèmes, coûte 1 crédit par vidéo quelle que soit sa longueur, et vous donne du texte horodaté que vous pouvez déposer directement dans un jeu de données, une piste de sous-titres ou un index de recherche. Voir la documentation pour la forme complète de la réponse.