Extract YouTube transcripts and translate them with AI — standalone alternative to youtube-transcript-api
Project description
U-Transkript
Extract YouTube transcripts and translate them with AI — no official API, no API keys for extraction, one dependency.
U-Transkript is a standalone alternative to youtube-transcript-api with built-in AI translation. It talks to YouTube's InnerTube endpoint directly using the ANDROID client, so transcript extraction needs no official API key and no PoToken.
- Transcript extraction via YouTube's InnerTube ANDROID client, with watch-page scraping as a fallback
- AI translation to any language via Google Gemini (
gemini-3.5-flashby default) - YouTube server-side translation (
transcript.translate("es")) — free, no AI key needed - Output formats: pretty text, plain text, JSON, SRT, WebVTT
- CLI with single-video and bulk channel mode, plus an optional Flask HTTP API
- Transparent disk cache (24h) on the CLI's fetch paths — repeated fetches of the same video are instant
- Python 3.10+, a single runtime dependency (
requests), resilient retries with backoff
Installation
pip install u-transkript
Optional extras:
pip install "u-transkript[api]" # Flask HTTP API wrapper
pip install "u-transkript[dev]" # pytest, pytest-cov, ruff
Quick start
u-transkript dQw4w9WgXcQ
from youtube_transcript import YouTubeTranscriptApi
transcript = YouTubeTranscriptApi.get_transcript("dQw4w9WgXcQ")
for entry in transcript:
print(f"[{entry['start']:7.2f}] {entry['text']}")
get_transcript returns a list of {"text", "start", "duration"} dicts.
CLI
# Single video — pretty-printed transcript to stdout
u-transkript dQw4w9WgXcQ
u-transkript "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
# Language preference (first available wins)
u-transkript dQw4w9WgXcQ -l de en
# Output formats: pretty (default), json, text, srt, vtt
u-transkript dQw4w9WgXcQ -f srt -o subtitles.srt
# See which transcript languages a video offers
u-transkript dQw4w9WgXcQ --list-transcripts
# Translate with Gemini AI (reads GEMINI_API_KEY from the environment)
u-transkript dQw4w9WgXcQ --translate Turkish -o ceviri.txt
# Channel mode — downloads recent videos as 1_<videoId>.srt, 2_<videoId>.srt, ...
u-transkript @MrBeast -f srt -o transcripts/ -n 25
# From a source checkout (no install needed)
python run.py dQw4w9WgXcQ -f json
| Flag | Description |
|---|---|
target |
Video URL/ID, or @handle / channel URL for bulk download |
-l, --languages |
Language codes in order of preference (e.g. en es fr) |
-f, --format |
pretty (default), json, text, srt, vtt |
-o, --output |
Output file (single video) or directory (channel mode) |
-n, --count |
How many recent videos to download in channel mode (default: 10) |
--list-transcripts |
List available transcript languages and exit |
--translate LANGUAGE |
Translate with Gemini AI (needs GEMINI_API_KEY; not for srt/vtt) |
--no-cache |
Bypass the 24-hour transcript disk cache (cache applies to fetching, not --translate) |
--version |
Print version and exit |
Fetched transcripts are cached for 24 hours in ~/.cache/u-transkript (single-video and channel modes; --translate always fetches fresh). Exit codes: 0 success, 1 user error, 2 network error, 3 transcript/API error.
Config file
Defaults can be set in ~/.u-transkriptrc:
language = en
format = srt
or ~/.config/u-transkript/config.toml:
language = "en"
format = "srt"
The first file found wins entirely. Only language and format are recognized; explicit CLI flags override.
Python library
Pick a language, list what's available
from youtube_transcript import YouTubeTranscriptApi
# Preferred languages, in order
transcript = YouTubeTranscriptApi.get_transcript("dQw4w9WgXcQ", languages=["de", "en"])
# Inspect everything YouTube offers for a video
transcripts = YouTubeTranscriptApi.list_transcripts("dQw4w9WgXcQ")
for t in transcripts:
print(t.language_code, t.language, "(auto)" if t.is_generated else "(manual)")
# Let YouTube translate server-side — no AI key required
english = transcripts.find_transcript(["en"])
if english.is_translatable:
spanish_entries = english.translate("es").fetch()
Export to subtitle formats
from formatters import SRTFormatter, get_formatter
from youtube_transcript import YouTubeTranscriptApi
entries = YouTubeTranscriptApi.get_transcript("dQw4w9WgXcQ")
srt = SRTFormatter().format_transcript(entries)
vtt = get_formatter("vtt").format_transcript(entries) # registry: pretty, json, text, srt, vtt
with open("subtitles.srt", "w", encoding="utf-8") as f:
f.write(srt)
Batch fetch
results = YouTubeTranscriptApi.get_transcripts(
["dQw4w9WgXcQ", "jNQXAC9IVRw"],
languages=["en"],
continue_on_failure=True,
)
for r in results:
status = f"{len(r['transcript'])} entries" if r["error"] is None else f"failed: {r['error']}"
print(r["video_id"], status)
Error handling
Every exception derives from TranscriptRetrievalError and carries a .suggestion with an actionable hint:
from exceptions import NoTranscriptFound, TooManyRequests, TranscriptRetrievalError
from youtube_transcript import YouTubeTranscriptApi
try:
transcript = YouTubeTranscriptApi.get_transcript("dQw4w9WgXcQ", languages=["fi"])
except NoTranscriptFound as e:
print(e) # includes the languages that ARE available
print(e.suggestion)
except TooManyRequests as e:
print(e.suggestion) # rate-limited: wait, or route through a proxy
except TranscriptRetrievalError:
... # base class for all transcript errors
get_transcript also accepts proxies={"https": "..."}, cookies="..." and preserve_formatting=True. For many fetches in a row, use the context-manager form (with YouTubeTranscriptApi() as api: ...) so the shared HTTP session is closed when you are done.
AI translation with Gemini
Translation requires a Gemini API key from Google AI Studio; extraction itself never needs a key.
from ai_translator import AITranscriptTranslator
translator = AITranscriptTranslator("GEMINI_API_KEY") # default model: gemini-3.5-flash
text = translator.set_lang("German").translate_transcript("dQw4w9WgXcQ")
# Fluent configuration; output types: "txt" (default), "json", "xml"
result = (
AITranscriptTranslator("GEMINI_API_KEY")
.set_model("gemini-3.5-flash")
.set_lang("Spanish")
.set_type("json")
.translate_transcript("dQw4w9WgXcQ")
)
# Custom prompts: a str.format template with {text} and {language} placeholders.
# Literal braces must be doubled ({{ }}).
summary = AITranscriptTranslator("GEMINI_API_KEY").translate_transcript(
"dQw4w9WgXcQ",
target_language="English",
custom_prompt="Summarize this transcript in {language} as 5 bullet points:\n\n{text}",
)
Or the one-line shortcut:
from ai_translator import quick_translate
text = quick_translate("dQw4w9WgXcQ", "GEMINI_API_KEY", target_language="French")
Long transcripts are automatically split into chunks (at entry boundaries) and translated chunk by chunk, so long videos don't silently truncate at the model output limit.
HTTP API
api.py ships in the repository (not in the PyPI package), so run it from a checkout:
git clone https://github.com/U-C4N/u-transkript.git
cd u-transkript
pip install -e ".[api]"
python api.py # binds 0.0.0.0:8080 — env vars: PORT, HOST, DEBUG
curl "http://localhost:8080/api?url=dQw4w9WgXcQ"
curl "http://localhost:8080/api?url=dQw4w9WgXcQ&format=srt" -o subtitles.srt
curl "http://localhost:8080/api?url=https://youtu.be/dQw4w9WgXcQ&languages=en,es&format=vtt"
| Endpoint | Description |
|---|---|
GET / |
Usage info |
GET /health |
Health check |
GET /api |
Fetch a transcript |
GET /api parameters: url (required), format (json default, pretty, text, srt, vtt), languages (comma-separated), proxy, preserve_formatting (1/true).
Errors come back as JSON with a type and an actionable suggestion; CORS is enabled. On Replit/Render/Railway-style platforms, set the run command to python api.py — the server binds to $PORT.
How it compares
| u-transkript | youtube-transcript-api | |
|---|---|---|
| Transcript extraction / SRT / VTT / JSON | ✓ | ✓ |
| Built-in AI translation (Gemini) | ✓ | — |
| Bulk channel download (CLI) | ✓ | — |
| HTTP API wrapper included | ✓ | — |
| ANDROID InnerTube client (no PoToken) | ✓ | — |
| Retry with exponential backoff built in | ✓ | — |
| 24h transcript disk cache (CLI) | ✓ | — |
| Single runtime dependency | ✓ | — |
How it works
- Talks to YouTube's InnerTube API using the ANDROID client first (no PoToken required), falling back to watch-page scraping and finally a bare timedtext URL.
- Parses all three transcript wire formats YouTube serves: srv3 XML, legacy XML, and json3.
- Retries transient failures with exponential backoff and honors HTTP 429 rate-limit responses with escalating waits.
Limitations and disclaimer
- Not affiliated with or endorsed by YouTube or Google.
- Relies on undocumented YouTube endpoints that can change without notice.
- Respect YouTube's Terms of Service and the rights of content owners.
- Heavy use can get your IP rate-limited (HTTP 429).
- Channel mode scrapes the channel page HTML, so it can only see the videos that page exposes (roughly the most recent ones);
-nis capped by what the page yields.
Development
git clone https://github.com/U-C4N/u-transkript.git
cd u-transkript
pip install -e ".[dev]"
python run.py dQw4w9WgXcQ -f json # run the CLI from source
pytest # test suite
ruff check src/ tests/ # lint
Links
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file u_transkript-3.3.0.tar.gz.
File metadata
- Download URL: u_transkript-3.3.0.tar.gz
- Upload date:
- Size: 34.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1167a4be34cc8df2cb88bf623bc7ec087cfdd54fbeea18bad971586adf10e2ec
|
|
| MD5 |
d73d9e3b800fd6a639e28bd96b43121a
|
|
| BLAKE2b-256 |
97cf58a98760253aff2737e587053fce5a36cce2408b41f4982a75acf97f8185
|
File details
Details for the file u_transkript-3.3.0-py3-none-any.whl.
File metadata
- Download URL: u_transkript-3.3.0-py3-none-any.whl
- Upload date:
- Size: 34.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f46ad3cf31e36659a9ba9d79f334df066d7bbe06edb3f8c7197d0a59d2ad9b36
|
|
| MD5 |
9bc55b697f06cd8a3649e38480603799
|
|
| BLAKE2b-256 |
0d403b24ac064e8debf6613e205a976608ec31063a587477d1d8858e7064b714
|