---
title: "Interpreting the recognition score"
description: "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."
slug: "/resources/concepts/score-thresholds"
section: "concepts"
keywords: [audd, recognition score, confidence threshold, false positives, copyright]
---

# Interpreting the recognition score

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 as `81`.
- 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.

```json
{
  "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 `81` does 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: null` is a "no match," not a low score.** No match means no
> `score` to 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.

```python
# 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.

```python
# 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.

```python
# 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: null` and
  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`, and `upc` rather than thresholding the number in
  isolation.

---

**Related**

- [Build a copyright scanner for user-uploaded content](/resources/recipes/ugc-copyright-scanner)
- [Generate DMCA takedown evidence](/resources/recipes/dmca-takedown-evidence)
- [Recognition result fields](/resources/reference/result-fields)
- [API reference](https://docs.audd.io)