Skip to main content

podpull

podpull

PyPI CI License: MIT Platforms

Find episodes from any show. Download straight to disk — Apple Podcasts, RSS, or 小宇宙/xiaoyuzhou.

Use the CLI (interactive multi-select picker, spinners, progress bars) or the browser UI. No app, no login, no DRM. Filenames are cloud-safe; CLI downloads land in a per-show folder in the current directory.

▶ Official site: https://podpull.xiaolei.work — landing / · app /app
▶ GitHub Pages mirror: https://xiaoleiy.github.io/podpull

Demo

podpull: search a show, pick episodes, download with a progress bar

text version
$ podpull get 1532755821
✓ 我們家的睡前故事 — 394 episodes
? Select episodes  (↑/↓ · space · enter)
❯ ◉ 2026-06-23  EP343 迷宮中的牛頭人
  ◯ 2026-06-16  EP342 午睡任務
  ◉ 2026-06-09  EP341 修煉龍 4
██████████████████  100%  21.2 MB  •  saved to ./我們家的睡前故事/

How it works

Apple Podcasts hosts no audio — it is a directory that points at each show's RSS feed, and every episode in that feed carries a direct <enclosure> audio URL. podpull walks that chain:

Apple show URL/id ──(iTunes Lookup API)──▶ RSS feedUrl
RSS feed          ──(<enclosure url>)────▶ direct .mp3 / .m4a
download          ──(resumable)──────────▶ <YYYY-MM-DD> - <title>.<ext>

It also resolves a pasted xiaoyuzhou episode page (via its og:audio tag), a pasted Apple episode link (…?i=<id>, matched in the feed), and a Ximalaya album URL (ximalaya.com/album/<id> → that album's RSS).

Install

# pipx (recommended) or pip — from PyPI
pipx install podpull
pip   install podpull

# or Homebrew (macOS / Linux)
brew install xiaoleiy/tap/podpull

Requires Python 3.9+. Optional: yt-dlp (deep-catalog Apple-episode fallback when the track isn't in the recent feed window). Also on PyPI.

Windows

Homebrew is macOS/Linux only — on Windows, install with pip/pipx (PowerShell or Windows Terminal):

# 1) Python 3.9+ (skip if you have it):
winget install Python.Python.3.12

# 2) Recommended — isolated install via pipx:
py -m pip install --user pipx
py -m pipx ensurepath          # then reopen the terminal
pipx install podpull

# …or plain pip:
py -m pip install podpull

Then run podpull in Windows Terminal or PowerShell. The interactive picker, colors, and progress bars work there; filenames are sanitized for Windows too (reserved names, illegal characters). If podpull isn't found after install, reopen the terminal (so PATH refreshes) or use py -m podpull.

Set up your AI coding agents (optional)

Teach your AI agents to use podpull so you can just ask them to grab an episode:

podpull skills install        # detected agents
podpull skills install --all  # all supported agents
podpull skills status         # see what's detected / installed
podpull skills uninstall      # remove what was installed

This writes podpull's instructions in each agent's native format:

Agent Installed as Location
Claude Code skill ~/.claude/skills/podpull/SKILL.md
Codex skill ~/.codex/skills/podpull/SKILL.md
OpenCode /podpull command ~/.config/opencode/commands/podpull.md
Cursor project rule <project>/.cursor/rules/podpull.mdc

Re-run after brew upgrade to refresh. Nothing is written until you run it. (Cursor has no file-based global rule — run it inside a project for a project rule, or paste the printed rule into Cursor Settings → Rules.)

Usage

podpull search "睡前故事"                 # find shows -> id, #episodes, name, author
podpull search "NPR" --country US --limit 5
podpull info  1532755821                  # show metadata (accepts URL, id, or RSS)
podpull list  1532755821                  # recent episodes, numbered (0 = newest)
podpull list  1532755821 --match "EP34"   # filter by title regex
podpull list  1532755821 --all            # every episode in the feed

Download

# Interactive picker — just give a show; pick one or many episodes with the keyboard:
podpull get 1532755821
#   ↑/↓ move · space toggle · a select-all · enter confirm
#   (podpull pull … is an alias for get)

# Or select non-interactively (also used when piping / scripting):
podpull get 1532755821 --latest 1               # newest episode
podpull get 1532755821 --match "牛頭人"          # by title regex
podpull get 1532755821 --index 0,2,5            # by list number (0 = newest)
podpull get 1532755821 --latest 3 --out ~/Audio/bedtime
podpull get 1532755821 --latest 1 -q            # quiet: paths on stdout only

# Pasted single-episode / album links:
podpull get "https://www.xiaoyuzhoufm.com/episode/<id>"
podpull get "https://podcasts.apple.com/.../id<show>?i=<track>"
podpull get "https://www.ximalaya.com/album/<id>"

Downloads default to ./<show name>/ in the current directory (override with --out). The saved file path is printed to stdout (so you can pipe/capture it); progress and messages go to stderr. Use --no-input to never open the picker (fail instead) for scripts.

JSON (scripting)

podpull --json search "睡前故事"
podpull --json list 1532755821 --limit 5 | jq '.episodes[0].title'
podpull --json get 1532755821 --latest 1 --no-input | jq -r '.downloads[].path'

--json goes before the command. On success, stdout is one JSON document (no tables/progress); on failure, a human message on stderr and a non-zero exit.

Filenames are normalized for cloud storage — emoji and other symbols are dropped, full-width/illegal characters folded or stripped — so files upload cleanly to Google Drive, OneDrive, Dropbox, iCloud, etc. (CJK and ordinary text are kept). When you grab multiple episodes at once, they're placed in a sub-folder named after the show.

<src> accepts: an Apple show URL, a bare Apple ID, a raw RSS feed URL, an Apple episode URL (?i=), a xiaoyuzhou episode URL, or a Ximalaya album URL (ximalaya.com/album/<id>).

Podcast Index (optional)

podpull can enrich search and feed resolution with the open Podcast Index directory. Get a free API key at api.podcastindex.org and set:

export PODCASTINDEX_API_KEY=...
export PODCASTINDEX_API_SECRET=...

Without these, podpull behaves exactly as before (iTunes only).

Web UI

Hosted at podpull.xiaolei.work:

Path What
/ Marketing landing (live Apple Top Podcasts strip)
/app Search, trending, per-episode browser download
/api/* Metadata only — no audio proxy

In the app: search shows, browse trending (中文 via xyzrank · International via Apple Top Podcasts), EN / 中文 UI, then Download or Open page per episode (browsers block bulk multi-file downloads). Audio always comes from the publisher CDN in your browser. Hosting notes: docs/HOSTING.md.

Local twin (same UI/API contract):

podpull serve                 # http://127.0.0.1:8787 (opens browser)
podpull serve --port 9000
podpull serve --host 0.0.0.0  # LAN (prints a warning)
podpull serve --no-open       # don't open a browser

For guaranteed saves into a per-show folder (cloud-safe names) in the current directory, keep using podpull get.

Roadmap

  • v0.1: search · info · list · download (stdlib only).
  • v0.2: interactive multi-select picker, rich progress bars + spinners, colored help, scriptable stdout. Adds rich + questionary.
  • v0.3: renamed podget → podpull.
  • v0.4: cloud-safe filename normalization; multi-episode downloads grouped into a per-show folder.
  • v0.5: pull alias for get; podpull skills install sets up integrations for Claude Code, Codex, OpenCode, and Cursor.
  • v0.6: robust feed parsing (RSS 2.0 / RSS 1.0 / Atom, dirty-XML recovery), verified against Chinese-market hosts, ximalaya.com/album/<id> links, optional Podcast Index (BYOK) search + feed-resolution fallback.
  • v0.7: --json output mode for scripting (podpull --json list … | jq).
  • v0.8: podpull serve + hosted site at podpull.xiaolei.work (landing /, app /app, metadata /api); trending (中文 / International); EN/中文; per-episode browser download (no audio proxy).
  • v0.9 (current): CLI downloads default to ./<show name>/ in the current directory (not ~/Downloads/Podcasts); every show download uses a per-show folder.
  • next: BYOK summarization (podpull[ai]).
  • v1+ (podpull[ai]): opt-in BYOK summarization — local transcription (faster-whisper) + your own LLM key (Anthropic/OpenAI). Fully local, private, no subscription. Cleanly isolated from the core.

podpull reads the public RSS feeds that podcasters publish for exactly this purpose, and downloads the enclosure files they distribute. Respect each show's copyright and terms — download only what you're entitled to, for personal use.

License

MIT © xiaoleiyu

Metadata

Release files for podpull 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for podpull 0.9.0
File Size Uploaded
podpull-0.9.0.tar.gz 1.3 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for podpull 0.9.0
File Interpreter ABI Platform
podpull-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.4 MB

Release files / podpull-0.9.0.tar.gz

Download URL podpull-0.9.0.tar.gz
Size 1.3 MB
Tags Source
SHA-256 checksum
How to use checksums
5c4bc2a52b5226109b751c665a545e7aaf12fcbc05c10a95a035f8c961b59119
BLAKE2b-256 checksum
How to use checksums
f9ae9b6c91de562c66cd204d8088bdb013947653f5aba57184e89f858abf6100
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / podpull-0.9.0-py3-none-any.whl

Download URL podpull-0.9.0-py3-none-any.whl
Size 45.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1145343af1f2b60dcdc5f9c2460f05c47d4863bbe48fb4daa92d660ad97954cc
BLAKE2b-256 checksum
How to use checksums
164925967f03708c4c585a7002a7b066e36e58e38f4fdaf8932d4c4589a476e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files

0.8.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page