Guess it’s done , SpotOn
Project: SpotOn · Stack: Node.js, Slack Bolt, MusicBrainz API, OpenTDB
Status: ✅ Working · Commands shipped: 6
What is SpotOn?
SpotOn is a Slack bot that brings music discovery into your workspace via slash commands. Random track picks, artist comparisons, similar artist recommendations, stats, trivia, and band name generation — all without leaving Slack.
The goal was simple: a bot you can actually use casually in a team channel without it feeling like a chore.
What I Built
CommandWhat it does/spoton-trackRandom track pick from the MusicBrainz catalogue/spoton-similar Finds artists sharing genres/tags/spoton-vs vs Head-to-head stat battle/spoton-stats Full artist profile — genres, albums, recordings/spoton-triviaInteractive music trivia with Slack buttons/spoton-bandnameGenerates a random band name + debut album title
The Spotify Problem
I originally built everything on the Spotify Web API. No user OAuth needed — just client credentials for public catalog data. Seemed perfect.
Then the /spoton-track command started throwing 403 Forbidden. The error body said it all:
“Active premium subscription required for the owner of the app.”
Spotify quietly tightened their API access in late 2024. Even the Search endpoint — purely public catalog data — now requires the app owner to have a Premium account. Every command I’d built against the Search API was dead.
Scrapped Spotify entirely. Switched everything to MusicBrainz.
Why MusicBrainz
Completely free, no API key required
Massive open music database
Structured data: tags, release groups, recordings, artist relationships
The only rule: identify your app in the User-Agent header and stay under ~1 req/sec
The tradeoff is no popularity scores or audio previews, but for everything SpotOn actually needs — artist metadata, genres, recordings, album counts — it’s more than enough.
Interesting Implementation Notes
Similar artists without a “related” endpoint
MusicBrainz has no direct “related artists” API. The workaround: fetch the source artist’s top tags, then search for other artists matching those same tags. It’s not perfect, but the results are surprisingly good — searching tag:alternative rock AND tag:indie for Radiohead surfaces exactly the kind of artists you’d expect.
Parsing /spoton-vs input
Getting two artist names from a single text field is messier than it looks. Users might type:
Taylor Swift vs Drake
Taylor Swift, Drake
oasis blur (no separator at all)
The final parsing tries vs split first, then comma, then a midpoint word split as a last resort. Not bulletproof, but handles the common cases. Documenting the preferred format in /spoton-help does the rest.
Trivia with interactive buttons
The /spoton-trivia command uses Slack’s Block Kit action buttons for the answer choices. Answer state is stored in a Map keyed by userId-channelId, so multiple people can have active questions at once. The map entry is deleted after the user answers, keeping memory clean.
One gotcha: the OpenTDB API returns HTML-encoded strings (&, ', etc.) in question and answer text. Had to add a small decode() helper to strip those before sending to Slack, otherwise the blocks render raw HTML entities.
MusicBrainz rate limiting
MusicBrainz asks for 1 request/second max. Commands like /spoton-stats make 3 sequential API calls (artist detail, albums, recordings). Running them with Promise.all works fine most of the time, but under load it can trigger a 503. The safe pattern is sequential calls with a sleep(300) between each — slower, but reliable.
Stack
Runtime: Node.js
Slack: Slack Bolt with Socket Mode (no public URL needed)
Music data: MusicBrainz API (free, no key)
Trivia: OpenTDB API (free, no key)
HTTP: axios
Config: dotenv
Comments 0
No comments yet. Be the first!
Sign in to join the conversation.