Rust CLI for Podcast Index API, supporting:
tableoutput for human readingjsonoutput for scripts/AI agents- YouTube video search and subtitle download
- Podcast episode download and transcription
Prerequisites
- Rust toolchain (
cargo) - Podcast Index credentials:
api_keyandapi_secret - yt-dlp (for YouTube features)
- ffmpeg (for audio processing)
- OpenAI Whisper (for transcription)
Install
cargo install --git https://github.com/the-waste-land/podcast-cli.git --tag v0.2.1
Or build locally:
cargo build --release
cargo install --path . --forceConfigure API Credentials
podcast-cli config set \ --api-key "<your_api_key>" \ --api-secret "<your_api_secret>" \ --default-output table \ --max-results 10
Proxy Configuration
If you need a proxy to access the Podcast Index API:
export HTTP_PROXY=http://127.0.0.1:7890 export HTTPS_PROXY=http://127.0.0.1:7890 # or ALL_PROXY for all protocols
Supported env vars: HTTP_PROXY, HTTPS_PROXY, ALL_PROXY (and lowercase variants)
Quick Start
podcast-cli search "rust" --limit 5
podcast-cli show 920666
podcast-cli episodes 920666 --limit 10
podcast-cli trending --limit 5
podcast-cli statsCommand Reference
| Command | Description | Common Options |
|---|---|---|
search <term> |
Search podcasts | --person --music --limit --output |
show <feed-id> |
Show podcast details by id | --url --output |
episodes <feed-id> |
List episodes under a feed | --limit --output |
episode <episode-id> |
Show episode details | --output |
download <episode-id> |
Download episode audio | --dest |
transcribe <audio-file> |
Transcribe audio with Whisper | --model --language |
trending |
Trending podcasts | --episodes --lang --limit --output |
recent |
Recent updates | --feeds --before --since --limit --output |
categories |
Category list | --output |
stats |
Platform metrics | --output |
config set/show/clear |
Manage local config | --api-key --api-secret --default-output --max-results |
youtube-search <query> |
Search YouTube videos | --limit --channel --since --with-meta --meta-concurrency --meta-timeout --json-envelope |
youtube-meta <video-id> |
Fetch YouTube metadata for a single video | --output <json|table> |
youtube-subtitles <video-id> |
Download YouTube subtitles | --lang --output |
Output Modes
--output table: default, concise table output--output json: machine-readable output
Examples
# Search podcasts podcast-cli search "Sam Altman" --limit 5 # Show podcast details podcast-cli show 6023552 # List episodes podcast-cli episodes 6023552 --limit 10 # Download episode audio podcast-cli download 51062882089 --dest ./episode.mp3 # Transcribe audio (requires Whisper) podcast-cli transcribe ./episode.mp3 --language en # YouTube search podcast-cli youtube-search "Sam Altman" --limit 5 podcast-cli youtube-search --channel "Lex Fridman" --since 30d podcast-cli youtube-search "Sam Altman" --limit 10 --with-meta podcast-cli youtube-search "Sam Altman" --with-meta --meta-concurrency 4 --meta-timeout 20 podcast-cli youtube-search "Sam Altman" --with-meta --json-envelope # YouTube single-video metadata podcast-cli youtube-meta 5MWT_doo68k podcast-cli youtube-meta 5MWT_doo68k --output table # YouTube subtitles podcast-cli youtube-subtitles 5MWT_doo68k --lang en --output json podcast-cli youtube-subtitles 5MWT_doo68k --lang en --output srt # Trending and recent podcast-cli trending --limit 10 podcast-cli recent --limit 10 podcast-cli recent --feeds --since 1700000000 --output json # Stats podcast-cli stats --output json # Categories podcast-cli categories --output json
Validation Rules
--limitrange:1..=100recent --beforeandrecent --sincemust be integer Unix timestampsyoutube-search --meta-concurrencyrequires--with-meta; range:1..=16youtube-search --meta-timeoutrequires--with-meta; range:1..=120seconds
YouTube Metadata Fields
youtube-meta returns a stable JSON object with explicit nullable fields:
video_id,title,channel,urlduration,upload_date,timestampview_count,like_count,comment_countavailability
For youtube-search --with-meta, the existing result shape is preserved and these fields are appended:
timestamp, view_count, like_count, comment_count, availability, meta_status.
If metadata fetch fails for one item, those appended fields are returned as null for that item and meta_status is set to failed or timeout.
meta_status values:
ok: metadata fetched successfullyfailed: metadata fetch failed (non-timeout error)timeout: metadata fetch timed outskipped: fallback status when metadata is skipped
YouTube Search JSON Shapes
Default mode returns a JSON array (backward-compatible):
[
{
"video_id": "...",
"title": "...",
"channel": "...",
"duration": 3600,
"upload_date": "2026-03-05",
"url": "https://www.youtube.com/watch?v=..."
}
]Envelope mode (--json-envelope) returns an object:
{
"query": "AI interview",
"items": [],
"meta": {
"searched": 20,
"with_meta": true,
"meta_success": 18,
"meta_failed": 1,
"meta_timeout": 1
}
}upload_date normalization in YouTube outputs:
- empty string / whitespace /
NA/null=>null YYYYMMDD=>YYYY-MM-DD
Troubleshooting
command not found: podcast-cli
source ~/.zshrc echo $PATH | tr ':' '\n' | rg '.cargo/bin'
Configuration error: api_key is not configured
podcast-cli config show podcast-cli config set --api-key "<key>" --api-secret "<secret>"
- Network/API timeout or DNS issue
curl -4 -v --connect-timeout 10 https://api.podcastindex.org
yt-dlperrors for YouTube commands
yt-dlp --version
yt-dlp --skip-download --dump-single-json "https://www.youtube.com/watch?v=dQw4w9WgXcQ"