mrkeyoor.com_
Sat 08 Aug 17:40 UTC
PyPIUtilsupdated 08 Aug 2026

youtube-transcript-api

youtube-transcript-api is a synchronous Python client that extracts caption tracks from YouTube's web client without an API key or headless browser. Given a video ID, it can choose preferred languages, distinguish manual from generated captions, request YouTube translations, preserve limited formatting, and emit raw dictionaries, text, JSON, SRT, or WebVTT. It also includes a CLI and proxy configuration. The convenience rests on an undocumented YouTube endpoint, not a supported public API, so availability is inherently less predictable than the simple call suggests.

Verdict

Excellent for small best-effort caption jobs and scripts, with a much nicer API than scraping the page yourself. Do not build a hard availability promise on it unless you are prepared for proxy costs, upstream breakage, and videos that simply have no usable track.

API stability3/5Version 1.x has a clear instance API centered on fetch and list, and returned FetchedTranscript and snippet dataclasses provide a cleaner contract than anonymous dictionaries. Yet the project's own warning is decisive: extraction uses an undocumented YouTube web-client API. Even an unchanged Python interface can begin raising new playability, blocking, parsing, or PO-token errors when YouTube changes its pages or backend behavior.
Docs5/5The README covers the current instance-based API, result objects, language priority, manual versus generated filtering, translation, formatting preservation, proxy setup, Session customization, every bundled formatter, CLI usage, and specific operational warnings. It clearly says video IDs are required, cookie authentication is broken, cloud IPs are often blocked, and the upstream endpoint is undocumented, which is the honesty users need for this category.
Maintenance4/5Version 1.2.4 was uploaded January 29, 2026 and the repository was pushed May 19, 2026. The non-archived repository has 8,007 stars and GitHub reports only 30 open issues and pull requests. Tests cover parsing fixtures, proxies, block errors, languages, translation, cookie failures, formatters, and the CLI. Maintenance necessarily reacts to YouTube changes, so quiet periods do not remove the risk of sudden breakage.
Ecosystem4/5PyPIStats records 8,756,166 downloads in the latest week, and the package serves both application code and shell workflows with no API key. Its output is easy to pass into NLP, subtitle, search, or archival tools, and standard SRT, WebVTT, JSON, CSV, and text formatters reduce glue code. The ecosystem is constrained by YouTube's private behavior and proxy market rather than by a supported extension platform.

Use it if

  • A Python script needs public caption text and timestamps for a modest set of ordinary YouTube videos
  • You need to prefer manual captions, fall back across languages, or use YouTube's available caption translations
  • A data pipeline wants list-like transcript objects plus ready-made SRT, WebVTT, JSON, or plain-text formatting
  • You accept a best-effort scraper and can monitor failures, throttle requests, and change network egress when YouTube blocks it
Skip it if

Setup reality

pip install youtube-transcript-api requires Python 3.8 or newer and installs requests plus defusedxml. There is no Google API key, OAuth client, or browser binary. Pass a video ID, not a full watch URL, and pass language preferences as a list even when there is only one. The difficult part is network reliability. The project uses an undocumented YouTube web-client endpoint, so markup, Innertube behavior, consent flows, PO-token requirements, and caption URLs can change independently of releases. Public cloud egress is frequently blocked; the README says rotating residential proxies are the most reliable workaround and includes an affiliate-backed Webshare adapter. That adds cost, credentials, geography choices, latency, and another failure domain, and even a proxy can be blocked. Generic HTTP or HTTPS proxy URLs may include credentials and must be kept out of logs. Requests are synchronous. The API creates a requests.Session, caches cookies, and is not thread-safe, so create one client per worker thread rather than sharing a global client. You can inject a Session to set CA verification or headers, but cookie authentication for restricted videos is currently commented out and documented as unavailable. Captions may be disabled, missing in preferred languages, automatically generated, untranslatable, age restricted, blocked, or require a PO token. Handle the library's specific exceptions and keep a queue or retry policy outside it; retries cannot manufacture a transcript that does not exist. Snippet duration is how long text stays onscreen, not guaranteed speech duration, and snippets can overlap. Generated captions and automatic translations need quality review before publication or search indexing.

Patterns

Fetch a video's English transcriptfetch-default-transcript

from youtube_transcript_api import YouTubeTranscriptApi

api = YouTubeTranscriptApi()
transcript = api.fetch('dQw4w9WgXcQ')
for snippet in transcript:
    print(snippet.start, snippet.duration, snippet.text)

Pass the video ID, not the full URL. The default language preference is English, and the call is synchronous.

Try languages in priority orderprefer-transcript-languages

transcript = api.fetch(
    video_id,
    languages=['de', 'en'],
)
print(transcript.language_code, transcript.is_generated)

languages is always an iterable of codes, even for one language. The first available match wins, with manual captions preferred over generated ones.

Convert result objects to dictionariesconvert-to-raw-data

transcript = api.fetch(video_id)
rows = transcript.to_raw_data()
# [{'text': '...', 'start': 0.0, 'duration': 1.54}, ...]
process_rows(rows)

duration describes onscreen caption duration, not exact spoken-word duration. Adjacent snippets can overlap.

Keep supported caption markuppreserve-caption-formatting

transcript = api.fetch(
    video_id,
    languages=['en'],
    preserve_formatting=True,
)
htmlish_text = '\n'.join(item.text for item in transcript)

This preserves selected tags such as italics and bold. Treat the result as untrusted external content before rendering it as HTML.

Inspect every available caption tracklist-available-transcripts

tracks = api.list(video_id)
for track in tracks:
    print({
        'language': track.language,
        'code': track.language_code,
        'generated': track.is_generated,
        'translatable': track.is_translatable,
    })

Listing fetches metadata, not all caption text. Call track.fetch() only for tracks the application actually needs.

Require manually created captionschoose-manual-captions

tracks = api.list(video_id)
track = tracks.find_manually_created_transcript(['fr', 'en'])
transcript = track.fetch()

This raises NoTranscriptFound rather than falling back to generated captions. Use find_generated_transcript when generated text is explicitly acceptable.

Request a YouTube caption translationtranslate-transcript

tracks = api.list(video_id)
source = tracks.find_transcript(['en'])
if source.is_translatable:
    german = source.translate('de').fetch()

translate uses YouTube's caption translation and returns a new track descriptor. The requested language must appear in translation_languages.

Render a transcript as SRTformat-as-srt

from youtube_transcript_api.formatters import SRTFormatter

transcript = api.fetch(video_id, languages=['en'])
srt = SRTFormatter().format_transcript(transcript)
with open('captions.srt', 'w', encoding='utf-8') as output:
    output.write(srt)

The formatter converts existing caption timing; it does not correct overlaps, transcription errors, or machine-generated punctuation.

Use a Webshare residential proxy poolconfigure-rotating-proxy

import os
from youtube_transcript_api import YouTubeTranscriptApi
from youtube_transcript_api.proxies import WebshareProxyConfig

api = YouTubeTranscriptApi(proxy_config=WebshareProxyConfig(
    proxy_username=os.environ['WEBSHARE_PROXY_USERNAME'],
    proxy_password=os.environ['WEBSHARE_PROXY_PASSWORD'],
    filter_ip_locations=['de', 'us'],
))
transcript = api.fetch(video_id)

The README says to use the rotating Residential product, not static or ordinary proxy-server plans. Proxy use costs money and still does not guarantee access.

Route requests through a generic proxyconfigure-generic-proxy

from youtube_transcript_api.proxies import GenericProxyConfig

api = YouTubeTranscriptApi(proxy_config=GenericProxyConfig(
    http_url=os.environ['HTTP_PROXY_URL'],
    https_url=os.environ['HTTPS_PROXY_URL'],
))

Static proxy addresses are often blocked after repeated use. URLs may contain credentials, so redact them from exception and configuration logs.

Provide a configured requests sessioncustomize-http-session

from requests import Session

session = Session()
session.headers.update({'Accept-Encoding': 'gzip, deflate'})
session.verify = '/etc/ssl/certs/private-ca.pem'
api = YouTubeTranscriptApi(http_client=session)

YouTubeTranscriptApi updates the Session's Accept-Language header. A client instance and its Session are not thread-safe; create one per thread.

Separate unavailable captions from network blockinghandle-retrieval-failures

from youtube_transcript_api import (
    NoTranscriptFound, TranscriptsDisabled,
    RequestBlocked, IpBlocked, VideoUnavailable,
)

try:
    transcript = api.fetch(video_id, languages=['en'])
except (NoTranscriptFound, TranscriptsDisabled):
    transcript = None
except (RequestBlocked, IpBlocked) as error:
    retry_on_other_egress(video_id, error)
except VideoUnavailable:
    mark_video_removed(video_id)

Do not blindly retry every exception. Missing tracks and unavailable videos are content states; IP blocking is an egress problem.

Alternatives

PackageRegistryPick it when
yt-dlpPyPIA broader media workflow also needs subtitle files, metadata, formats, playlists, or audio extraction
pytubefixPyPIPython code needs broader video and stream access along with caption-track handling
google-api-python-clientPyPIYou control the videos and need the supported YouTube Data API with OAuth, quotas, and official caption resources