A Python project for downloading YouTube transcripts using two different approaches:
- Native API (YouTube Transcript API) - Free, works with videos that have captions enabled
- Supadata API - Paid service, can generate transcripts for videos without captions
- uv - Fast Python package manager
- (Optional) Supadata API Key - Required for downloading videos without captions
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# Or with pip
pip install uvuv syncThis will create a virtual environment and install all dependencies from pyproject.toml.
If you want to use the Supadata API for videos without captions:
- Get your API key from Supadata
- Create a
.envfile in the project root - Add your API key:
SUPADATA_API_KEY=your_api_key_hereYou can download transcripts for individual YouTube videos without adding them to content_resources.json:
# Basic usage - provide the video ID
uv run main.py --video-id dQw4w9WgXcQ
# Video IDs that start with '-': use = to avoid flag ambiguity
uv run main.py --video-id=-s9Oj3koBTc
# Pass a full YouTube URL instead of just the ID (extra params like &t=15s are ignored)
uv run main.py --video-id "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=15s"
uv run main.py --video-id https://youtu.be/dQw4w9WgXcQ
uv run main.py --video-id "https://www.youtube.com/shorts/dQw4w9WgXcQ"
# With custom creator name and language
uv run main.py --video-id dQw4w9WgXcQ --creator "Rick Astley" --lang en
# With all options
uv run main.py --video-id dQw4w9WgXcQ --creator "Rick Astley" --lang en --date 10-25-1987 --title "Never Gonna Give You Up"
# Custom output directory
uv run main.py --video-id dQw4w9WgXcQ --output-dir my_transcripts
# View all available options
uv run main.py --helpCommand-line Options:
--video-id: YouTube video ID or full YouTube URL (required for single video mode). For IDs starting with-, use--video-id=VALUE.--creator: Content creator name (default: "YouTube")--date: Published date in MM-DD-YYYY format (default: today's date)--lang: Native language code (e.g., zh, en, ja)--title: Video title for display purposes--output-dir: Output directory (default: transcripts)
Supported URL formats for --video-id:
| Format | Example |
|---|---|
| Standard watch URL | --video-id "https://www.youtube.com/watch?v=dQw4w9WgXcQ" |
| Short URL | --video-id https://youtu.be/dQw4w9WgXcQ |
| Shorts URL | --video-id "https://www.youtube.com/shorts/dQw4w9WgXcQ" |
| Bare video ID | --video-id dQw4w9WgXcQ |
ID starting with - |
--video-id=-s9Oj3koBTc |
Shell quoting: URLs containing
?(e.g.watch?v=...) must be quoted in zsh/bash, otherwise the shell expands?as a glob wildcard and you getzsh: no matches found. Short URLs (youtu.be/...) and bare IDs have no?and don't need quoting.
Note: Single video downloads do NOT update content_resources.json. They're standalone downloads for quick, ad-hoc use.
Always run this first. Downloads transcripts using YouTube's native API for videos with captions enabled.
uv run main.pyUpdates JSON flags:
downloaded_via_native_api:trueif successfulcaption_enabled:trueif captions exist,falseif not
Only run this if you have videos where:
caption_enabledisfalse(no YouTube captions available)downloaded_via_supadataisfalse(not yet downloaded via Supadata)
This approach uses AI to generate transcripts for videos without captions (requires paid API key).
uv run main_supadata.pyUpdates JSON flags:
downloaded_via_supadata:trueif successful
Recommended Workflow:
- Run
uv run main.pyfirst (native API - free) - Check which videos failed due to
caption_enabled: false - If needed, run
uv run main_supadata.py(paid API) to download those specific videos
After running uv sync, a virtual environment is created in .venv with Python 3.13. You have two options to use it:
This automatically uses the virtual environment without manual activation:
# Check Python version
uv run python --version
# Run your script
uv run main.py
# Run any Python command
uv run python -c "print('Hello')"If you want to activate it for your current shell session:
# Activate the virtual environment
source .venv/bin/activate
# Now you can use Python directly
python --version # Should show 3.13.5
python main.py
# When done, deactivate
deactivate# Add a regular dependency
uv add <package-name>
# Add a development dependency
uv add --dev <package-name>
# Example: Add YouTube transcript API
uv add youtube-transcript-api# Install dependencies from pyproject.toml
uv sync
# Run a Python script
uv run <script.py>
# Run a Python command
uv run python -c "print('Hello')"
# Add a package
uv add <package-name>
# Remove a package
uv remove <package-name>
# Update all packages
uv lock --upgrade
# Show installed packages
uv pip list
# Create a lock file
uv lockyt-transcript-downloader/
├── .python-version # Python version specification (3.13)
├── pyproject.toml # Project configuration and dependencies
├── main.py # Main application entry point
└── README.md # This file
This project uses Python 3.13 and uv for dependency management. The virtual environment is automatically managed by uv.
The content_resources.json file tracks the following flags for each video:
downloaded_via_native_api(boolean) -trueif successfully downloaded via YouTube APIdownloaded_via_supadata(boolean) -trueif successfully downloaded via Supadata APIcaption_enabled(boolean) -trueif video has YouTube captions,falseif notbehind_paywall(boolean) - Video access status
- Download individual videos on-demand without updating JSON
- Free (uses YouTube Transcript API)
- Quick and flexible for ad-hoc downloads
- Accepts a bare video ID or any YouTube URL; extra params (e.g.
&t=15s) are stripped automatically - Usage:
uv run main.py --video-id <VIDEO_ID_OR_URL>
- Free
- Works only with videos that have captions enabled
- Always run this first for batch processing
- Updates:
downloaded_via_native_apiandcaption_enabled - Usage:
uv run main.py
- Paid API (requires API key)
- Generates transcripts for videos without captions
- Only processes videos where
caption_enabled = falseanddownloaded_via_supadata = false - Updates:
downloaded_via_supadata - Usage:
uv run main_supadata.py