← Tom Halpin

Recent Work · No. 3

Rebuilding WXPN's 1966 Day, One API Call at a Time

August 2026 · 5 minute read

On August 27th, WXPN spent the whole day in 1966 — Motown next to garage rock next to jazz next to novelty singles, the actual texture of the year rather than a greatest-hits reel. I kept the radio on far longer than I meant to, and by evening I had the station's log: 239 songs, in order, from "Solitary Man" to the thirteen-minute raga of the Butterfield Blues Band's "East-West." I wanted that exact day as a Spotify playlist, and I figured asking an AI to build it would be the short version of the story.

It wasn't. But the long version taught me more about where AI tooling actually stands in 2026 than the short one would have.

The connector that couldn't

Claude has a Spotify connector, and mine showed as connected — but the tools wouldn't surface in the conversation. It took several rounds of checking before I ended up manually adding the capability to the chat myself, a rough edge worth remembering the next time someone describes agent integrations as seamless.

Then came the real limitation. The connector doesn't let the AI search for a track and add it to a playlist. It accepts a natural-language request — "make me a study mix" — and hands it to Spotify's own playlist generator. Give it 239 specific songs in a specific order and it simply errors out.

The AI could talk to Spotify. It just couldn't say anything as specific as what I wanted.

To Claude's credit, it flagged the mismatch instead of papering over it: the generator can't guarantee exact tracks or exact order, and it can't report what it silently dropped. For a list full of garage-psych deep cuts — Wimple Witch, The Stumblin' Blox, a Jerry Blavat single — silent substitution was the one failure mode I couldn't accept. So we went around the connector entirely.

Dropping down to the real API

The replacement was a Python script against Spotify's Web API: search each of the 239 tracks, score every candidate on artist and title similarity, accept nothing below a confidence threshold, add the winners to a playlist in broadcast order, and print an honest list of what it skipped. The scoring puts hard floors on both halves so a strong title can never carry the wrong artist — a rule that exists because the world contains many covers of "Wild Thing" and exactly one Troggs.

First run: 233 of 239 matched. Then a 403 Forbidden on the final step — Spotify developer apps launch in a restricted development mode, and even your own account has to be explicitly allowlisted before the app can create a playlist on its behalf.

A terminal traceback: 233 of 239 tracks matched, then a 403 Forbidden error from the Spotify API while creating the playlist.
233 matches, then Forbidden. The searches all worked; creating the playlist required my own account on my own app's user list.

The debugging is what cost me. Each retry re-ran all 239 searches at three queries apiece, and somewhere past the two-thousandth API call Spotify's rate limiter cut me off: retry after 85,963 seconds. Twenty-four hours. A second app hit the same wall on its very first call, which meant the throttle was at the account level, not the app — and no, a VPN didn't change its mind.

Terminal output: 'Your application has reached a rate/request limit. Retry will occur after: 85210 s'
A 24-hour lockout, applied account-wide. Creating a fresh app didn't reset it.

Engineering for a world that says no

The rate limit turned out to be a useful code reviewer. A day of forced waiting makes you ask why a 239-song list needed two thousand API calls, and the answer was careless retries. The final script never asks the same question twice: every search result is written to a cache file the moment it arrives, so a crash, a 403, or another rate limit resumes instead of restarting. It stops searching a track the instant it finds a confident match, and it paces itself to roughly two calls per second.

It also grew a post-run audit, because "matched" and "correct" are different claims. A CSV report lists every track with its match score, album, and release date. Sort by release date and a row showing 1971 is probably a re-recording wearing the right title; sort by score ascending and the weakest matches surface first, ready for an ear check. The script can find a song called "Try a Little Tenderness" — only a listener can confirm it's the Stax recording.

Terminal output of the successful run: numbered OK lines streaming past — Neil Diamond, Cream, Buffalo Springfield, The Troggs, and more.
The clean run: each result cached the moment it lands, so no interruption can take it back.

Twenty-four hours later, one clean pass: playlist created, 233 of 239 tracks in broadcast order, and a skip list of exactly the obscurities I expected — the garage 45s that never made it to streaming. Those six stay where they've always lived: on the radio.

What I learned

Agent connectors are wishes; APIs are instructions. The AI-to-Spotify integration is built for "play something mellow," not for precise, verifiable work. When exactness matters — this track, this order, tell me what's missing — you drop down a layer to the real API, where every call does one specific thing and admits whether it worked.

Cache before you retry. Nearly all of my wasted API calls were repeat questions. Any script talking to a rate-limited service should treat every answer as too expensive to ask for twice. That lesson cost a 24-hour lockout to learn and one JSON file to fix.

Make the machine confess. The best design decision in the project was forcing a skip list. A tool that silently substitutes its best guess is worse than one that admits defeat, because a visible failure costs you six songs and an invisible one costs you trust in the other 233.

The playlist is called "WXPN 1966 Day - Aug 27, 2026." It opens with Neil Diamond and closes with thirteen minutes of "East-West," exactly the way the radio did it.

← Back to tomhalpin.com © 2026 Tom Halpin