When you need to file a takedown over copyrighted music in someone else’s content, the complaint is only as strong as the evidence behind it: which recording, who released it, and exactly where it plays. This recipe runs a piece of offending content through AudD’s enterprise endpoint, then assembles a per-match evidence record — track identification plus precise start/end timestamps in the file — and renders it both as JSON (for your records and tooling) and as a human-readable summary (to paste into a complaint).
What you’ll build
A function that takes a URL or file for the offending content and returns an evidence pack: one record per recognized track, each with the recording’s identification (artist, title, album, label, ISRC, UPC, song_link, score) and its location in the content (file-absolute start and end timestamps), plus the source reference. You’ll render the pack two ways — structured JSON and a plain-text summary.
The enterprise endpoint is the right tool: it handles arbitrary-length content and finds every track rather than one. A complaint has to say when the recording plays, and each match answers that with start_seconds and end_seconds — the song’s file-absolute position in the content. recognize_enterprise requests accurate offsets by default, so those positions are precise; there’s no chunk-offset math to do.
AudD provides the identification data, not the legal filing. This recipe produces evidence — what was recognized and where. Deciding whether to file, drafting the complaint, and asserting ownership are yours to handle. The output is input to that process, not legal advice.
AudD provides the identification data, not the legal filing. This recipe produces evidence — what was recognized and where. Deciding whether to file, drafting the complaint, and asserting ownership are yours to handle. The output is input to that process, not legal advice.
Prerequisites
- An API token from dashboard.audd.io. Get your own token there; the enterprise endpoint must be enabled on it. ISRC and UPC in responses require a Startup plan or higher; they’re returned on enterprise calls.
- Python 3.10+ with the SDK: pip install audd
- The offending content as a URL or a file. AudD parses social URLs server-side, so you can pass a link to live content directly. https://audd.tech/example.mp3 works for a happy-path run.
Walkthrough
Step 1: Recognize the content
Run the content through recognize_enterprise — pass a URL, file bytes, or a path. It handles any length and returns a flat list[EnterpriseMatch]: one entry per recognized fragment, in time order, each carrying the precise start_seconds / end_seconds the evidence is built on (accurate offsets are requested by default).
Running this prints one line per recognized fragment. A recording that plays for several minutes produces a run of consecutive matches all naming it — Step 4 collapses that run into one evidence span. Clean content returns an empty list, not an error.
Always set limit during development. The enterprise endpoint bills per 12 seconds of audio processed. An unbounded call on a long video can produce hundreds of metered fragments. Start with limit=25 and raise it only when you understand the cost on your real inputs.
Always set limit during development. The enterprise endpoint bills per 12 seconds of audio processed. An unbounded call on a long video can produce hundreds of metered fragments. Start with limit=25 and raise it only when you understand the cost on your real inputs.
Step 2: The timestamps the evidence cites
The timestamps a reviewer verifies against the content are start_seconds / end_seconds: where this song plays in the content file, in seconds (e.g. 246.78 to 251.64). They’re file-absolute and go into the evidence record as-is. They’re None only when a fragment arrived without a usable position — skip those.
Two other fields are easy to mistake for them. start_offset / end_offset are the raw offsets the seconds are derived from: milliseconds within AudD’s internal 12-second scan fragment (0–~12000), not file seconds; you rarely need them. And timecode is the position inside the matched recording, not the content file — it tells you which part of the song was playing, so never use it to locate the infringement.
Step 3: Format an in-file position into HH:MM:SS
A complaint wants a clock time, not raw seconds. Format the file-absolute position:
This always gives a zero-padded HH:MM:SS, which reads cleanly in a filing even for content that runs past an hour.
Step 4: Collapse a held track into one evidence span
For evidence you want one record with a time range — “this recording plays from 00:04:06 to 00:06:18” — not ten near-identical records, one per fragment. Collapse the consecutive matches for a track into a single span: the start is the first match’s start_seconds, the end is the last match’s end_seconds. A new (artist, title) opens a new span; a repeat of the one you’re currently extending stretches its end.
A two-minute play becomes one span with a real start and end, which is exactly what a reviewer wants to verify against the content. The start_seconds guard and the end_seconds fallback are there because the SDK parses responses leniently: any field can be absent or None, and one positionless fragment shouldn’t sink the whole pack.
Step 5: Keep only the spans worth citing
Not every recognition is takedown-grade evidence. A defensible record points at a commercial release — one with a label and/or an ISRC/UPC. Filter to spans that carry those identifiers, and drop the rest (or route them to manual review).
A non-null label or a present isrc/upc is your signal that the match is a released recording you can name precisely. ISRC and UPC require a Startup plan or higher; if they’re null on every span, check your plan tier before assuming the content is unidentifiable.
Step 6: Assemble an evidence record per span
For each citable span, build a record with two halves: identification (what the recording is and who released it) and location (where it plays in the file). Add the source reference so the record stands on its own.
source is the URL or filename of the offending content — record it explicitly so each pack is self-describing. The location times are file-absolute: they come straight from each match’s start_seconds / end_seconds, so they point at the exact moment in the content a reviewer can seek to.
Step 7: Generate the pack end to end
pack is the structured evidence — store it as JSON alongside your case records.
Step 8: Render a human-readable summary
The JSON is for your systems. For the complaint itself, render a plain-text summary a human can read and a reviewer can verify against the content.
This produces something you can paste straight into a takedown form: each recording named, identified, and located by its position in the file.
What you get back
Every match in the flat list is one recognized fragment, in time order. The fields the evidence uses:
For the example file, a match might come back with start_seconds = 246.78 (00:04:06) and end_seconds = 251.64 (00:04:11) — the span the evidence record cites.
isrc and upc populate on enterprise responses for Startup-plan accounts and higher. A null ISRC/UPC doesn’t mean the match is invalid — label plus artist/title still identify the recording — but an ISRC makes the strongest record.
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 content URL or file wasn’t decodable audio/video. For evidence work this matters: a record built on content the API couldn’t actually read is worthless. Treat it as a hard failure for that source, not a silent empty pack.
- Connection errors (AudDConnectionError) — transient. Retry with backoff.
An empty records list (no citable matches) is a valid, distinct outcome from an error — it means the content recognized nothing commercial, so there’s nothing to file on.
Going further
- Re-running recognition re-meters every fragment. Generate the pack once per source and persist the JSON; regenerate the human-readable summary from stored JSON rather than re-scanning.
- Reading metadata fields beyond the ones above — including undocumented or beta fields the API returns — comes through each match’s model_extra. If a filing needs a field not listed here, read it off m.model_extra on the match.
- To find offending content in the first place — scanning uploads as they arrive — see Build a copyright scanner for user-uploaded content.
- For multi-hour content, see Enterprise cost optimization on trading coverage against metered chunks.
- Build a copyright scanner for user-uploaded content
- Enterprise cost optimization
- Standard, enterprise, or streams: how to choose
- Python SDK docs
- API reference

