Interpreting the recognition score
Every AudD match carries a confidence score. How to read it and how to set application-specific thresholds for auto-accept, blocking, and human review.
Every match AudD returns carries a score. This page explains what the
score is, what it is not, and — the part that actually matters — how to turn
it into a decision: when to auto-accept a match, when to block on it, and
when to send it to a human. It’s for anyone building a flow where the cost of
a wrong match isn’t symmetric, such as copyright filtering or a “now playing”
display.
TL;DR
- A match includes a
score— an integer you can treat as a 0–100 confidence indicator, where higher means AudD is more confident in the match. Real responses return values such as81. - The score is not a probability and not a percentage of how much of the song matched. It’s a relative confidence signal, useful for ranking and thresholding.
- A match is only returned when AudD is confident enough to return one at all. The score lets you apply a stricter bar on top of that.
- The right threshold is application-specific. A copyright-blocking flow should set a high bar (false positives are expensive). A “now playing” widget can set a low bar (a wrong song is harmless).
- Don’t hard-code a number you read in a blog post. Calibrate on your own data — run real inputs through the API, look at the scores on known-good and known-bad matches, and pick the cut points that fit your tolerance.
Why this matters
Recognition gives you a yes-or-no answer: either a match comes back, or
result: null does. But “a match came back” and “I should act on this match”
are not the same thing. The cost of acting on a wrong match varies enormously
by application.
Consider two flows that both call the same endpoint:
- A copyright filter that blocks uploads containing a matched track. A false positive here blocks a creator’s legitimate content — a real harm, possibly an appeal, possibly a lost user. You want to be quite sure before you act.
- A “now playing” widget that shows the listener what song is on. A false positive shows the wrong title for a few seconds. Nobody is harmed; the next match corrects it. You’d rather show something than nothing.
These two flows want opposite behavior from the same score, and that’s the
whole point of having it. The score lets one codebase make confident,
low-risk decisions on high scores and route the ambiguous middle differently
depending on what a mistake costs. Without it, you’d treat every returned
match identically — which over-acts in the strict case and under-shows in the
lenient one.
What the score is
The score is an integer that accompanies each match and behaves as a
confidence indicator on a 0–100 scale: a higher score means AudD is more
confident this is the right song. You’ll see concrete values like 81 in
real responses. Use it the way you’d use any confidence signal — to rank
candidates and to set a cutoff.
{
"status": "success",
"result": {
"artist": "Imagine Dragons",
"title": "Warriors",
"album": "Smoke + Mirrors (Deluxe)",
"score": 81,
"song_link": "https://lis.tn/Warriors"
}
}
Two properties make it useful:
- Higher is more confident. Between two matches, the higher score is the one AudD is more sure of.
- It’s comparable across your own results. Within your application, on your kind of inputs, the score lets you separate the matches you trust from the ones you don’t.
What the score is not
Reading too much into the number leads to brittle code. The score is not:
- Not a probability. A score of
81does not mean “81% chance this is correct.” It is a confidence indicator, not a calibrated likelihood, and you shouldn’t multiply it, average it as if it were a percentage, or feed it into a formula that assumes it’s a probability. - Not a percentage of the song that matched. It doesn’t say “81% of the audio lined up.” Don’t describe it to users as “81% match.”
- Not an absolute quality grade you can compare across applications. A cutoff that works for your clean studio uploads may not transfer to someone else’s noisy field recordings. Treat the number as meaningful relative to your own inputs, calibrated on your own data.
Don’t surface the raw score to end users as a “match percentage.” It isn’t one. If you show confidence at all, map it to coarse buckets you’ve defined (“confirmed” / “likely”) rather than printing the integer as a percent.
A match is already a confident answer
It helps to know what the score sits on top of. AudD only returns a match
when it’s confident enough to return one — below that internal bar you get
result: null, a clean “no match,” not a low-scoring guess. So the matches
you receive have already cleared a floor.
The score lets you raise that floor for your own purposes. You are not
salvaging matches AudD would have rejected; you’re deciding which of the
matches it did return are confident enough for your use. For a lenient
display you might accept essentially everything that comes back. For a strict
filter you add your own, higher bar on top of AudD’s.
result: nullis a “no match,” not a low score. No match means noscoreto read — there’s no result object. Handle “no match returned” and “match returned with a low score” as two distinct branches in your code.
Choosing a threshold
There’s no single correct number, because the cost of being wrong differs by application. Pick thresholds by working backward from that cost.
Step 1: decide what a false positive costs you
- Expensive (blocking content, charging a fee, sending a legal notice): bias toward a high threshold. Better to miss a real match than to act on a wrong one. Route the uncertain middle to human review.
- Cheap (showing a label, logging a play, populating a widget): bias toward a low threshold. A wrong match is self-correcting and harmless; showing nothing is the worse outcome.
Step 2: calibrate on your own data
Run a representative batch of your real inputs through the API and record the scores, separated into matches you know are correct and matches you know are wrong (or content you know is clean). Look at where the two groups fall. Your auto-accept cutoff goes high enough that almost everything above it is correct; your auto-reject cutoff goes low enough that almost everything below it is wrong. The gap between them is your human-review band.
Recalibrate when your inputs change — a new capture device, a new content category, a shift from clean uploads to noisy recordings can all move the scores.
Step 3: implement the bands
The numbers below are illustrative, not official. AudD does not publish or endorse a specific cutoff. These values exist to show the shape of a thresholding policy; replace them with numbers you derived from your own data in Step 2.
# Illustrative thresholds — calibrate these on your own inputs.
AUTO_ACCEPT = 80 # at or above: act automatically
REVIEW_FLOOR = 50 # between floor and accept: send to a human
def classify(result):
if result is None:
return "no_match" # AudD returned no match at all
if result.score >= AUTO_ACCEPT:
return "accept"
if result.score >= REVIEW_FLOOR:
return "review"
return "reject" # low confidence; treat as no actionable match
The three-way split — accept / review / reject — is the general shape. A lenient application can collapse it to two (accept almost everything that comes back); a strict one keeps the review band wide and the accept bar high.
Worked example
The same recognition result, two applications, two policies.
A strict copyright filter
You scan uploads and block ones containing copyrighted music. A false positive blocks a legitimate creator, so the bar is high and anything short of high confidence goes to a human rather than to an automatic block.
# Illustrative — calibrate against your own labeled scans.
BLOCK_AT = 85 # auto-block only when very confident
REVIEW_AT = 55 # below block, above this: human moderator decides
def copyright_decision(result):
if result is None:
return "publish" # nothing recognized
if result.score >= BLOCK_AT:
return "block" # high confidence: auto-block
if result.score >= REVIEW_AT:
return "review" # ambiguous: send to a moderator
return "publish" # low confidence: don't act on a weak match
Here the score earns its keep in the middle band: matches that came back but
aren’t confident enough to block automatically get a human look instead of a
wrong automated decision. Combine the score with other signals from the
result — a non-null label, a present isrc/upc (returned on enterprise
calls and on Startup plan or higher) — to make the block decision sharper than
score alone.
A lenient “now playing” display
You show listeners the current track on a radio stream. A wrong title for a few seconds costs nothing and the next match fixes it, so the bar is low: show whatever comes back, and only suppress the genuinely weak matches to avoid obvious nonsense.
# Illustrative — a lenient display wants a low bar.
SHOW_AT = 40 # show almost anything that comes back
def now_playing(result):
if result is None or result.score < SHOW_AT:
return None # show nothing rather than a weak guess
return f"{result.artist} — {result.title}"
Same score field, same API. The copyright filter treats 81 as
“interesting but verify”; the now-playing widget treats it as “good enough,
show it.” The difference lives entirely in your thresholds, set from the cost
of being wrong in each context.
Common mistakes
- Hard-coding a number you read somewhere as “the recommended threshold.” There isn’t an official one. Calibrate on your own inputs and revisit it when those inputs change.
- Treating the score as a probability or a percentage match. It’s a confidence indicator, not a calibrated likelihood and not “how much of the song matched.” Don’t do arithmetic on it that assumes either.
- Using the same threshold for a blocking flow and a display flow. The cost of a false positive is the whole input to the decision; reuse the endpoint, not the cutoff.
- Confusing a low score with no match. No match is
result: nulland carries no score at all. A returned match with a low score is a different branch — handle them separately. - Showing the raw integer to users as “N% match.” Map it to coarse, meaningful buckets instead.
- Relying on score alone for high-stakes decisions. For copyright, combine
it with
label,isrc, andupcrather than thresholding the number in isolation.
Related
Reading this as an AI agent? The raw Markdown is at concepts/score-thresholds.md, and the full index is /resources/llms.txt.
