freqblog-com Spotify Compat API

Spotify-shape-compatible routes for endpoints deprecated 2024-11-27. Field names and response structure match Spotify byte-for-byte; numeric values come from this pipeline's open-source analysis and open datasets. Values are FreqBlog's own, not Spotify's — re-derive any thresholds on our values. See /v1/audio-features description and the Field-by-Field Threshold Guide at /blog/spotify-audio-features-migration-thresholds/.

Operations 2

GET /v1/audio-features/{identifier} Get audio features — Spotify-shape compatible #
GET /v1/audio-features Get audio features for several tracks — Spotify-shape compatible (batch) #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/freqblog-com:freqblog-com-spotify-compat-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

freqblog-com-spotify-compat-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Freqblog Com Spotify Compat API
  version: 1.5.0
  contact:
    name: FreqBlog
    url: https://freqblog.com/
    email: hello@freqblog.com
  license:
    name: Commercial — see freqblog.com/terms.html
    url: https://freqblog.com/terms.html
  description: 'Operations tagged Spotify-compat across 2 of this provider''s published API definitions: freqblog-com-openapi.json, freqblog-com-openapi1.json. Each path carries the servers of the definition it was published in.'
servers:
- url: https://api.freqblog.com
  description: Production
security:
- APIKeyHeader: []
tags:
- name: Spotify-compat
  description: Spotify-shape-compatible routes for endpoints deprecated 2024-11-27. Field names and response structure match Spotify byte-for-byte; numeric values come from this pipeline's open-source analysis and open datasets. Values are FreqBlog's own, not Spotify's — re-derive any thresholds on our values. See /v1/audio-features description and the Field-by-Field Threshold Guide at /blog/spotify-audio-features-migration-thresholds/.
paths:
  /v1/audio-features/{identifier}:
    get:
      tags:
      - Spotify-compat
      summary: Get audio features — Spotify-shape compatible
      description: '**Spotify-shape compatible replacement for the deprecated `GET /v1/audio-features/{id}`.** Change the host (`api.spotify.com` → `api.freqblog.com`) and the path + response field names match byte-for-byte: `acousticness`, `danceability`, `energy`, `instrumentalness`, `key` (0–11, `-1` if undetected), `liveness`, `loudness` (dB), `mode` (0/1), `speechiness`, `tempo` (BPM), `time_signature`, `valence`, plus `id` / `uri` / `track_href` / `analysis_url` / `duration_ms` / `type`.


        `{identifier}` is auto-detected: a 22-character base-62 string → Spotify track ID (also accepts `spotify:track:…` URIs and `open.spotify.com/track/…` URLs); a 12-character ISRC (e.g. `USUM71900001`, hyphens optional) → ISRC lookup (catalog first, then MusicBrainz resolves the recording on a miss). A Spotify track ID is resolved per request: we read the track''s **title** from Spotify''s public embed page and match it against our catalogue. Spotify IDs are never stored — a resolution is remembered in memory for at most 24 hours. A title that several catalog artists share is **ambiguous and returns 404 rather than a guess** — the wrong recording would mean the wrong bpm and key — so this is still not a universal Spotify-ID reverse lookup. For reliable coverage, identify tracks by **name or ISRC**: `GET /lookup?track=&artist=` searches the full catalog and queues an on-demand fetch + analysis on a miss; if your own Spotify integration already gives you the ISRC (`external_ids.isrc`), call this same route as `GET /v1/audio-features/` or `GET /lookup?isrc=`.


        Notes — the *values* are FreqBlog''s own measurements, on our own scale, not Spotify''s numbers: re-derive any thresholds on our values rather than copying Spotify''s (see the Field-by-Field Guide).


        `?calibrate=spotify` is deprecated and ignored (since 2026-09-26): values are always returned uncalibrated.


        `analysis_url` is always `null` (no per-segment-analysis analog). For tracks resolved by ISRC, `uri` / `track_href` are `null` and `id` carries the ISRC, since we have no Spotify ID for the recording. Error bodies use FreqBlog''s `{"detail": …}` shape, not Spotify''s `{"error": {…}}`. Counts as one quota request. For batches, use the Spotify-shaped sibling: `GET /v1/audio-features?ids=…` (comma-separated, up to 100 IDs).'
      operationId: spotify_audio_features_endpoint_v1_audio_features__identifier__get
      parameters:
      - name: identifier
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 128
          description: A Spotify track ID (22-char base-62, or a `spotify:track:…` URI / open.spotify.com URL) — or an ISRC (12-char, e.g. `USUM71900001`; hyphens optional).
          title: Identifier
        description: A Spotify track ID (22-char base-62, or a `spotify:track:…` URI / open.spotify.com URL) — or an ISRC (12-char, e.g. `USUM71900001`; hyphens optional).
      - name: calibrate
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: Deprecated and ignored since 2026-09-26 — accepted so existing clients keep working, but values are always returned uncalibrated, whatever the value. (It applied a loudness regression fitted to Spotify's own values, which Spotify's Developer Terms do not permit.)
          title: Calibrate
        description: Deprecated and ignored since 2026-09-26 — accepted so existing clients keep working, but values are always returned uncalibrated, whatever the value. (It applied a loudness regression fitted to Spotify's own values, which Spotify's Developer Terms do not permit.)
        deprecated: true
      - name: x-api-key
        in: header
        required: false
        schema:
          type: string
          title: X-Api-Key
      responses:
        '200':
          description: Audio features in Spotify's `AudioFeaturesObject` shape
          content:
            application/json:
              schema: {}
              example:
                acousticness: 0.04
                danceability: 0.76
                duration_ms: 200040
                energy: 0.91
                id: 0VjIjW4GlUZAMYd2vXMi3b
                key: 1
                loudness: -7.1
                mode: 0
                tempo: 171.0
                time_signature: 4
                track_href: https://api.spotify.com/v1/tracks/0VjIjW4GlUZAMYd2vXMi3b
                type: audio_features
                uri: spotify:track:0VjIjW4GlUZAMYd2vXMi3b
                valence: 0.51
        '401':
          description: 'Missing, unrecognised, deactivated or superseded API key, or a free key whose account can no longer hold one — `error_code` says which: `KEY_MISSING` / `KEY_DEACTIVATED` / `KEY_SUPERSEDED` / `KEY_FREE_UNAVAILABLE`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: No track found for that Spotify track ID / ISRC. **For an ISRC we could identify but don't yet hold, this 404 also queues an on-demand fetch — retry the same call in ~15-30s and it usually resolves.** A Spotify ID is resolved per request from the track's title (read from Spotify's public embed page) — so it 404s when nothing in the catalog carries that title, or when several artists share it and the right recording is ambiguous (we return 404 rather than guess). For reliable coverage, identify the track by name (`GET /lookup?track=<title>&artist=<artist>`, which fetches on a miss) or by ISRC (`GET /lookup?isrc=<ISRC>`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: '`identifier` is neither a 22-char Spotify track ID nor a 12-char ISRC'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Monthly quota exceeded, or too many concurrent single-ID lookups for this key (per-key in-flight cap shared with /lookup — free 6, paid 10). Back off per Retry-After, or send a large migration through the batch form `GET /v1/audio-features?ids=…`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: The resolve exceeded its internal deadline (kept below nginx's 30s) under load — transient. Retry per Retry-After, or use the time-boxed batch form `GET /v1/audio-features?ids=…` / `POST /bulk`, which return partial results instead of timing out.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    servers:
    - url: https://api.freqblog.com
      description: Production
  /v1/audio-features:
    get:
      tags:
      - Spotify-compat
      summary: Get audio features for several tracks — Spotify-shape compatible (batch)
      description: '**Spotify-shape compatible replacement for the deprecated `GET /v1/audio-features?ids=…`.** Up to **100** comma-separated identifiers per request. Returns `{"audio_features": [...]}` — the array length and order match the input; entries are `null` for IDs that don''t resolve (matching Spotify''s behaviour).


        Each `ids` entry is auto-detected exactly like the single-ID route: a 22-char base-62 string → Spotify track ID (also accepts `spotify:track:…` URIs and `open.spotify.com/track/…` URLs); a 12-char ISRC (e.g. `USUM71900001`, hyphens optional) → ISRC lookup. Identifiers that match neither shape fill their slot with `null` rather than failing the whole batch.


        As with the single-ID route, a Spotify ID is resolved per request by reading the track''s **title** from Spotify''s public embed page (Spotify IDs are never stored). Slots come back `null` when nothing in the catalog carries that title, or when several artists share it and the right recording is ambiguous — we return `null` rather than guess, since the wrong recording would mean the wrong bpm and key.


        **A `null` ISRC slot now queues an on-demand fetch.** When we can identify the recording behind an ISRC but don''t hold it yet, the slot comes back `null` (and uncharged) *and* an ingest is queued — so **re-sending the same call in ~15-30s typically returns the data**. Retrying a null-heavy batch once is worthwhile; it wasn''t before. Spotify-ID slots can''t do this: an unresolvable Spotify ID has no track name to fetch by. For reliable coverage, identify tracks by **name or ISRC**: `GET /lookup?track=&artist=` searches the full catalog and fetches on a miss, and ISRCs (e.g. from your own Spotify integration''s `external_ids.isrc`) resolve via the same batch route or `GET /lookup?isrc=`.


        **Quota — one token per ID that returns data.** Each resolved ID counts as one quota request; IDs that come back `null` — an unresolvable Spotify ID, an ISRC with no match, or one not resolved before the time-box — are **not** charged, exactly like the single-ID route, which is free on a 404 miss. So a 100-ID batch of raw Spotify IDs where only 5 resolve bills 5, not 100. Spotify billed a 100-ID batch as 1 call; we bill per *resolved* ID, because each resolved lookup runs its own catalog query (and potentially a MusicBrainz roundtrip on an ISRC). See `/pricing` for higher tiers.


        Other notes from the single-ID route apply: the *values* are FreqBlog''s own measurements, on our own scale (see the Field-by-Field Guide); `analysis_url` is always `null`; errors use FreqBlog''s `{"detail": …}` shape, not Spotify''s `{"error": {…}}`.


        **Resolved in parallel server-side, bounded for stability.** A large batch of uncatalogued IDs can take a while (each ISRC miss is a MusicBrainz roundtrip); if it exceeds the server time-box you get **partial results** — IDs resolved in time carry features, the rest come back `null` (and aren''t charged) and resolve on a retry. A small number of `/bulk` + `/v1/audio-features` batch requests per key run at once — excess get `429` with `Retry-After`.'
      operationId: spotify_audio_features_batch_endpoint_v1_audio_features_get
      parameters:
      - name: ids
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: Comma-separated identifiers (max 100). Each is either a Spotify track ID (22-char base-62; also accepts `spotify:track:…` URIs and `open.spotify.com/track/…` URLs) or an ISRC (12-char, e.g. `USUM71900001`; hyphens optional). Only IDs that resolve to data are charged — unresolvable IDs return `null` for free. No raw Spotify IDs? Identify tracks by name via `GET /lookup?track=<title>&artist=<artist>`.
          examples:
          - USUM71900001,003vvx7Niy0yvhvHt4a68B,USUMV1902060
          title: Ids
        description: Comma-separated identifiers (max 100). Each is either a Spotify track ID (22-char base-62; also accepts `spotify:track:…` URIs and `open.spotify.com/track/…` URLs) or an ISRC (12-char, e.g. `USUM71900001`; hyphens optional). Only IDs that resolve to data are charged — unresolvable IDs return `null` for free. No raw Spotify IDs? Identify tracks by name via `GET /lookup?track=<title>&artist=<artist>`.
      - name: calibrate
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: Deprecated and ignored since 2026-09-26 — accepted so existing clients keep working, but values are always returned uncalibrated, whatever the value. (It applied a loudness regression fitted to Spotify's own values, which Spotify's Developer Terms do not permit.)
          title: Calibrate
        description: Deprecated and ignored since 2026-09-26 — accepted so existing clients keep working, but values are always returned uncalibrated, whatever the value. (It applied a loudness regression fitted to Spotify's own values, which Spotify's Developer Terms do not permit.)
        deprecated: true
      - name: x-api-key
        in: header
        required: false
        schema:
          type: string
          title: X-Api-Key
      responses:
        '200':
          description: 'Batch audio features in Spotify''s `{audio_features: [...]}` shape; entries are `null` for IDs that don''t resolve (Spotify''s behaviour)'
          content:
            application/json:
              schema: {}
              example:
                audio_features:
                - acousticness: 0.04
                  danceability: 0.76
                  energy: 0.91
                  id: 0VjIjW4GlUZAMYd2vXMi3b
                  key: 1
                  mode: 0
                  tempo: 171.0
                  type: audio_features
                  uri: spotify:track:0VjIjW4GlUZAMYd2vXMi3b
                  '...': (remaining Spotify fields)
                - null
                - id: USUM71900001
                  key: 5
                  tempo: 85.39
                  type: audio_features
                  track_href: https://api.freqblog.com/lookup?isrc=USUM71900001
                  '...': (remaining Spotify fields)
        '400':
          description: '`ids` missing, empty, or longer than 100. Supply `?ids=<comma-separated Spotify track IDs or 12-char ISRCs, up to 100>`. No raw Spotify IDs? Identify tracks by name via `GET /lookup?track=<title>&artist=<artist>`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: 'Missing, unrecognised, deactivated or superseded API key, or a free key whose account can no longer hold one — `error_code` says which: `KEY_MISSING` / `KEY_DEACTIVATED` / `KEY_SUPERSEDED` / `KEY_FREE_UNAVAILABLE`'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Monthly quota insufficient, or too many concurrent batch requests for this key (shared with /bulk)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Reserved for genuine service unavailability. The server time-box no longer fails a slow batch — IDs unresolved in time come back `null` in the 200 response (and aren't charged); retry to collect them.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    servers:
    - url: https://api.freqblog.com
      description: Production
components:
  schemas:
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    ErrorResponse:
      properties:
        detail:
          type: string
          title: Detail
        error_code:
          anyOf:
          - type: string
          - type: 'null'
          title: Error Code
      type: object
      required:
      - detail
      title: ErrorResponse
      description: 'A JSON error. `detail` is for humans, `error_code` is for code.


        ⚠ Branch on `error_code`, never on `detail`. `detail` is prose we deliberately keep

        improving — five of these messages were reworded on 2026-08-13 alone — so matching on

        it is a breaking change waiting to happen. `error_code` is UPPER_SNAKE, always present

        on an error, and its meaning is frozen once shipped. Full registry: app/errors.py.'
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ErrorResponse_2:
      properties:
        detail:
          type: string
          title: Detail
        error_code:
          type:
          - string
          - 'null'
          title: Error Code
      type: object
      required:
      - detail
      title: ErrorResponse
      description: 'A JSON error. `detail` is for humans, `error_code` is for code.


        ⚠ Branch on `error_code`, never on `detail`. `detail` is prose we deliberately keep

        improving — five of these messages were reworded on 2026-08-13 alone — so matching on

        it is a breaking change waiting to happen. `error_code` is UPPER_SNAKE, always present

        on an error, and its meaning is frozen once shipped. Full registry: app/errors.py.'
  securitySchemes:
    APIKeyHeader:
      type: apiKey
      in: header
      name: X-Api-Key
x-refined-from:
- freqblog-com-openapi.json
- freqblog-com-openapi1.json