Home Pricing Docs Compare SDK Blog Changelog Status Get API Key →
Music API · Migration · Field guide

Migrating from Spotify Audio Features: a Field-by-Field Threshold Guide

May 2026 · Rewritten 26 September 2026 · 5 min read · FreqBlog

Rewritten 26 September 2026. Earlier versions of this guide compared our values with Spotify’s and offered a ?calibrate=spotify mode. To comply with Spotify’s developer terms we no longer use Spotify’s data to calibrate or compare values, so those comparisons and the calibrated mode are gone (?calibrate= is still accepted but ignored). What remains is the field mapping and one piece of advice: re-derive your thresholds on our values rather than copying numbers tuned for another service. See the changelog for the detail.

You swapped the host from api.spotify.com to api.freqblog.com. Your GET /v1/audio-features/{id} handler still parses the response unchanged — same path, same field names, same JSON shape. What is not the same is how the numbers are made: we compute ours from signal analysis (Essentia for BPM and key, our own descriptors for the rest) and, where a recording has them, from AcousticBrainz’s open classifiers. Same names, different measurements — so any hard-coded threshold in your code needs checking against our values before you rely on it.

If you haven't done the migration yet: start with Spotify Audio Features Is Dead. Here's What to Use Instead in 2026 for the landscape and the host-swap code. This post is the next step — making sure your numbers behave once the calls are flowing.

Field mapping

Every field Spotify’s audio_features object carried is present in our /v1/audio-features/{id} response under the same name. Some are null where we have no trustworthy source — your code should already handle a null numeric field.

Spotify fieldOur fieldWhat ours measuresNullable?
tempotempo (bpm on /lookup)Essentia RhythmExtractor2013. bpm_alt on /lookup flags likely half-time readings.No
keykey (0–11, −1 if undetected)Essentia KeyExtractor. /lookup adds the key name, camelot and open_key.No
modemode (0 minor / 1 major)Same extractor as key.Only on AcousticBrainz-only tracks
time_signaturetime_signatureAssumed 4 (4/4) for every track; not measured.No
loudnessloudness (dB)Loudness of the 30-second analysis window. Compare tracks relative to each other.No
energyenergyHow energetic the 30-second preview sounds, from a PANNs-based model (since 2026-10-12; it was RMS loudness before), on our own scale with a median near 0.65. Re-derive thresholds rather than reusing Spotify ones.No
danceabilitydanceability[0–1] on AcousticBrainz’s scale: AcousticBrainz’s trained classifier where the recording has it; otherwise a PANNs audio model trained on AcousticBrainz’s open labels. Re-derive any cut-off.No
valencevalenceMusical positiveness of the 30-second preview, from a model built on PANNs audio embeddings (Kong et al. 2020, CC BY 4.0), on our own scale (median about 0.48). It still partly tracks energy, so treat it as a sorting signal.No
acousticnessacousticnessAcousticBrainz’s trained classifier where the recording has it; otherwise a PANNs audio model trained on AcousticBrainz’s open labels.Rarely
instrumentalnessinstrumentalnessLikelihood that the 30-second preview has no vocals, from a PANNs-based model trained on Demucs vocal separation (since 2026-10-12). Spotify’s was whole-track, so a vocal song with an instrumental intro can score high; hip-hop beat instrumentals are its weak spot. 0.5 is a sensible cut-off.Yes — preview-level, and null where a track hasn’t been through the model yet
speechinessspeechinessNot served for our own analyses.Yes — null for our analyses
livenesslivenessNot served for our own analyses.Yes — null for our analyses

A more accurate analysis of the descriptor fields is in progress; when it ships, the null fields will start carrying values and the changelog will say so.

How to re-derive a threshold

  1. Pull a labelled sample from your own library. Take a few hundred tracks you already know the answer for — “danceable” vs not, acoustic vs electric — and fetch them in batches with GET /v1/audio-features?ids=id1,id2,… (up to 100 per call).
  2. Look at our distribution, not a remembered number. Sort the sample by the field and see where your two groups separate. That crossing point is your threshold. Percentiles work too: “top 20% by energy” transfers across any scale.
  3. Prefer ordering to absolute cut-offs for valence, danceability and acousticness: they rank tracks usefully, but no single value is a verdict on one track.
  4. Handle null. Drop speechiness and liveness from filters for now, and treat a null instrumentalness (a track the model hasn’t reached yet) as “unknown”, not 0.
  5. Don’t filter on time_signature. It is always 4: we don’t measure meter.

Identifiers

/v1/audio-features/{id} accepts an ISRC (with or without hyphens) or a Spotify track ID, URI or URL; /lookup?track=…&artist=… takes a name. A raw Spotify ID is resolved per request by matching the track and can miss when a title is shared by several artists, so for reliable coverage key your lookups by ISRC — your Spotify track object already carries it at external_ids.isrc — or by name. In a batch, IDs we can’t resolve come back null and are not charged.

Try the drop-in — free tier, no card

Get an API key in seconds; the first 1,000 lookups per month are free.

Get API Key →

Further reading