| Path | Transcript coverage | Your one-time effort | Ongoing cost | Verdict |
|---|---|---|---|---|
| 1. Spotify Web API direct /me/player/recently-played + /shows + Spotify-native transcripts |
~10–15% of episodes. Spotify launched transcripts in 2024 but coverage is patchy and there is no API to fetch them — they're rendered in-app only. The community has begged for an endpoint for years; Spotify confirmed (April 2026) they don't share audio/podcast content with Anthropic for training, which is the same wall here. | One OAuth login (~3 min). Authorization Code w/ PKCE, durable refresh token, runs for years without re-auth. | $0. Read-only scopes are free. Feb-2026 catch: Dev-Mode apps now require the owner to hold active Premium or the app dies. Verify before relying. | Listening signal yes, transcripts no. Useful as half of the hybrid. |
| 2. YouTube cross-reference your insight For each Spotify episode, find the same episode on YouTube, pull auto-captions via youtube-transcript-api or yt-dlp |
~75–90% for the podcasts you actually listen to (NLW's AI Daily Brief, Matt Wolfe, Lex, Hard Fork, Acquired, etc. — all cross-post to YouTube with auto-captions). The miss cases are interview shows that go Spotify-exclusive (rare for your taste) and the occasional episode where the YouTube upload date drifts from the Spotify release date by a few days. | None beyond Path 1's OAuth. YouTube transcript fetch needs no auth at low volume from a residential IP — this is exactly Apex's situation. | $0 if we use auto-captions. YouTube Data API v3 quota for episode-matching is ~3–5 units per lookup; 10k/day free quota covers thousands of matches. | Highest-coverage transcript path. But it needs Path 1's listening signal to know what to look up. |
| 3. Hybrid: Spotify signal + YouTube transcripts + Whisper fallback Spotify history → YouTube transcript → Whisper on Apex GPU for the ~10% YouTube-misses |
~95%+. Path 2 catches the bulk; Apex's faster-whisper handles the long-tail episodes that aren't on YouTube but have a downloadable preview/RSS audio (a few major shows still publish open RSS). | Same as Path 2. | Whisper compute is local on Apex GPU — the only cost is electricity. Post-Saturday's 64 GB RAM upgrade this gets noticeably more comfortable; faster-whisper large-v3 fits with room for the rest of TP3 running alongside. | Recommended for Phase 2. The Whisper leg only fires for misses, so most days it does nothing. |
| (Not on the list) Audio download/re-encode of Spotify streams | — | — | — | Off-table. ToS-breaking, DRM-cracking, and you said you don't need it. spotDL is also broken under Feb-2026 API changes — not even an option to debate. |
Path 3 (hybrid) is the answer, but built in two clearly separated phases so we don't waste cycles on transcripts before you've eyeballed the listening signal. The reasoning:
Goal: a flat JSON file of your Spotify listening history, no transcripts yet, just so you can see what you've been consuming. This alone is genuinely valuable — you'll likely look at it and notice patterns you didn't realize.
/me/player/recently-played in a loop with before= cursor pagination, walking backward as far as the endpoint allows./shows/{id} + /episodes/{id} to capture: show name, episode title, episode description, release date, duration, Spotify URL, listened-at timestamp.~/spotify_history/listening_history.json on Apex — persistent path, NOT /tmp (which gets wiped on the WSL2 bounce).Spotify's recently-played endpoint only returns the last 50 items, and the before= cursor walks backward roughly ~50 plays at a time with no guaranteed deep history. Real-world community testing puts the usable window at the most recent few weeks to a few months, not a clean 6–12 months. The "last year" target requires a different mechanism: your Spotify Privacy → Account Privacy → "Download your data" extended export, which gives a full streaming history back to account creation as a JSON file Spotify emails you within ~5–30 days. Phase 1 covers the recent window via API right now; the long-tail backfill arrives via the privacy export, which I request on your behalf with one click in your signed-in browser. Both sources flow into the same listening_history.json.
recently-played + show/episode joins, refresh-token persistence on a non-/tmp path.Goal: every episode in listening_history.json becomes a row in tp3_memories_local with source='spotify_podcasts', full transcript embedded, searchable through the same /ask + brief surfaces the Boys SMS threads now flow through.
search.list with q="{show name} {episode title}" + a release-date window of ±5 days. The first result with a duration within ±10% of the Spotify duration is a confident match. (~3–5 quota units/lookup, well within 10k/day.) Cache the YouTube video ID against the Spotify episode ID so we never re-look-up.youtube-transcript-api on Apex (fast lane, no API key). If it returns RequestBlocked or empty, fall back to yt-dlp --write-auto-subs --skip-download with --cookies-from-browser firefox and the bgutil-ytdlp-pot-provider plugin (robust lane, handles the 2026 PO-token wall). Both routes are battle-tested in the YouTube power research.faster-whisper large-v3 on the Apex GPU. Otherwise mark the episode as transcript_status='unavailable' and move on. No retries against ToS-grey audio sources.tp3_memories_local with: source='spotify_podcasts', doc = full transcript text, metadata_json = {show, episode, release_date, listened_at, duration_ms, spotify_url, youtube_url, transcript_status, transcript_source}. ON CONFLICT (source, external_id) DO NOTHING where external_id = Spotify episode ID. Embed via the local nomic-embed-text ollama model (already running, already used by the rest of TP3).tp3_cursor_report in the existing Mark-speak shape ("12 new podcast episodes ingested, 1 transcript missing, took 7 min"). Non-zero exit + FATAL log if zero episodes or the JSON isn't fresh — per the fail-loud hard rule.Same surface as the Boys SMS threads now — /ask over TP3, or directly through Claude with TP3-search in context. Realistic examples:
source='spotify_podcasts' + show LIKE '%AI Daily Brief%' + listened_at > now() - 30 days, returns the 3–5 matching transcript chunks with episode + timestamp.recently-played is still 50/call, but the show/episode joins are now per-call (no bulk). At ~100 episodes/month listened that's ~200 calls/month total — nowhere near the rate limit. Not a real risk.youtube-transcript-api for days during the Dec 2025 / Jan 2026 backend changes. We mitigate with the dual-lane fetch (api → yt-dlp) but a third tighten by YouTube could blow up both lanes simultaneously. If that happens, the Whisper fallback catches the new episodes but the historical backfill stalls until the community ships a fix. Build the failure path before the happy path — per fail-loud./private/?key= gate is in front of /ask, and no transcript ever lands on the public dashboard. If you ever want to share an excerpt publicly, that's a per-quote decision, not a default.