Beta founding offer: lock in 50% off any plan for life, before the beta ends. Claim founding pricing →
ResourcesIntegration GuidesIdentify a Song from an Audio File with One API Call

Identify a Song from an Audio File with One API Call

Post a Chromaprint fingerprint, or the raw audio, to /v1/tracks/identify and get the matched catalogue track back, with its ISRC, genre, and cross-platform links. A Shazam-style recognition API.

What identify does

POST /v1/tracks/identifytakes an audio fingerprint (or a raw audio file) and returns the catalogue tracks it matches, best match first. It's the request-response, one-shot counterpart to a live stream monitor: give it a few seconds of a recording and it tells you what the recording is. Under the hood it matches against a Beatport catalogue and AcoustID; it's a single fingerprint lookup with a tight error-tolerance ceiling, so a returned match is a confident one.

It's a paid-tier endpoint; the free tier gets a 403. The response is deliberately minimal (id, title, artists, confidence); you hydrate the full metadata in a cheap second call once you know which track matched.

Two ways to call it

There are two modes, chosen by Content-Type. They resolve to the same matcher and return the same shape:

  • Fingerprint mode (JSON): you run Chromaprint locally, then POST the resulting integer array. Roughly 1 KB on the wire, a flat 10 credits. This is the mode to prefer.
  • Raw-audio mode: POST the file bytes (up to 100 MB) with Content-Type: application/octet-stream and the server fingerprints it for you. Zero client-side setup, but it ships the whole file and costs 10 + ceil(MB).
💡A 320 kbps file already gives the best match we can produce, so sending a 40 MB lossless WAV buys you nothing and you pay for every byte of it. Send a fingerprint, or the whole track as a 320 kbps MP3, not a FLAC master. Trimming to a short clip is cheaper still, but the matchable section can sit anywhere in a recording, so a clip from the wrong part misses.
Build
1

Fingerprint the audio

fpcalcis Chromaprint's command-line tool: a single static binary, packaged for every platform. The -raw flag emits the fingerprint as a comma-separated integer array (plus the decoded duration) instead of the compressed base64 form:

bashfingerprint
# Chromaprint's CLI - one static binary, no service to run
$ fpcalc -raw track.mp3
DURATION=270
FINGERPRINT=1809491111,1809487015,1810011303, … ,1739372152
2

POST the fingerprint

Parse the FINGERPRINT= line into an array of numbers and send it as JSON. Pass fingerprint_duration when you have it, since it sharpens the match:

TypeScriptidentify.ts
import { execSync } from "node:child_process";

// Decode + fingerprint locally, then post ~1 KB of integers.
const raw = execSync("fpcalc -raw track.mp3", { encoding: "utf8" });
const line = raw.split("\n").find(l => l.startsWith("FINGERPRINT="))!;
const fingerprint = line.slice("FINGERPRINT=".length).split(",").map(Number);

const res = await fetch(`${BASE}/v1/tracks/identify`, {
  method:  "POST",
  headers: { "content-type": "application/json", "x-api-key": API_KEY },
  body:    JSON.stringify({ fingerprint, fingerprint_duration: 270 }),
});
const out = await res.json();
3

Read the ranked results

You get back matched, a best-first results array, and credits_charged. Each result carries a confidence from 0 to 1:

POST/v1/tracks/identify200 OK
{
  "matched": true,
  "results": [
    {
      "id": "315587019",
      "title": "Digital Love",
      "artists": [
        { "id": 1925615, "name": "Daft Punk", "is_primary": true, "is_remixer": false }
      ],
      "confidence": 0.97
    }
  ],
  "credits_charged": 10
}

Take results[0] as the answer. If you need a certainty threshold, gate on confidenceand treat a lone low-confidence result as “no match”.

4

Hydrate the full record

The match tells you which track; a second call tells you everything about it. Look it up by id for the ISRC, genre, duration, and label, and hit /v1/tracks/links for the platform links:

TypeScripthydrate.ts
// The identify response is slim - pull the full record by id.
const id = out.results[0].id;

const track = await fetch(`${BASE}/v1/tracks/${id}`, {
  headers: { "x-api-key": API_KEY },
}).then(r => r.json());

// track.isrc          → the representative ISRC (may be null)
// track.genre         → ["House", …]
// track.duration      → seconds
// track.releases[0]   → { title, label: { name }, release_date }

That's the same track shape the rest of the API returns, so from here you can render a “now playing” card, attach Listen On / Buy On buttons, or write the ISRC into your own store.

The zero-setup alternative

If you can't run Chromaprint where the audio lives (a serverless function, a locked-down mobile runtime), skip step 1 and POST the bytes directly:

TypeScriptupload.ts
// No Chromaprint on your side? Post the bytes and let the server fingerprint.
await fetch(`${BASE}/v1/tracks/identify`, {
  method:  "POST",
  headers: { "content-type": "application/octet-stream", "x-api-key": API_KEY },
  body:    await readFile("clip.mp3"),  // ≤ 100 MB · costs 10 + ceil(MB)
});

Same response, same hydration path. On a miss the server quietly retries deeper 30-second windows of your clip before giving up, so a recording that only becomes recognisable a minute in still resolves.

Handling misses and errors

  • No match. matched is false and resultsis empty. The recording isn't in the catalogue, or the clip was too short / too noisy to fingerprint.
  • 429.The request would cost more credits than you have left this month. It's rejected before any matcher work, so a denied identify never burns credits.
  • 503 with Retry-After.The matcher is briefly unreachable (e.g. mid-deploy). Wait the header's seconds and retry the same request; it's idempotent.
  • 413. The upload exceeded 100 MB. Send a shorter clip, or fingerprint it client-side and use the JSON mode instead.

Frequently asked questions

What audio formats can I send?

For the upload mode, anything ffmpeg can decode: mp3, flac, wav, m4a, mp4, ogg, opus. The format is sniffed from the bytes, not the extension or Content-Type. For the fingerprint mode it doesn't matter, because you've already decoded the audio locally with fpcalc; you're only sending the resulting integer array.

What's the difference in cost between fingerprint and upload?

A fingerprint is a flat 10 credits. An upload is 10 + ceil(MB) credits, billed on the full uploaded bytes, so a 4 MB clip costs 14. If you can run Chromaprint on your side, the fingerprint mode is both cheaper and lighter on the wire (roughly 1 KB versus the whole file).

What does the confidence score mean?

It's a 0–1 score derived from how closely the fingerprint matched, with 1.0 being an exact match. Results come back ranked best-first. In practice a real match sits high; treat a low-confidence single result with suspicion and, if you need certainty, cross-check the hydrated metadata (title, artist, duration) against what you expected.

Why doesn't the response include the ISRC directly?

The identify response is deliberately slim (id, title, artists, confidence) so it stays fast. Take the id of the top result and call GET /v1/tracks/:id to get the full record: ISRC, genre, duration, releases and label. GET /v1/tracks/links gets you the Spotify / Apple Music / Beatport / Tidal links.

Ready to build?

Free API key. No credit card. 1,000 requests to get started.

Get Free API Key
More in Integration Guides
Integration GuidesCursor Pagination Patterns: Walking Large Result Sets Safely5 min readIntegration GuidesA Discogs API Alternative With Streaming IDs and Bulk Lookup7 min read