The audio_features replacement · BPM · Key · Energy · Mood · Genre
The maintained Spotify audio_features replacement — full analysis from just a track name, in one call:
/v1/audio-features — same path + JSON shape; full coverage via name or ISRC (raw Spotify IDs resolve for mapped tracks only). Migration guide →£0.17 per 1k · 941+ genres · 12+ language families · Auto-enrichment + dashboard
No account required · No ISRC needed · 886k+ tracks instant · J-Pop · K-Pop · Mandopop · Bollywood · MPB · Afrobeats · Free tier — no card
Type any song. We'll call /lookup against a public demo key and render the response below. No signup, no card, no email.
Public demo key. Per-IP rate-limited via nginx; capped monthly quota. For real usage, grab your own free key — no card, instant by email.
No ID hunting. No 30-second waits. Send a name, get data. Here's what makes it different.
Multi-source fallback chain (own catalog + AcousticBrainz + Million Song Dataset + FMA) returns audio features in under 300ms. If we don't have it, Intelligent Backfill fetches the preview from iTunes, runs full Essentia analysis, ingests it, and notifies you when it's ready — any commercial release on iTunes typically resolves in ~15 seconds (always under a minute). Per-track emails for normal use; bulk catalog work rolls into a single daily digest. No upload. No waitlist. Name-based lookup — no ISRC required.
The only music metadata API with an MCP server. Add one line to Claude, Cursor, or Windsurf and your AI can query BPM, key, mood, and genre directly — no code required. See setup →
We don't just guess. We analyze the specific audio of remixes, edits, and club mixes — not just the original release — for significantly higher BPM precision.
Standard APIs ship a fixed 100-genre taxonomy. Our catalog carries 941 distinct genres across 12+ language families — Amapiano, Mandopop, Cantopop, Bollywood, Tamil, Telugu, Punjabi, MPB, Sertanejo, Pagode, Baile Funk, Reggaeton, Bachata, Khaleeji, Turkish Pop, French Chanson, Hyperpop, Phonk, UK Drill and more. 96% of all catalog tracks carry a genre tag. See global coverage →
Audio analysis (/analyze, /identify) runs through per-tier concurrency lanes — Hobbyist 2, Starter 3, Pro 6, Enterprise 8 simultaneous. Catalog /lookup has a generous per-key concurrency cap (6 simultaneous in-flight on Free, 10 on paid — excess parallel calls get an instant 429 with Retry-After: 2) — typically 25–40ms with keep-alive, p95 under 100ms.
Catalog lookups return in ~25–40ms with keep-alive (p95 under 100ms) — results are pre-computed once and served from an in-memory cache. Lookups never trigger live analysis; they just fetch a row. Consistent latency at any scale.
POST /bulk resolves up to 50 tracks in a single round-trip — by name or ISRC (exact-match, ideal when your candidates already carry ISRCs from Chartmetric or Spotify), looked up in parallel server-side, so one HTTP call replaces fifty. It's quota-only (no per-minute rate cap), so it scales with your plan's monthly allowance — perfect for playlist enrichment, data pipelines, and recommendation engines.
Spotify's audio_features ML model was trained English-default. AudD recognises mostly Western commercial releases. We mirror what charts in every region we ingest from — Apple Music storefronts across 45 countries, Deezer genre charts, region-specific feeds, plus customer-driven backfill for the long tail. Below: track counts in the catalog right now, by language family.
Same JSON shape, same 42 fields, in every language. No region flag in the request, no special endpoint, no extra cost. Send artist=BLACKPINK&track=Pink Venom or artist=Bad Bunny&track=Tití Me Preguntó — same response shape. UTF-8 in, BPM/key/energy/genre out. Diacritics, kana, hangul, hanzi, Cyrillic and Arabic are matched via NFKD normalisation; the catalog uses native scripts where Apple Music does. Missing a track? Intelligent Backfill fetches and analyses it in ~15 seconds (always under a minute) — works the same for Tokyo indie as for Tennessee country.
From Amapiano to Zydeco, Mandopop to Hyperpop — 941 distinct genres live in the catalog right now, 96% of all tracks carry one. Standard APIs ship a fixed 100-genre taxonomy; we mirror what the catalog actually contains. Click any tag to search the live catalog — results return below.
BPM, key, mood, energy, genre, ISRC and 30 more — all pre-computed, all returned in a single API call.
| Field | Description | Coverage |
|---|---|---|
| bpm + bpm_confidence | Tempo in BPM with per-track confidence score | ✅ 100% |
| bpm_alt | Half-time / double-time corrected BPM — populated when our heuristic suggests Essentia locked onto the wrong beat grid (e.g. Blinding Lights returns 85 with bpm_alt=170). Null when no correction is warranted. | ✅ when warranted |
| key + key_int + mode + key_confidence | Musical key (human-readable e.g. C#-Minor) plus Spotify-shaped numeric form: key_int = pitch class 0–11, mode = 0 minor / 1 major, plus confidence |
✅ 100% |
| camelot + open_key | Camelot wheel notation (e.g. 8A) and Open Key (e.g. 1m) for harmonic mixing | ✅ 100% |
| energy | 0–1 normalised RMS energy | ✅ 100% |
| loudness_db | LUFS-style dB reading | ✅ 100% |
| time_signature | Integer beats per bar | ✅ 100% |
| danceability | Tempo + beat strength + regularity | ✅ 100% |
| valence | Musical positivity — scale + tempo + brightness + energy | ✅ 100% |
| acousticness | Spectral-flatness estimate — approximate; calibrate per use-case | ✅ 100% |
| instrumentalness | MFCC vocal-formant estimate — approximate; calibrate per use-case | ✅ 100% |
| liveness | Noise-floor / room-ambience proxy — approximate; calibrate per use-case | ✅ 100% |
| speechiness | Zero-crossing-rate estimate — approximate; calibrate per use-case | ✅ 100% |
| mood | MIREX-style label: happy, calm, sad, tense, energetic, melancholic, neutral | ✅ 100% |
| mood_vector | 5-axis probabilities: happy, sad, aggressive, relaxed, party |
◯ AB-only |
| representative_segment_start | Highest-energy 30s window — 0.0 for 30-second previews (most of the catalog); non-zero only on full-length uploads | ✅ 100% |
| onset_rate | Onset events per second — higher = more rhythmic / percussive | ◯ AB-only |
| dynamic_complexity | Loudness variation across the track (dB) — higher = wider dynamic range | ◯ AB-only |
| tuning_frequency | Estimated A4 reference in Hz — modern recordings cluster near 440 | ◯ AB-only |
| average_loudness | Normalised average loudness [0–1] — perceptual-loudness reference | ◯ AB-only |
| mbid | MusicBrainz recording ID for cross-referencing other databases | ◔ ~6% |
| isrc | International Standard Recording Code | ◑ ~75% |
| is_remix + remixer + mix_name + remix_of_isrc | Remix labelling parsed from the title — is_remix (true/false), the remixer credit when attributable, the mix_name version label (e.g. Extended Mix, Alesso Remix), and remix_of_isrc — the ISRC of the original recording a remix is based on, so you can jump straight to the source. Populated for catalog remix/version rows; null on fallback layers. |
◔ remix rows |
| genre | Genre tag — primarily iTunes, then AcousticBrainz and Last.fm | ◕ ~96% |
| feature_source | Provenance label for the row: essentia_preview (analysed from a 30s preview by this pipeline — bpm/key/meter via Essentia, loudness via librosa, valence/energy/danceability via librosa-derived heuristics), acousticbrainz (MBID-matched, July 2022 dump), fma, msd, or user_upload. See the migration thresholds guide for per-field numerical implications when comparing to Spotify. |
✅ 100% |
| album_name + release_date | Album or single title and ISO 8601 release date | ◕ ~99% |
| duration_ms | Full track length in milliseconds | ◕ ~99% |
| explicit | Boolean explicit flag from catalog | ◕ ~97% rising |
| source + first_ingested_at | Which chart/source first surfaced this track and when | ✅ 100% |
Coverage snapshot — 2026-07-17. Percentages measured against our 886,691-track own catalog. ✅ populated on every catalog row. ◕ ≥80% — fills via our MusicBrainz enrichment pass. ◑ 50–80%. ◔ <50% — fills gradually via MusicBrainz resolution at lookup time plus Deezer enrichment on chart-source ingests. ◯ AB-matched only — populated where the track's MusicBrainz ID exists in the AcousticBrainz July-2022 dump (a partial slice of the current catalog; the MBID-bearing share has been diluted as the catalog has grown from 50k → 886k+ via on-demand backfill). These are research-grade descriptors with no active backfill path — the AB-matched slice is frozen, so its share of the catalog gradually declines as the catalog grows. The low-coverage fields above are different: they keep rising over time via MusicBrainz resolution at lookup time plus Deezer enrichment on chart-source ingests.
No per-request billing surprises. Pay monthly, cancel anytime. Free keys are instant — paid plans activate immediately on checkout.
🌍 Prices are shown in your local currency and charged in that currency at checkout — switch any time with the currency selector at the bottom-left.
Build a proof of concept. No card required.
/bulkPerfect for side-projects and early-stage apps.
/bulkShip your integration. Enough headroom for a live product.
/bulkHigh-volume apps, teams, and production workloads.
/bulkUnlimited volume, dedicated infrastructure, and full catalog licensing.
Cost calculator
Drag the slider. We compare what you'd pay on FreqBlog versus the closest commercial alternatives at the same monthly volume.
Comparison rates pulled from each provider's published pricing in 2026: AudD ($5/1k → $3.60/1k volume tier), MusicAPI.com (€189/12k → €890/120k tiers, ~€7.40–€15.75 per 1k), Cyanite (custom pricing — listed at indicative $0.01/track).
Currency conversion via open.er-api.com.
Three independent limits per plan — all visible on your dashboard, and every limit response tells you how long to wait.
| Plan | Requests / month | Rate limit* |
|---|---|---|
| Free | 1,000 | 50 / min |
| Hobbyist | 15,000 | 300 / min |
| Starter | 150,000 | 600 / min |
| Pro | 750,000 | 600 / min |
*The per-minute rate limit applies to the upload endpoints /analyze and /identify. Lookup endpoints (/lookup, /bpm, /key, /bulk, /v1/audio-features) are quota-only. Monthly quota resets on the 1st; /identify costs 2 requests per call.
/lookup for a track that isn’t in the catalogue yet gets analysed on demand and added to the catalogue — we email you when it’s ready. There’s no daily cap; you’re bounded only by your monthly quota (1,000 lookups/mo on Free). Upgrade for a higher quota.
| Response | Meaning | What to do |
|---|---|---|
200 | Found — full data returned. | — |
202 | Not in the catalogue yet — queued for on-demand analysis (~15s, occasionally up to a minute under load). This is a success, not an error. | Add ?wait=N (up to 25s) to /lookup to hold the request and get the track back inline (200) in the same call — otherwise retry shortly (honours Retry-After), or wait for the email / dashboard notice when ready. |
404 | No match anywhere (and no title to ingest from). | Check spelling, or try ?isrc= / ?mbid=. |
422 | A required parameter is missing or malformed — e.g. no track, bpm, or ids supplied. | Add the parameter the endpoint expects; the detail field names it. |
429 | A limit hit — per-minute rate, monthly quota, or the per-key /lookup concurrency cap. | Honour the Retry-After header and back off; if it's the monthly quota, upgrade or wait for the 1st. |
401 | Missing or invalid API key. | Send X-Api-Key or ?key=; check the key email (incl. spam). |
413 | Upload over the 15 MB /analyze / /identify limit. | Trim to a ~30s clip or lower the bitrate. |
415 | Unsupported / unreadable audio file. | Use MP3/WAV/OGG/FLAC/AAC–M4A; re-export if corrupt. |
503 | Genuine service unavailability — not normal back-pressure. Slow batches no longer 503: /bulk, /bulk-csv & /v1/audio-features all return a 200 with partial results (uncharged). | Check the status page; retry shortly. |
Send any clip, get back the title, artist, MusicBrainz ID and ISRC of the recording it represents — plus the full 42-field FreqBlog audio analysis when the track is already in our catalog. Powered by Chromaprint fingerprinting and the AcoustID open dataset of 60M+ recordings.
Multipart upload, MP3 / WAV / FLAC / AAC / OGG, max 15 MB. Returns within ~2 seconds for a 60-second clip. Audio is held in /dev/shm (RAM tmpfs) for fingerprinting and unlinked the moment Chromaprint returns — no audio is written to durable storage.
When the matched MBID resolves to a track in our catalog, the response carries the full audio_features object — BPM, key, mood, valence, danceability, AcousticBrainz descriptors. No second round-trip needed.
/identify charges 2 quota requests (vs 1 for the rest), so on Pro that's £0.34 per 1,000 identifications. AudD's lowest tier is $5/1k. Same Chromaprint fingerprinting, same AcoustID dataset.
CPU-bound work runs through the per-tier semaphores already protecting /analyze — Free 1, Hobbyist 2, Starter 3, Pro 6, Enterprise 8 concurrent. No surprise contention with your other endpoints.
$ curl -F "[email protected]" \ -H "X-Api-Key: sk_live_..." \ https://api.freqblog.com/identify { "matched": true, "score": 0.94, "mbid": "5b11f4ce-a62d-471e-81fc-a69a8278c7da", "isrc": "CAUM71900002", "track_name": "Blinding Lights", "artist_name": "The Weeknd", "in_catalog": true, "audio_features": { "bpm": 85.39, "bpm_alt": 170.78, "key": "F-Minor", "camelot": "4A", "mood": "tense", ... } }
When the recording isn't in our catalog yet, audio_features is null — call /lookup with the returned name + artist to queue an on-demand analysis — add ?wait=N (up to 25s) to get the features back in the same call.
Pass a track id, get the 10 most acoustically similar tracks in the catalog. Cosine similarity over an 18-feature audio embedding — BPM, key, energy, loudness, danceability, valence, mood and AcousticBrainz descriptors. No model to host. No vector DB to manage.
A single matrix multiply over the entire audio-feature index returns ranked recommendations faster than your network round-trip. No vector DB, no ANN tuning, no infrastructure to babysit.
Per-feature z-score normalisation runs over real values only, so a track with 8 real features doesn't get false-matched to one with only 2. Tracks with too few real features are filtered from results entirely.
exclude_same_artist=true drops other tracks by the seed's artist — ideal for "more like this, but different" radio feeds and discovery lanes.
/recommendations replacementSpotify removed /v1/recommendations and related-artists in Nov 2024 — we shipped both back. /recommendations blends up to 5 seed tracks into one centroid and returns nearest tracks re-ranked by genre affinity; /related-artists derives an artist graph from the same index — no editorial listening data needed.
Cyanite charges enterprise rates for similarity tagging. We return cosine similarity over a fully transparent 18-feature embedding for the same £0.17 per 1,000 requests as everything else.
$ curl https://api.freqblog.com/similar?track_id=1488408568&limit=3 \
-H "X-Api-Key: sk_live_..."
{ "seed": { "track_name": "Blinding Lights", "artist_name": "The Weeknd" },
"count": 3,
"results": [
{ "score": 0.94, "track": { "track_name": "Don't Start Now", "artist_name": "Dua Lipa" } },
{ "score": 0.91, "track": { "track_name": "Levitating", "artist_name": "Dua Lipa" } },
{ "score": 0.89, "track": { "track_name": "Save Your Tears", "artist_name": "The Weeknd" } }
]
}
Pass any itunes_track_id from a /lookup, /search, or /bpm response. Counts as one quota request regardless of result count.
Generate a Pioneer Rekordbox XML, M3U, or plain-text cuesheet from any list of catalog ids — with BPM, Camelot key, and a hot cue at the chorus already baked in. Pair with /radio to build a harmonic playlist in one chained call.
Greedy walk over the similarity index that respects Camelot wheel adjacency and BPM continuity. Pass a seed and get a 30-track playlist that flows like a real DJ set, no ML model to host.
Full Rekordbox 1.0.0 XML with AverageBpm, Tonality, and a POSITION_MARK cue at the highest-energy 30-second window — that's your "Chorus" hot cue out of the box.
Extended M3U for Serato / Traktor / Engine DJ / VirtualDJ, or a paste-friendly plain-text cuesheet for chats and notebooks. Same data, three formats, one quota request.
Real-time top-songs feeds for 45 countries, sourced from Apple Music RSS and cross-referenced against the catalog so you can pipe a chart directly into /lookup, /similar, or your /export chain. Cached 12 hours.
Other APIs hand you raw key and BPM and leave the mixing to you. We score the transition itself. /transition rates any two tracks 0–100 on Camelot-wheel key fit, octave-aware BPM proximity (half/double-time counts) and energy smoothness, with a plain-English reason. /next-track ranks a seed’s sonic neighbours by that score. POST /setlist orders a whole crate (2–100 tracks) into a peak_time / warmup / cooldown energy arc and hands back a flow_score — then chain into /export/rekordbox to drop the set straight into your decks.
# 1. Generate a 20-track harmonic playlist from a seed $ curl "https://api.freqblog.com/radio?seed_track_id=1488408568&n=20" \ -H "X-Api-Key: sk_live_..." # 2. Pipe its track ids straight into a Rekordbox XML download $ curl "https://api.freqblog.com/export/rekordbox?track_ids=$IDS" \ -H "X-Api-Key: sk_live_..." -o playlist.xml # 3. Drag playlist.xml into Rekordbox → ready to mix. # Each TRACK already has BPM, Tonality (Camelot key) and a Chorus cue point.
# Score how cleanly one track mixes into another $ curl "https://api.freqblog.com/transition?from_track_id=1488408568&to_track_id=1871990263" \ -H "X-Api-Key: sk_live_..." { "score": 99, "components": { "harmonic": 100, "tempo": 96, "energy": 99 }, "reason": "4A→4A same key, 85→85 BPM (-0.44), energy -0.004" } # Order a crate into a peak-time set, then export it to Rekordbox $ curl -X POST "https://api.freqblog.com/setlist" \ -H "X-Api-Key: sk_live_..." -H "Content-Type: application/json" \ -d '{"track_ids": ["1488408568","1871990263","1527115454"], "arc": "peak_time"}' $ curl "https://api.freqblog.com/export/rekordbox?track_ids=$ORDERED_IDS" \ -H "X-Api-Key: sk_live_..." -o set.xml
Quota: /radio and /transition cost 1 request each, /next-track counts as 3 and /setlist as 5 — charged only on a served result. Track audio is referenced via a synthetic freqblog://<id> location — relink to your local library after import using your DJ software's built-in tools.
Missing genre, release date, or ISRC? The API queues a background job to enrich the track. Your search returns instantly — the data arrives via email and dashboard.
On every incomplete track, the worker intelligently enriches and merges results by field authority. Searches return instantly — enrichment happens in the background.
Get an email when a track you searched for is enriched. The notification includes the track name, artist, and which fields were populated. Above the per-day cap (5 free / 20 paid), notifications roll up into a single end-of-day digest — bulk catalog work won't flood your inbox.
See all your enriched tracks via the API. Query /backfill/notifications to list completed backfills with timestamps and updated fields.
The backfill job runs in the background — your search response is instant. Backfill jobs use exponential backoff and rate limiting, so they never hammer external APIs or impact response times.
How it works: Search /lookup for a track with missing fields (e.g., genre is null). The API returns the partial result instantly and queues a background job. The worker enriches the track, updates the database, and logs a notification. Per-track emails arrive within ~2 minutes; on a heavy catalog pass they roll up into a single end-of-day summary instead, so a big backfill never spams your inbox. Query /backfill/notifications anytime to see completed enrichments.
📖 Full guide: For API endpoints, integration examples, and troubleshooting, check the API documentation.
The core fields — BPM, key, energy and loudness — come from peer-reviewed signal-processing libraries (MTG-Essentia, librosa) used in academic music research. The perceptual descriptors (mood, valence and the timbral estimates) are lighter signal-analysis heuristics — useful, but calibrate before you rely on them. Audio is processed in memory and never written to disk.
Beat-tracking algorithm from the Music Technology Group at Universitat Pompeu Fabra. Returns a track-level confidence score alongside the BPM — so you know how reliable each result is. Time signature is inferred from the detected beat grid (defaults to 4/4 when beats are too sparse).
Chromagram-based key estimation using the HPCP (Harmonic Pitch Class Profile) algorithm. Returns human-readable key names (e.g. "A-Minor") with a 0–1 confidence score.
Root Mean Square energy normalised to 0–1, plus mean loudness in dBFS — the standard digital full-scale reference.
Six audio descriptors computed via spectral flatness, zero-crossing rate, MFCC vocal formants, dynamic-range ratio, and tempo analysis. The four timbral estimates (acousticness, instrumentalness, liveness, speechiness) are coarse approximations — calibrate per use-case. Mood is derived from the MIREX four-quadrant model — valence vs. energy.
On full-length uploads, a sliding window finds the highest-energy 30-second segment — pinpointing the chorus or drop. For the 30-second previews that make up most of the catalog it returns 0.0 (the preview is already the clip). Cross-references recording databases for MBID and ISRC codes used in rights tracking.
Release metadata captured at ingest time — no extra lookup required. Every catalog track includes album, release date, full track length, and explicit flag alongside the audio features.
When a track isn't in our live catalog, we look it up in curated datasets of pre-analyzed tracks. Returns mood, mood_vector (5-axis dimensional scores), genre, acousticness, and instrumentalness alongside BPM and key. These fields are also backfilled for catalog tracks when a match is found; they are null when no match exists.
13,000+ Creative Commons tracks from public archives — used to compute valence and other emotional descriptors not derivable purely from local signal analysis. Acts as enrichment for any result missing valence, and as a standalone fallback for indie/CC catalog.
1 million tracks from 1955–2011 with full audio features including tempo, key, mode, loudness, energy, and danceability. Ideal for classic rock, jazz, pop, and historic catalog not covered by the live ingest. Matched by artist + title name lookup.
Zero-footprint pipeline — uploaded audio is processed in memory and discarded the moment analysis completes. Nothing is written to disk and no audio is retained.
Paste one URL into Claude, Cursor, or Windsurf — zero install, no API key — and your AI looks up BPM, key, mood, and genre as a first-class tool. No code required. Prefer a local server with the full 23-tool set? The music-metadata-mcp npm package has you covered too.
Paste the hosted connector URL. No key, no install — shared monthly cap. Start ↓
Free key + the npm package — 1,000 req/mo, no card, full 23 tools. Get a key
Direct HTTP / SDK from £0.17 / 1,000 — webhooks, bulk CSV, SLAs. See pricing →
In Claude, open Settings → Connectors → Add custom connector and paste the URL below. No npm, no API key — it works in Claude (web, desktop & mobile), Cursor and Windsurf.
https://mcp.freqblog.com/mcp
Cursor — {"mcpServers":{"freqblog-music":{"url":"https://mcp.freqblog.com/mcp"}}}
Windsurf — same, but the field is serverUrl.
Or run it locally — npm package, your own key, full 23 tools
{
"mcpServers": {
"music-metadata": {
"command": "npx",
"args": ["music-metadata-mcp", "--api-key=sk_live_YOUR_KEY"]
}
}
}
{
"mcpServers": {
"music-metadata": {
"command": "npx",
"args": ["music-metadata-mcp", "--api-key=sk_live_YOUR_KEY"]
}
}
}
Once connected, your AI can answer questions like:
Hosted connector — 12 tools
The hosted connector exposes 12 tools — the core six (audio features, batch, search, BPM & key discovery, harmonic keys) plus transition scoring, next-track, setlist & recommendations — zero install. The music-metadata-mcp npm package (npx or npm i -g) adds the full 23-tool set: harmonic radio, DJ export, country charts, lyrics, waveforms & more.
Connector reference — what each tool does
get_audio_features — Audio features for one track: BPM, key (name + Camelot + Open Key), energy, danceability, valence, acousticness, instrumentalness, mood, genre, time signature & more. Identify by name+artist, ISRC, MusicBrainz ID, or Spotify ID. Read-only.get_audio_features_batch — The same features for up to 50 tracks in one call — ideal for analysing a whole playlist. Read-only.search_catalog — Full-text search by track / artist / album to resolve a fuzzy or partial name into a concrete track before looking up features. Read-only.find_tracks_by_bpm — Find catalog tracks near a target tempo (± tolerance), each with full features — for DJ sets and workout playlists. Read-only.find_tracks_by_key — Find tracks in a given musical key (Camelot, Open Key, or key name) for harmonic, key-locked playlists. Read-only.find_compatible_keys — Given a Camelot key, return the harmonically compatible keys for mixing (same key, relative major/minor, adjacent ±1, and optionally ±7 energy boost/drop). Pure music theory, no quota. Read-only.Setup
https://mcp.freqblog.com/mcp (include the /mcp path). No account or API key.mcpServers.Troubleshooting
/mcp path, and add it as a custom / remote connector (not a local one). It's authless — no key is required.search_catalog first.isrc, mbid, or spotify_id for an exact match.bpm is the raw value (e.g. 85 for “Blinding Lights”); the perceptual tempo (~171) is in bpm_alt. This is by design.music-metadata-mcp npm package.Still stuck? Email [email protected].
The boring infra-team checklist that often blocks production. Webhooks signed with HMAC-SHA256, single-file Python and Node SDKs, bulk CSV enrichment, and a plain-language Security & Data Handling page covering GDPR posture, subprocessors and DPA contact.
Configure an HTTPS callback via POST /me/webhook and we'll POST a JSON event when your on-demand ingests (ingest.completed/.failed) and backfills finish — so a pipeline is pushed the result (with a result_url) instead of polling. Each request is signed with a timestamp-bound X-FreqBlog-Signature: sha256=… — reject anything else.
~200 lines. httpx only. curl -O https://freqblog.com/sdk/freqblog.py and you're done. Includes a one-line verify_webhook() helper.
~150 lines. Zero dependencies on Node 18+ (uses native fetch). CJS + ESM. Same verifyWebhook() contract as the Python.
POST a CSV with artist,title columns; get the same CSV back with 26 audio-feature columns appended. Up to 500 rows per call. No new schema to learn — just enrich whatever CSV you already have.
All requests authenticated with a single header. Interactive OpenAPI docs at /docs.
https://api.freqblog.com
X-Api-Key: <your_key>
application/json
https://api.freqblog.com/docs
X-Api-Key: your_key_here — Request your free key to get started.
limit + offset.8A). Always includes same, relative, adjacent_up, adjacent_down; with extended=true also energy_boost (+7) and energy_drop (-7). No quota, no auth.embedding, embedding_mask (which positions are real vs filler), and fields (positional field names). Pass an itunes_track_id from any /search or /lookup response.[{ms, text}] — no LRC math client-side. Cached 30 days per (track, artist).currentColor. Sizes 120×20 to 2400×400. Buckets cached 90 days — subsequent renders take ~1 ms.{track, score} pairs. Pass exclude_same_artist=true for cross-artist discovery feeds. Tracks with too few real features (< 6) are filtered from both seeds and candidates.isrc, mbid, spotify_id or catalog track_id. A tag-shaped projection of /lookup (no upload, no new compute). Every tag carries a confidence (measured / derived / model-estimated / catalog-genre) and a provenance, so you know exactly what’s measured vs estimated. Counts as 1 quota request./v1/recommendations replacement. Blends 1–5 seed track ids into a feature-space centroid and returns the nearest catalog tracks re-ranked by genre affinity — so a feature-close cross-genre track doesn’t outrank same-genre picks. Each result carries the raw audio-feature cosine score (genre drives the order, so the list isn’t strictly score-descending). Optional exclude_seed_artists=true. Counts as 2 quota requests.related artists with score, match_count and a sample_track_id. Counts as 2 quota requests.in_catalog=true entries carry a full TrackStub./export/rekordbox for a ready-to-mix XML.score, the harmonic/tempo/energy components and a plain-English reason. Counts as 1 quota request.score, components and reason. Tune with min_score, bpm_drift and max_key_distance. Counts as 3 quota requests.{"track_ids": […], "arc": "peak_time"} (2–100 ids; arc = peak_time/warmup/cooldown/flat). Returns the play order, per-step transition scores and an overall flow_score — pipe the ordered ids into /export/rekordbox. Counts as 5 quota requests.rekordbox (Pioneer XML 1.0.0 with hot cues), m3u (Extended M3U8 for Serato/Traktor/Engine DJ/VirtualDJ), or cuesheet (plain text). Up to 200 tracks per call.artist,title columns and receive an enriched CSV with all 26 audio-feature columns appended. Up to 500 rows per request. Each row counts as one quota request.ingest.completed, ingest.failed, backfill.completed). POST {"url": "https://..."} to set (public https only), DELETE to clear. Each fired webhook carries X-FreqBlog-Timestamp + a timestamp-bound X-FreqBlog-Signature: sha256=<hex>; verify with the SDK helper.Example response — /lookup or /analyze
{
"track_name": "Blinding Lights",
"artist_name": "The Weeknd",
"album_name": "Blinding Lights - Single", // catalog metadata
"itunes_track_id": "1488408568",
"isrc": "USUMV2403154",
"mbid": "1227659f-a933-4549-8fc4-d839b4cd3d45",
"is_remix": false, // true for a third-party remix; then remixer + mix_name populate
"remixer": null, // e.g. "Alesso" on a remix
"mix_name": null, // e.g. "Extended Mix" / "Alesso Remix"
"remix_of_isrc": null, // on a remix: the ISRC of the original it's based on
"release_date": "2019-11-29",
"duration_ms": 201570, // track duration in milliseconds
"explicit": false,
"source": "itunes_search", // source identifier
"first_ingested_at": "2026-04-05 07:39:31",
// ── tempo · key · loudness — the precise, deterministic fields ──
"bpm": 85.39, // raw detected tempo (never auto-doubled)
"bpm_alt": 170.78, // half/double-time candidate — use when the genre runs faster
"bpm_confidence": 3.6127, // higher = more confident; typical range 0–10
"key": "F-Minor",
"key_confidence": 0.9124, // 0–1
"mode": 0, // 0 = minor, 1 = major
"key_int": 5, // pitch class 0–11
"camelot": "4A", // harmonic mixing notation (Camelot wheel)
"open_key": "9m", // Open Key notation (Serato / Mixed In Key)
"loudness_db": -10.96, // dBFS integrated loudness
"time_signature": 4,
"mood": "tense", // happy · calm · sad · tense · energetic · melancholic · neutral
"genre": "synthwave",
// ── feel — useful, but approximate; calibrate to your use-case ──
"energy": 1.0, // 0–1 normalised RMS
"danceability": 0.7036, // 0–1
"valence": 0.4224, // 0–1; high = happy/euphoric
// acousticness, instrumentalness, liveness, speechiness also returned — coarse perceptual estimates; see the field guide before relying on them
"mood_vector": null, // 5-axis probabilities — populated when AB low-level present
"representative_segment_start": 0.0, // seconds into track; highest-energy 30s window
"onset_rate": null, // onset events per second (AB low-level)
"dynamic_complexity": null, // loudness variation, dB (AB low-level)
"tuning_frequency": null, // A4 reference, Hz (AB low-level)
"average_loudness": null, // normalised loudness (AB low-level)
"feature_source": "essentia_preview", // where the audio features came from
"backfill_status": null, // 'queued' if missing fields are being enriched
"backfill_notification_id": null
}
Multi-source fallback response (track not in live catalog)
Automatically resolved via an intelligent multi-source fallback chain. Fields populated vary by which source matched.
{
"track_name": "Windowlicker",
"artist_name": "Aphex Twin",
"bpm": 136.0,
"key": "F-Minor",
"camelot": "4A",
"open_key": "9m",
"danceability": 0.8142,
"acousticness": 0.0312, // spectral analysis
"instrumentalness": 0.9821, // vocal-detection score
"mood": "energetic", // derived from valence + energy
"mood_vector": {"happy": 0.18, "sad": 0.21, "aggressive": 0.64, "relaxed": 0.11, "party": 0.88},
"onset_rate": 3.42,
"dynamic_complexity": 4.18,
"tuning_frequency": 439.6,
"average_loudness": 0.812,
"genre": "electronic", // genre classification
"mbid": "b10bbbfc-cf9e-42e0-be17-e2c3e1d2600d",
"isrc": "GBAAN9800112",
"feature_source": "acousticbrainz"
}
Pick your language and drop it straight into your project.
★ Spotify-shape — host-swap, same response structure (values may need threshold-tuning)
curl "https://api.freqblog.com/v1/audio-features/0VjIjW4GlUZAMYd2vXMi3b" \ -H "X-Api-Key: your_key_here" # Returns Spotify's exact AudioFeaturesObject shape. # {id} auto-detects: 22-char Spotify ID or 12-char ISRC (hyphens optional). # NOTE: a raw Spotify ID only resolves tracks we've already mapped to one # (a minority of the catalog) — for full coverage use the ISRC, or # look up by name: GET /lookup?track=...&artist=... # Batch: GET /v1/audio-features?ids=id1,id2,... (up to 100)
Name-based lookup (instant for chart tracks)
curl "https://api.freqblog.com/lookup?track=Blinding+Lights&artist=The+Weeknd" \ -H "X-Api-Key: your_key_here"
Find tracks by BPM
curl "https://api.freqblog.com/bpm?bpm=128&tolerance=2&limit=10" \ -H "X-Api-Key: your_key_here"
Find tracks by key (Camelot, Open Key, or name)
curl "https://api.freqblog.com/key?q=8A&limit=10" \ -H "X-Api-Key: your_key_here" # Also accepts: ?q=1m (Open Key) or ?q=A-Minor (key name)
Upload for analysis (tracks not in catalog)
curl -X POST "https://api.freqblog.com/analyze" \ -H "X-Api-Key: your_key_here" \ -F "[email protected]"
★ Spotify-shape — host-swap, same response structure (values may need threshold-tuning)
import requests API_KEY = "your_key_here" spotify_id = "0VjIjW4GlUZAMYd2vXMi3b" # or an ISRC like "USUM71900001" resp = requests.get( f"https://api.freqblog.com/v1/audio-features/{spotify_id}", headers={"X-Api-Key": API_KEY} ) data = resp.json() print(f"tempo={data['tempo']} key={data['key']} mode={data['mode']}") # {id} auto-detects 22-char Spotify ID or 12-char ISRC. Same AudioFeaturesObject shape. # A raw Spotify ID only resolves tracks already mapped to one (a minority of the # catalog) — prefer the ISRC, or look up by name: GET /lookup?track=...&artist=... # Batch: GET /v1/audio-features?ids=id1,id2,... (up to 100; returns {"audio_features": [...]})
Name-based lookup
import requests API_KEY = "your_key_here" BASE = "https://api.freqblog.com" headers = {"X-Api-Key": API_KEY} resp = requests.get( f"{BASE}/lookup", params={"track": "Blinding Lights", "artist": "The Weeknd"}, headers=headers ) data = resp.json() print(f"BPM: {data['bpm']} (confidence: {data['bpm_confidence']:.2f})") print(f"Key: {data['key']} (confidence: {data['key_confidence']:.0%})")
Upload for analysis
with open("track.mp3", "rb") as f: resp = requests.post( f"{BASE}/analyze", headers=headers, files={"file": f} ) print(resp.json())
★ Spotify-shape — host-swap, same response structure (values may need threshold-tuning)
const API_KEY = 'your_key_here'; const spotifyId = '0VjIjW4GlUZAMYd2vXMi3b'; // or an ISRC like 'USUM71900001' const res = await fetch(`https://api.freqblog.com/v1/audio-features/${spotifyId}`, { headers: { 'X-Api-Key': API_KEY } }); const data = await res.json(); console.log(`tempo=${data.tempo} key=${data.key} mode=${data.mode}`); // {id} auto-detects 22-char Spotify ID or 12-char ISRC. Same AudioFeaturesObject shape. // A raw Spotify ID only resolves tracks already mapped to one (a minority of the // catalog) — prefer the ISRC, or look up by name: GET /lookup?track=...&artist=... // Batch: GET /v1/audio-features?ids=id1,id2,... (up to 100; returns {audio_features:[...]})
Name-based lookup
const API_KEY = 'your_key_here'; const BASE = 'https://api.freqblog.com'; const params = new URLSearchParams({ track: 'Blinding Lights', artist: 'The Weeknd' }); const res = await fetch(`${BASE}/lookup?${params}`, { headers: { 'X-Api-Key': API_KEY } }); const data = await res.json(); console.log(`BPM: ${data.bpm}, Key: ${data.key}`);
Upload for analysis
const form = new FormData(); form.append('file', fileInput.files[0]); const res = await fetch(`${BASE}/analyze`, { method: 'POST', headers: { 'X-Api-Key': API_KEY }, body: form }); console.log(await res.json());
Quick-start answers first, then the errors developers hit most often.
audio-features endpoint: GET /v1/audio-features/{id} accepts an ISRC or a Spotify track ID and returns BPM, key, energy, valence, danceability and more in the same JSON shape. One caveat: a raw Spotify ID only resolves if we've already mapped that track to a Spotify ID — today a minority of the catalog (~2.4%). It is not a universal Spotify-ID reverse lookup, and we can't resolve an unknown Spotify ID server-side. For reliable results, look up by name (GET /lookup?track=…&artist=… — searches the full catalog and queues an on-demand fetch on a miss) or by ISRC (GET /v1/audio-features/<ISRC>). Values are derived with MTG-Essentia rather than Spotify's models, so they're directionally compatible — re-tune any hard thresholds (see our field-by-field guide).
GET /lookup?track=…&artist=… — no audio file or ISRC required. The API resolves the track from its 886,691 pre-analysed catalog and returns BPM (with a half-time/double-time bpm_alt), key, Camelot, Open Key and 30+ other fields, typically in 25–40ms.
/lookup?isrc=…, /lookup?mbid=… (a MusicBrainz recording ID — the precise key when no ISRC exists, e.g. pre-1986 vinyl, or when a name match is ambiguous), or /lookup?spotify_id=…. The Spotify-shaped /v1/audio-features/{id} accepts an ISRC or a Spotify ID too. Note that a raw Spotify ID only resolves when we've already mapped that track to a Spotify ID (a minority of the catalog) — it isn't a universal Spotify-ID reverse lookup. For the widest coverage, look up by name (/lookup?track=…&artist=…) or, if your own Spotify integration already gives you the ISRC (external_ids.isrc), pass that — /lookup?isrc=… or /v1/audio-features/<ISRC>.
https://mcp.freqblog.com/mcp (Cursor uses a "url" entry, Windsurf "serverUrl"). See the connector setup. It's a free, shared service with a monthly cap; when the cap is reached you get a clear in-chat message to grab your own free key — 1,000 requests/month, no card — for a dedicated quota plus the full 23-tool set. For direct HTTP or SDK calls you do need a key: grab one free.
/lookup, /bulk, /v1/audio-features and the rest) through RapidAPI's gateway, billed on your RapidAPI account — coverage, fields and name-based lookups are identical to calling us directly. Prefer to onboard with us instead? The free tier and direct API keys work exactly as before — RapidAPI is simply a second way in for teams that standardise on it.
GET /transition?from_track_id=&to_track_id= scores how cleanly two tracks mix (0–100) from Camelot-wheel key fit, octave-aware BPM proximity (half/double-time counts as a match) and energy smoothness, and returns a plain-English reason like 11B→11B same key, 118→117 BPM (-0.29), energy +0.12. GET /next-track?seed_track_id=&n=10 returns the seed’s sonic neighbours re-ranked by that transition score (tune with min_score, exclude_same_artist, bpm_drift, max_key_distance). POST /setlist takes {"track_ids":[…],"arc":"peak_time"} (2–100 ids; arc = peak_time/warmup/cooldown/flat) and orders the crate into a beat-matched set with per-step scores and an overall flow_score. Chain the ordered ids into GET /export/rekordbox?track_ids=… to drop the set straight into Rekordbox or Serato. Track ids are the catalog itunes_track_id values from /search or any /lookup response. Quota: /transition 1 request, /next-track 3, /setlist 5 — charged only on a served result.
/radio walks the similarity index while keeping each step within your max_key_distance on the Camelot wheel and within your bpm_drift. (We fixed a latent bug on 25 June 2026 where it had been reading key data from un-enriched rows, so its harmonic filter was effectively inert and it matched on BPM continuity alone; harmonic adjacency is now genuinely applied.) For finer control over how individual transitions are scored, or to order an existing crate rather than generate a fresh playlist, use the Harmonic Set Builder endpoints (/transition, /next-track, /setlist) above.
/recommendations endpoint is gone?GET /v1/recommendations and GET /v1/artists/{id}/related-artists) — same auth and monthly quota as /lookup, charged only on a served result. GET /recommendations?seed_tracks=&limit=20 takes 1–5 catalog track ids, blends them into one feature-space centroid, returns the nearest catalog tracks and re-ranks them by genre affinity — so a feature-close but cross-genre track doesn’t outrank your same-genre picks. Each result carries the raw audio-feature cosine score; because genre drives the order, the list isn’t strictly score-descending. Add exclude_seed_artists=true to drop the seeds’ own artists. Costs 2 quota requests. GET /related-artists?artist=&limit=20 derives an artist graph from the same similarity index (there’s no editorial listening graph behind it, just as there wasn’t behind Spotify’s): the seed artist’s track-vector centroid → nearest catalog tracks → aggregated by artist, each scored on its top-3 track sims (so a prolific back-catalogue can’t dominate) with a same-genre lift and a cross-genre penalty. It returns related artists with a score, match_count and a sample_track_id, and costs 2 quota requests. Both are built on our own audio-feature index and catalogue genre data — not ListenBrainz or any third-party graph. seed_tracks and sample_track_id are the catalog itunes_track_id values from /search or any /lookup response.
GET /tag with the track — by name (track + artist), isrc, mbid, spotify_id or a catalog track_id — and you get a compact tag array: energy, danceability, valence, acousticness, instrumentalness, a mood label and a broad genre. It’s a tag-shaped projection of the same analysis /lookup already returns — no audio upload, no new compute — so it costs just 1 request, charged only on a served result. What sets it apart from opaque taggers (e.g. Cyanite) is that every tag tells you how we know it: a confidence of measured (our Essentia analysis, provenance: essentia), derived (a MIREX-quadrant mood from measured valence + energy), model-estimated (an AcousticBrainz mood-model probability, raw value in value) or catalog-genre (a broad catalogue tag). Numeric tags carry their [0,1] value; the mood category and genre are label-only (value: null). Example: /tag?track=Blinding Lights&artist=The Weeknd → measured high-energy (1.0), very-danceable and acoustic, a derived mood of tense, and a catalog-genre of synthwave. The broad, reliable coverage is the measured tags — available for the full analysed catalogue and for any track you look up by name (fetched and analysed on demand on a miss). You can also tag by mbid or isrc against 7.5M+ AcousticBrainz recordings when you supply that identifier. For the full numeric feature set use /lookup; for the nearest-sounding tracks use /similar.
/lookup call shortly — or skip the 202 next time by adding ?wait=N (up to 25s) to /lookup to hold the request and get the track back inline (200) in the same call. Looking up by ISRC (?isrc=…) or Spotify ID (?spotify_id=…) returns 404 instead — those identifier paths can't fall back to a name+artist search. If we've already tried a ?track= lookup and the track isn't on any streaming source we can analyse, repeat calls return 404 with terminal: true, outcome: "unavailable", a reason (not_on_streaming or no_preview_audio) and a Retry-After header (currently 7 days) rather than another 202 — so automated clients can stop polling a verdict that won't change yet (see "Why does a track come back 'not available'?" below). If a track truly can't be found (e.g. SoundCloud-only, no commercial preview anywhere), upload the audio file directly via POST /analyze — MP3, WAV, OGG, FLAC, and AAC/M4A up to 15 MB.
?track= lookup returns 404 with outcome: "unavailable" and reason: "not_on_streaming" (or "no_preview_audio" when a match exists but carries no preview clip). This isn’t an error or a matching gap on our side — the recording simply isn’t on a streaming service we can pull audio from, and no metadata provider can supply audio features without the audio. Everything with a commercial streaming release resolves in ~15 s (usually under a minute) via Intelligent Backfill. If you have the audio file yourself, you can still analyse it directly via POST /analyze (MP3, WAV, OGG, FLAC, AAC/M4A up to 15 MB).
X-Api-Key: your_key_here (preferred for production code) or the URL param ?key=your_key_here (convenient for quick browser/curl tests). Bearer tokens are not accepted. Key must be active; if you just signed up, check your inbox (including spam) for the key email. Test with curl "https://api.freqblog.com/lookup?track=Test" -H "X-Api-Key: your_key_here" or paste https://api.freqblog.com/lookup?track=Test&key=your_key_here straight into a browser.
OPTIONS from a non-allowlisted origin is rejected, so a key embedded in client-side JavaScript can’t be scraped and abused. Call the API from your backend instead — curl, an SDK, any server-to-server request (none of which use CORS, so none are affected) — or, if you’re building a browser app, proxy the requests through your own server so the key stays server-side. This is the same reason Stripe and other secret-key APIs don’t allow their keys to be used directly from the browser.
429 means you've hit one of them, not that anything is broken. (1) A per-minute rate limit (50/min Free • 300/min Hobbyist • 600/min Starter • 600/min Pro • 600/min Enterprise) applies to the CPU-bound endpoints — /analyze and /identify — only. /lookup, /bulk, /bpm, and /key are not rate-limited per-minute — they're bounded by your monthly quota and a per-key concurrency cap (point 4 below). (2) A monthly quota (1,000 / 15,000 / 150,000 / 750,000 requests for Free / Hobbyist / Starter / Pro) resets on the 1st of each month. (3) A per-key concurrency limit on /lookup — up to 6 requests in flight at once on Free (10 on paid); fire more in parallel and the extras get an instant 429 with Retry-After: 2 — pace your fan-out or use /bulk to resolve up to 50 tracks in one call. This is normal back-pressure, not an outage. Every 429 carries a Retry-After header, so back off for that many seconds; the per-minute-limited endpoints also return standard RateLimit-Limit/-Remaining/-Reset headers so you can pace before you hit the wall. Note that /identify costs 2 quota requests per call (an upstream AcoustID lookup is included); batch endpoints — /bulk, /v1/audio-features?ids= and /bulk-csv — charge 1 per track or row in the batch; every other endpoint is 1 per call. If you're hitting the monthly cap regularly, consider upgrading your plan.
200 with partial results. /bulk and /v1/audio-features?ids= hand back the tracks that finished and return the slow ones as null (or found:false with backfill_status:"processing" on /bulk). /bulk-csv behaves the same way — a slow file comes back as a partial CSV where resolved rows are enriched and any the deadline cut off carry found="processing" (also uncharged), with X-Bulk-Complete, X-Bulk-Rows-Processed and X-Bulk-Rows-Pending headers so you can spot a partial without scanning rows. Just re-submit the unfinished items to collect them. Either way the practical fix is smaller batches — ~15 tracks per /bulk call, ~25 IDs per /v1/audio-features?ids= call, or split a large /bulk-csv file into chunks. If every request fails (not just the miss-heavy ones), check the status page.
multipart/form-data file upload, not a URL or JSON — sending those returns 415.
An honest comparison of every alternative since Spotify deprecated audio_features, with migration code.
Three real failure modes — Unicode drift, search blind spots, naive result-picking — and how to fix each.
What you lost when AcousticBrainz shut down, what's still usable from the dump, and how to build on it.
This API is built using open-source signal processing libraries and public music databases released under permissive licenses. Audio analysis is powered by established music-information-retrieval research.
This API is not affiliated with, endorsed by, or sponsored by Spotify, Apple, or any other music platform. Artist and track names appear only as metadata and remain the property of their respective owners. No audio is stored or retained.