When Spotify deprecated Audio Features, Recommendations, and Related Artists for new apps in November 2024, a wave of "drop-in replacement" APIs appeared. Most stop at parity: you send a track, you get BPM, key and energy back. Useful β but that's the same lookup Spotify already gave you.
FreqBlog went a layer further. It rebuilt the dead endpoints, then shipped the thing Spotify never had: a set-builder. Pairwise transition scoring, next-track ranking, and full setlist ordering around the Camelot wheel. And the whole surface is exposed over an MCP server, so an LLM or agent can plan a DJ set by calling tools directly β no glue code between the model and the music theory.
This is for people building music or AI tooling. I'll show the harmonic-mixing model concretely, then call it two ways: plain REST and MCP.
Before the interesting part, the boring-but-necessary drop-ins:
GET /recommendations
(and the MCP tool get_recommendations
) β the replacement for the removed /v1/recommendations
, re-ranked by genre affinity so a feature-close cross-genre track can't outrank same-genre picks.GET /related-artists
β replaces the killed related-artists endpoint.GET /v1/audio-features/{id}
returns a bare Spotify AudioFeaturesObject
; GET /v1/audio-features?ids=
returns the {"audio_features":[...]}
array envelope. Both mirror Spotify's own shapes, so porting existing code is a small diff.The native lookup is flatter and richer. GET /lookup
resolves a track by name, ISRC, MusicBrainz ID or Spotify ID and returns one flat object β over 40 fields, no nesting:
curl -s "https://api.freqblog.com/lookup?track=Strobe&artist=deadmau5&wait=10" \
-H "X-Api-Key: $FREQBLOG_KEY"
// shape (values illustrative) β every feature is top-level, no "audio_features" wrapper
{
"track_name": "Strobe",
"artist_name": "deadmau5",
"bpm": 128.0,
"key": "B",
"camelot": "1A",
"mode": "minor",
"energy": 0.61,
"danceability": 0.72,
"valence": 0.35,
"genre": "progressive house"
}
Two things worth knowing: bpm
and key
are always present and non-null, and ?wait=10
opts into a bounded synchronous mode β up to 25 seconds β that returns the analysed track inline as a 200
instead of the default 202 + Retry-After
when a track isn't cached yet.
Every musical key maps to a clock position on the Camelot wheel: a number 1
β12
plus a letter (A
= minor, B
= major). Two tracks mix without a key clash when they sit next to each other on the wheel: the same key, the relative major/minor (same number, flipped letter), or the adjacent +1
/-1
neighbours. Jump +7
and you get the classic energy-boost mix.
find_compatible_keys
is pure theory β no catalog hit, zero quota:
// find_compatible_keys(camelot="8A", extended=true)
{
"camelot": "8A",
"compatible": [
{ "camelot": "8A", "relation": "same" },
{ "camelot": "8B", "relation": "relative" }, // minor <-> major
{ "camelot": "7A", "relation": "adjacent_down" }, // -1
{ "camelot": "9A", "relation": "adjacent_up" }, // +1
{ "camelot": "3A", "relation": "energy_boost" }, // +7 (extended=true)
{ "camelot": "1A", "relation": "energy_drop" } // -7 (extended=true)
]
}
Knowing which keys could mix is table stakes. score_transition
rates how well one real track mixes into another, 0
β100
, blending Camelot key compatibility, octave-aware BPM proximity (half/double-time counts as a match), and energy smoothness β and it hands back a human reason:
// score_transition(from_track_id="apple_ad1829eeccb70f9a",
// to_track_id="apple_7c1120fbe0") β costs 1 quota
{
"score": 91,
"components": { "harmonic": 95, "tempo": 92, "energy": 86 },
"reason": "8A->9A +1 adjacent, 126->128 BPM (+2.0), energy +0.04"
}
There's no raw key/BPM endpoint anywhere that gives you that β the pairwise judgement is the product.
suggest_next_track
takes the track that's playing and returns the top-N catalog tracks to play next, each with the same score, components and reason (e.g. "11B->11B same key, 118->117 BPM (-0.29), energy +0.12"
). It's genre-aware by default, so an off-genre track that only coincidentally shares your key/BPM sinks to the bottom.
build_setlist
orders an entire crate (2β100 tracks) into a beat-matched set that follows an energy arc
β peak_time
, warmup
, cooldown
, or flat
β keeping every consecutive transition harmonically and tempo-smooth. It returns an overall flow_score
, the tracks in play order, and the per-step transitions.
Here's where it stops being an API and starts being a capability you hand to a model. Point any MCP client at:
https://mcp.freqblog.com/mcp
That exposes twelve tools β search_catalog
, get_audio_features
, get_audio_features_batch
, find_tracks_by_bpm
, find_tracks_by_key
, find_compatible_keys
, get_recommendations
, get_related_artists
, score_transition
, suggest_next_track
, build_setlist
, tag_track
. The agent orchestrates them itself. A single prompt like "build me a 90-minute peak-time set from these ten tracks" becomes:
search_catalog
on each fuzzy name β concrete itunes_track_id
sbuild_setlist(track_ids=[...], arc="peak_time")
β ordered set + flow_score
itunes_track_id
s to GET /export/rekordbox
(also traktor
, m3u
, cuesheet
, csv
) and drop the crate straight into your DJ softwareNo orchestration code on your side β the tool descriptions carry enough for the model to chain them. The set-builder tools cost a little more quota than a plain lookup (score_transition
1, get_recommendations
2, suggest_next_track
3, build_setlist
5), because each one is doing real combinatorial work.
Auth is an X-Api-Key
header (a ?key=
query fallback exists for browser and email links). Everything above is also available as plain REST β GET /transition
, GET /next-track
, POST /setlist
, GET /similar?track_id=...
β if you'd rather not run an MCP client. It's on RapidAPI too. Pricing starts at Β£0.17/1k, and the free tier is 1,000 requests/month, which is plenty to prototype a set planner.
itunes_track_id
s, so a track has to resolve first (search_catalog
/ /lookup
). Coverage is deep but not universal β niche or regional catalogs have holes./analyze
and /identify
β not the audio itself.Grab a free key and the OpenAPI docs at ** api.freqblog.com/docs**, or read more about the API on