← Back to blog

Get YouTube Transcripts in 125+ Languages: A Guide

David Boulen · 7/1/2026 · 10 min read

Get YouTube Transcripts in 125+ Languages: A Guide

Get YouTube Transcripts in 125+ Languages: A Guide

If you're building a multilingual content tool, feeding transcripts into an LLM, or serving users across different locales, you need more than just English captions. YouTube hosts video in dozens of languages and offers auto-translated tracks for hundreds more — but actually getting those transcripts programmatically is more complicated than it looks.

This guide covers everything: how YouTube's caption system works under the hood, how to request transcripts in a specific language with one parameter, how fallback behavior works, and how to confirm which language you actually got back.

Developer working on multilingual API integration with multiple language flags on screen

How YouTube Handles Multilingual and Translated Captions

YouTube maintains several types of caption tracks per video, and understanding the difference matters when you're pulling transcripts programmatically.

Manually created captions are uploaded directly by the video creator. These are the most accurate and often the most complete — think professional channels, news outlets, and educational content. A creator might upload English and Spanish versions of the same transcript.

Auto-generated captions are YouTube's speech-to-text output. YouTube produces auto-captions for content in roughly 60+ languages including English, Spanish, Portuguese, French, German, Japanese, Korean, and Hindi. According to YouTube's own help documentation, auto-caption accuracy varies by audio quality and speaker clarity — the gap between auto-generated output and human-verified transcription can matter for accessibility workflows (the 3Play Media 2024 State of Captioning report found 90% of organizations caption at least some content, with accuracy standards ranging widely by use case).

Auto-translated tracks are YouTube's machine translation layer. Even if a video only has English captions, YouTube can serve translated versions in many additional languages. This is the mechanism that pushes the practical language coverage well beyond the ~60 auto-caption languages.

When you call the transcript API, you're navigating this three-tier system. A well-structured request will prefer manually created tracks when available, fall through to auto-generated if not, and ultimately land on auto-translated as a last resort.

Requesting a Transcript in a Specific Language

The /api/v2/transcript endpoint accepts a lang parameter that takes a BCP-47 language code. This is the same format YouTube itself uses for its caption tracks.

Basic pattern:

GET https://getyoutubetranscriber.com/api/v2/transcript
  ?video_url=<VIDEO_ID>
  &lang=<BCP-47-CODE>

curl

# French transcript
curl "https://getyoutubetranscriber.com/api/v2/transcript?video_url=dQw4w9WgXcQ&lang=fr" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Brazilian Portuguese
curl "https://getyoutubetranscriber.com/api/v2/transcript?video_url=dQw4w9WgXcQ&lang=pt-BR" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Simplified Chinese
curl "https://getyoutubetranscriber.com/api/v2/transcript?video_url=dQw4w9WgXcQ&lang=zh-Hans" \
  -H "Authorization: Bearer YOUR_API_KEY"

Python

import httpx

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://getyoutubetranscriber.com/api/v2/transcript"

def get_transcript(video_id: str, lang: str = None) -> dict:
    params = {"video_url": video_id}
    if lang:
        params["lang"] = lang

    response = httpx.get(
        BASE_URL,
        params=params,
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    response.raise_for_status()
    return response.json()

# Get Spanish transcript
data = get_transcript("dQw4w9WgXcQ", lang="es")
print(f"Language returned: {data['language']}")
for segment in data["transcript"][:3]:
    print(f"[{segment['start']:.1f}s] {segment['text']}")

Node.js

const API_KEY = "YOUR_API_KEY";

async function getTranscript(videoId, lang = null) {
  const url = new URL("https://getyoutubetranscriber.com/api/v2/transcript");
  url.searchParams.set("video_url", videoId);
  if (lang) url.searchParams.set("lang", lang);

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });

  if (!res.ok) throw new Error(`API error: ${res.status}`);
  return res.json();
}

// Get Japanese transcript
const data = await getTranscript("dQw4w9WgXcQ", "ja");
console.log(`Language returned: ${data.language}`);
data.transcript.slice(0, 3).forEach(({ start, text }) => {
  console.log(`[${start.toFixed(1)}s] ${text}`);
});

Go

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
)

const apiKey = "YOUR_API_KEY"
const baseURL = "https://getyoutubetranscriber.com/api/v2/transcript"

type TranscriptResponse struct {
    VideoID    string `json:"video_id"`
    Language   string `json:"language"`
    Transcript []struct {
        Text     string  `json:"text"`
        Start    float64 `json:"start"`
        Duration float64 `json:"duration"`
    } `json:"transcript"`
}

func getTranscript(videoID, lang string) (*TranscriptResponse, error) {
    params := url.Values{"video_url": {videoID}}
    if lang != "" {
        params.Set("lang", lang)
    }

    req, _ := http.NewRequest("GET", baseURL+"?"+params.Encode(), nil)
    req.Header.Set("Authorization", "Bearer "+apiKey)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var result TranscriptResponse
    json.NewDecoder(resp.Body).Decode(&result)
    return &result, nil
}

func main() {
    data, err := getTranscript("dQw4w9WgXcQ", "de") // German
    if err != nil {
        panic(err)
    }
    fmt.Printf("Language returned: %s\n", data.Language)
    for _, seg := range data.Transcript[:3] {
        fmt.Printf("[%.1fs] %s\n", seg.Start, seg.Text)
    }
}

Example JSON Response

A successful request returns the transcript segments alongside a language field confirming which track was served:

{
  "video_id": "dQw4w9WgXcQ",
  "language": "fr",
  "transcript": [
    { "text": "Nous nous connaissons depuis si longtemps", "start": 17.6, "duration": 4.2 },
    { "text": "Votre cœur fait mal et vous savez pourquoi", "start": 21.8, "duration": 3.9 },
    { "text": "Je voulais te le dire mais je n'ai pas eu le cœur", "start": 25.7, "duration": 4.1 }
  ]
}

Language code format: Use standard BCP-47 codes. Two-letter codes (fr, es, de, ja, ko) cover most cases. For regional variants use the extended form: pt-BR for Brazilian Portuguese, zh-Hans for Simplified Chinese, zh-Hant for Traditional Chinese, en-GB for British English.

Falling Back to Auto-Translated Tracks

What happens when you request lang=ar but the video only has English captions? Rather than returning an error, the API falls back to YouTube's auto-translation layer and returns an Arabic-language transcript generated from the original captions.

This is useful but worth understanding precisely:

  • Exact track match: If the video has a manually created or auto-generated Arabic caption track, that's returned directly.
  • Auto-translation fallback: If no Arabic track exists, the API requests YouTube's auto-translated version, which runs the original captions through machine translation.
  • Language confirmation: The language field in the response reflects what was actually served. If you requested ar but got auto-translated output, you'll still see ar — the key signal is whether the content came from a human-created track or machine translation, which you'd need to infer from your knowledge of the video.

For production pipelines where caption quality matters — accessibility, legal transcription, regulated content — you should validate whether the video has a human-created track before relying on auto-translated output. For use cases like LLM training data, summarization, or search indexing, auto-translated tracks are often good enough to be useful.

Fetching a transcript with fallback handling in Python:

def get_transcript_with_fallback(video_id: str, preferred_lang: str, fallback_lang: str = "en"):
    """
    Try preferred language first. If translation quality is critical,
    check the returned language against what was requested.
    """
    data = get_transcript(video_id, lang=preferred_lang)
    returned_lang = data.get("language", "")

    if not returned_lang.startswith(preferred_lang.split("-")[0]):
        # The API returned a different language — fall back to English
        print(f"Requested {preferred_lang}, got {returned_lang}. Falling back to {fallback_lang}.")
        data = get_transcript(video_id, lang=fallback_lang)

    return data

Detecting Available Languages Per Video

The /api/v2/transcript endpoint doesn't expose a separate "list available languages" call — you discover what's available by attempting to fetch the transcript and inspecting the language field in the response.

A practical pattern for localization pipelines: fetch the transcript in each of your target languages and record what comes back. For batch workflows, combine this with the bulk endpoint.

Bulk Fetching Transcripts Across Languages

The /api/v2/transcripts-bulk endpoint accepts up to 50 video IDs per POST request — but if you need the same video in multiple languages, you'll send one request per language. Here's a pattern that fetches a single video across several target locales:

import httpx
import asyncio

API_KEY = "YOUR_API_KEY"
BULK_URL = "https://getyoutubetranscriber.com/api/v2/transcripts-bulk"

TARGET_LANGUAGES = ["en", "es", "fr", "de", "ja", "pt-BR", "zh-Hans", "ar"]

async def fetch_multilingual_transcripts(video_id: str) -> dict[str, dict]:
    async with httpx.AsyncClient() as client:
        tasks = []
        for lang in TARGET_LANGUAGES:
            tasks.append(
                client.get(
                    "https://getyoutubetranscriber.com/api/v2/transcript",
                    params={"video_url": video_id, "lang": lang},
                    headers={"Authorization": f"Bearer {API_KEY}"},
                )
            )
        responses = await asyncio.gather(*tasks, return_exceptions=True)

    results = {}
    for lang, resp in zip(TARGET_LANGUAGES, responses):
        if isinstance(resp, Exception):
            results[lang] = {"error": str(resp)}
        elif resp.status_code == 200:
            data = resp.json()
            results[lang] = {
                "language": data["language"],
                "segment_count": len(data["transcript"]),
                "sample": data["transcript"][0]["text"] if data["transcript"] else "",
            }
        else:
            results[lang] = {"error": f"HTTP {resp.status_code}"}

    return results

# Run it
video_id = "dQw4w9WgXcQ"
transcripts = asyncio.run(fetch_multilingual_transcripts(video_id))
for lang, info in transcripts.items():
    print(f"{lang}: {info}")

Sample output:

en: {'language': 'en', 'segment_count': 112, 'sample': "We've known each other for so long"}
es: {'language': 'es', 'segment_count': 109, 'sample': 'Nos conocemos hace tanto tiempo'}
fr: {'language': 'fr', 'segment_count': 108, 'sample': 'Nous nous connaissons depuis si longtemps'}
ja: {'language': 'ja', 'segment_count': 95, 'sample': '私たちはとても長い間知り合いです'}

This approach gives you a practical language availability map for any video without a separate API call.

Abstract visualization of world languages and code streams representing multilingual data processing

Use Cases for Localization Teams

The 125+ language coverage unlocks workflows that would be painful to build on raw YouTube APIs or open-source libraries.

Subtitle and Caption Generation

Caption files for accessibility and compliance are the most direct use case. Pull the transcript in the viewer's language, format the segments into SRT or VTT, and serve them alongside embedded video. The start and duration fields on each segment map directly to subtitle timing.

def transcript_to_srt(transcript: list[dict]) -> str:
    lines = []
    for i, seg in enumerate(transcript, 1):
        start = seg["start"]
        end = start + seg["duration"]

        def fmt(seconds):
            h, remainder = divmod(seconds, 3600)
            m, s = divmod(remainder, 60)
            ms = int((s % 1) * 1000)
            return f"{int(h):02}:{int(m):02}:{int(s):02},{ms:03}"

        lines.append(f"{i}\n{fmt(start)} --> {fmt(end)}\n{seg['text']}\n")
    return "\n".join(lines)

data = get_transcript("VIDEO_ID", lang="ko")
srt = transcript_to_srt(data["transcript"])
with open("subtitles_ko.srt", "w", encoding="utf-8") as f:
    f.write(srt)

Multilingual LLM Pipelines

If you're building a RAG system or fine-tuning dataset, you often want transcript data in the same language as your users' queries. Rather than translating after the fact, pull the transcript directly in the target language — you get cleaner text without an extra translation step. For more on assembling transcript datasets for AI training, see Building Transcript Datasets for LLM Fine-Tuning.

Channel-Level Localization Monitoring

For teams tracking multilingual YouTube channels — news outlets, international brands, educational publishers — you can combine the channel-videos endpoint with per-language transcript fetching to maintain a multilingual content index. The Monitor a YouTube Channel for New Videos via API guide covers the channel polling pattern; add lang parameters to transcript calls once you have new video IDs.

Content Repurposing at Scale

According to Business Research Insights, the video localization market reached an estimated $1.87 billion in 2024, driven by platforms and content teams localizing video assets into new markets. If you're running a content repurposing pipeline — turning YouTube videos into blog posts, summaries, or social content — pulling the transcript in the reader's language is faster and cheaper than translating after extraction.

A practical setup: detect user locale, pass lang=<user_locale> to the transcript call, then pass the result to your LLM for summarization. One API call, one credit, localized output.

def summarize_video_for_locale(video_id: str, locale: str, openai_client) -> str:
    transcript_data = get_transcript(video_id, lang=locale)
    full_text = " ".join(seg["text"] for seg in transcript_data["transcript"])

    response = openai_client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": f"Summarize the following transcript in {locale} in 3 bullet points."},
            {"role": "user", "content": full_text[:8000]},
        ]
    )
    return response.choices[0].message.content

Accessibility and Compliance Workflows

The 80/20 reality of captions: studies consistently find that a large majority of caption users are not hearing-impaired — they're watching in noisy environments, quiet offices, or in a non-native language. For teams with accessibility or regulatory requirements (particularly in the EU under the European Accessibility Act), being able to pull clean caption text programmatically across languages matters as much for compliance auditing as for user experience.

Practical Gotchas

A few things to watch for when building with multilingual transcripts:

  • Right-to-left languages: Arabic (ar), Hebrew (he), and Persian (fa) return text in RTL order. If you're rendering this in a UI, make sure your layout handles dir="rtl" correctly.
  • Segment count varies by language: Translated tracks often have different segment boundaries than the original. Don't assume segment count or timing will match between languages even for the same video.
  • Language code specificity: pt returns generic Portuguese; pt-BR requests Brazilian Portuguese specifically. If you're building for a Brazilian audience, the more specific code gets you more relevant output.
  • Videos with no captions: Some videos have no caption tracks at all. These return an error rather than an empty transcript. Handle this with a try/except or status-code check, and consider this when designing retry logic.
  • Rate limits: High-volume multilingual fetching (e.g., 8 languages × 1,000 videos = 8,000 calls) should respect rate limits. Check the rate limits documentation and batch requests accordingly.

Getting Started

YouTube Transcriber starts with 100 free credits and no credit card required. For transcript requests, that's 100 multilingual transcript calls to explore the language support before committing.

The full parameter reference for the lang parameter — including all supported BCP-47 codes and examples for edge-case languages — is in the API documentation. If you're new to transcript fetching in general, Get YouTube Transcripts in Python: The Complete Guide covers the basics before you layer in language selection.

Start with the languages your users actually need, confirm the language field in responses, and build your fallback logic around the cases where exact tracks don't exist. That's the pattern that holds up in production.