A recorded DJ set or continuous mix is one long file with dozens of tracks beat-matched and blended into each other. This recipe sends that file to AudD’s enterprise endpoint and produces a timestamped tracklist — the kind you’d post to a forum or a site like 1001Tracklists, with each track placed at the time it comes in.
What you’ll build
A script that recognizes a mix, reads the matches, and formats each distinct track as HH:MM:SS Artist — Title. You’ll also handle the thing that makes mixes harder than albums: tracks overlap. During a transition the outgoing and incoming track both fingerprint, so the server can match two different songs at the same moment. The recipe shows how to present those blends instead of dropping one side.
Each tracklist line is anchored at the moment its track comes in, and the SDK gives you that moment directly: recognize_enterprise returns a flat list[EnterpriseMatch], and each match carries start_seconds / end_seconds — its position in your mix, in file-absolute seconds. The call requests accurate offsets by default, so those positions are precise; there’s no chunk-offset math to do.
A one-off tracklist needs no code. The AudD CLI prints one: audd recognize set.mp3 --enterprise --limit none --tracklist. Build the version below when tracklists are part of your own product.
A one-off tracklist needs no code. The AudD CLI prints one: audd recognize set.mp3 --enterprise --limit none --tracklist. Build the version below when tracklists are part of your own product.
Prerequisites
- An API token from dashboard.audd.io. Get your own token there; the enterprise endpoint must be enabled on it. label, isrc, and upc come back on enterprise responses (ISRC/UPC require a Startup plan or higher) — useful if you want to enrich the tracklist, but not required for a basic one.
- Python 3.10+ with the SDK: pip install audd
- A mix file or URL. https://audd.tech/example.mp3 is a public, reproducible file you can run against end-to-end.
Walkthrough
Step 1: Recognize the mix
Hand the mix to recognize_enterprise as a URL, file bytes, or a path. However long the set runs, you get back a flat list[EnterpriseMatch]: one entry per recognized fragment, in time order, each with a precise start_seconds / end_seconds (accurate offsets are requested by default).
When you run this you’ll see one line per match, in time order. A track that plays for three minutes shows up as a run of consecutive matches naming the same song; transitions show up either as adjacent matches that switch from one song to the next, or — during a crossfade — as two matches sharing the same start_seconds, because the server matched both tracks in the same moment of the mix.
Always set limit during development. The enterprise endpoint bills per 12 seconds of audio processed. A 60-minute mix is hundreds of fragments; an unbounded call meters all of them. Run with limit=25 until your formatting and blend handling are right, then raise the cap for the full mix.
Always set limit during development. The enterprise endpoint bills per 12 seconds of audio processed. A 60-minute mix is hundreds of fragments; an unbounded call meters all of them. Run with limit=25 until your formatting and blend handling are right, then raise the cap for the full mix.
Step 2: Pick the right time field
Timestamps are the whole product here, so be precise about which field is which. Each EnterpriseMatch carries three kinds of position:
- start_seconds / end_seconds — where this track plays in your mix, in seconds (e.g. 288.0). This is the value you place the track at. It’s None only when a fragment arrived without a usable position.
- start_offset / end_offset — the raw offsets the seconds are derived from: milliseconds within AudD’s internal 12-second scan fragment, not file seconds. You rarely need them.
- timecode — position inside the matched track, not your mix. It tells you which part of the song was playing; never use it to place a track.
Place tracks by start_seconds, not timecode. start_seconds is the position-in-your-file; the song’s timecode is a position inside that song’s own recording. Mixing them up puts every track at the wrong time.
Place tracks by start_seconds, not timecode. start_seconds is the position-in-your-file; the song’s timecode is a position inside that song’s own recording. Mixing them up puts every track at the wrong time.
Step 3: Format the position into HH:MM:SS
A tracklist wants a clock time, not raw seconds. Format it:
This always gives a zero-padded HH:MM:SS, which sorts and aligns cleanly in a tracklist even for sets that run past an hour.
Step 4: Collapse a held track, but keep the transitions
Within a single track you get many consecutive matches naming it. Collapse those to one tracklist entry, anchored at the first match the track appears in. But when the track changes from one match to the next, that’s a real transition, not noise, and each new track earns its own line.
The rule: a new (artist, title) starts a new entry; the same (artist, title) as the entry you’re currently extending does not.
Any field on a match can come back absent or None (the SDK parses responses leniently), which is what the start_seconds guard is for: a fragment without a position gets skipped instead of crashing the scan.
This prints a tracklist like:
Anchoring each track at its first match is what you want for a tracklist: the listed time is when the track comes in, even though it keeps playing across the next several matches.
Step 5: Present blended transitions
In a real mix two tracks share airtime during a crossfade. The server can match both in the same ~12-second moment of the mix, and the SDK returns them as two separate matches in the flat list, sharing the same start_seconds, because they come from the same point in the file. That shared start time is the signal for a blend: the dominant track and the one bleeding in or out are pinned to the same instant. The Step 4 logic keeps only the first track at each new position, so it shows the cleaner cut. If you want a 1001Tracklists-style overlap marker, look for matches that share a start_seconds.
You can render those as a w/ (with) line, the way tracklist sites mark a blend:
A reasonable confidence floor (here score >= 50) keeps faint, incidental matches out of the transitions. Raise it if your mix is producing spurious “w/” lines; lower it if a known blend isn’t showing up. The (b.score or 0) guard handles a match whose score came back None.
The blend view here and the tracklist view from Step 4 read the same matches from one recognition call — you don’t pay twice. Both fall out of the flat list: the tracklist walks it in order, and a blend is simply the case where two matches land on the same start_seconds.
Step 6: Sample a long set when you don’t need every track
For a quick “what’s the rough tracklist” pass on a multi-hour set, sample instead of recognizing every fragment. Pass the sampling parameters to the same call:
every=1, skip=3 meters one fragment a minute, roughly a quarter of the audio. Most tracks in a set run longer than a minute, so you’ll still catch them — but short edits and quick doubles can slip through the gaps. Use a full pass (no skip, higher limit) when you want every track placed exactly.
What you get back
recognize_enterprise returns a flat list[EnterpriseMatch], one per recognized fragment in time order. The fields you use here:
The shared start_seconds is what makes blend handling possible: two tracks playing at the same moment of the mix come back as two matches pinned to the same file position, not something you have to correlate yourself.
Handling errors
The SDK raises typed exceptions; catch the ones you can act on.
- Authentication errors (AudDAuthenticationError) — bad or missing token. Fail at startup.
- Quota / subscription errors (AudDSubscriptionError) — request limit hit, or the enterprise endpoint isn’t enabled on your token. The enterprise endpoint and ISRC/UPC both depend on plan tier; surface these to the account owner rather than retrying.
- Invalid-audio errors — the mix URL or file wasn’t decodable. Treat it as a hard failure for that source, not a silent empty tracklist.
- Connection errors (AudDConnectionError) — transient; retry with backoff.
Quiet intros, ambient breakdowns, or a track not in the 160-million-song database simply produce no match for that stretch — that’s a gap in the tracklist, not an error.
Going further
- Persist the tracklist keyed by mix ID; re-running re-meters every fragment.
- Generating a music credits list for a podcast instead, where you want one credit per song with a time range? See Generate music credits for a podcast episode.
- For multi-hour sets, see Enterprise cost optimization on trading every/skip coverage against metered chunks.
- Generate music credits for a podcast episode
- Enterprise cost optimization
- Standard, enterprise, or streams: how to choose
- Python SDK docs
- API reference

