---
title: "Agent notes: custom catalogs"
description: "Gotcha notes for AI agents working with AudD custom catalogs — the access requirement, how a custom match looks different, and why uploads must not be blindly retried."
slug: "/resources/agents/custom-catalog"
section: "agents"
keywords: [audd, custom catalog, agent, upload, audio_id, idempotent, fingerprint]
---

# Agent notes: custom catalogs

Terse gotchas for an agent building against AudD custom catalogs. A custom
catalog is **your account's private fingerprint database** — songs you
upload that aren't in the 160-million-track public DB. Once a track is in
your catalog, ordinary recognition calls can match against it. Read these
before you generate upload or matching code.

## 1. It needs special access — an agent can't self-enable it

Custom-catalog upload goes to `POST https://api.audd.io/upload/`, but the
endpoint requires **special access on the account**. You can't turn it on
from code or from the dashboard.

> **If the task needs a custom catalog, it needs a human to request access
> first.** Access is granted by emailing **api@audd.io**. Don't generate
> code that assumes `/upload/` will work on a fresh account — it won't
> until access is enabled. Surface this as a prerequisite, not a runtime
> retry.

If you call `/upload/` without access, treat the failure as a setup
problem (escalate to the human), not something to loop on.

## 2. A custom match looks different — key off `audio_id`

When recognition matches a track from your custom catalog, the result is
**not** shaped like a public-DB match:

- `audio_id` — an integer, present and identifying **your** uploaded track.
  This is the field that says "this came from your catalog."
- `artist` / `title` — **may be `null`**, because the matched track is your
  private audio and AudD has no public metadata for it. Whatever you
  supplied at upload time (or nothing) is what you get.

```python
result = audd.recognize(source)
if result is None:
    ...  # no match at all
elif result.audio_id is not None:
    ...  # CUSTOM-catalog match — your track; look it up by audio_id
else:
    ...  # public-DB match — artist/title are real
```

> **Don't branch on `artist`/`title` to detect a custom match.** They can
> be null on a custom match and populated on a public match — branch on
> `audio_id` being present. Map `audio_id` back to your own records to
> recover the real metadata for your private tracks.

## 3. Never blindly auto-retry an upload

`/upload/` is **not safe to retry naively**. A retry after a timeout — when
the first upload actually succeeded — can create a **duplicate catalog
entry**, and now the same audio matches two `audio_id`s.

> **Make uploads idempotent on your side before you retry anything.** Track
> which tracks you've uploaded (by your own stable key / hash), check that
> registry before re-uploading, and only retry when you can prove the prior
> attempt didn't land. A generic "retry on network error" wrapper around
> `/upload/` is a bug here.

This is the opposite of the recognition endpoints, where a retry on a
transient failure is fine — recognition is read-only, upload mutates your
catalog.

## 4. Recognition against the catalog is the same call

There is no separate "recognize against my catalog" endpoint. You make the
**same recognition call** — `POST https://api.audd.io/` (or enterprise for
long files) with your account's token. The only difference is what's in
your account's fingerprint DB: with tracks uploaded, matches can come from
your catalog (returning `audio_id`) as well as the public database.

So the integration is two phases:

1. **Once, at setup** — upload your tracks to `/upload/` (idempotently;
   see above), with access already granted.
2. **Per request** — recognize normally; inspect `audio_id` to tell a
   custom match from a public one.

You don't pass a "catalog" flag on the recognize call. If your tracks
aren't matching, the question is whether they're in your catalog, not
whether you set the right recognition parameter.

## 5. Store the metadata you'll want back at upload time

Because a custom match can return `null` for `artist` / `title`, the
`audio_id` is often the *only* useful thing on the result. Plan for that
before you upload, not after.

> **Keep your own `audio_id` → metadata table.** Whatever you need on a
> match — title, internal asset ID, rights owner, a URL — store it keyed by
> the `audio_id` the upload assigns, so a later match resolves to something
> meaningful. AudD won't hold arbitrary metadata for you; the catalog is a
> fingerprint store, not a metadata store.

A match against your catalog then becomes a two-step lookup: AudD gives you
the `audio_id`, your table turns it into the record you actually care about.

## 6. Don't treat a custom miss as a public miss

When you've uploaded private tracks and a clip *doesn't* match, the result
is still a plain `result == null` — exactly the same no-match outcome as the
public DB. There is no separate "not in your catalog" signal.

So if you expected one of your uploaded tracks to match and got `null`,
debug the **catalog state**, not the call:

- Did the upload actually succeed (vs. a silent failure you retried into a
  duplicate, or skipped)?
- Is the clip long and clean enough to fingerprint? Custom tracks follow
  the same recognition constraints as public ones — a very short or very
  noisy clip may not match either database.

Don't add recognition parameters trying to "force" a catalog hit; there's
nothing to force.

## Quick checklist before you ship

- Access granted on the account (human emailed api@audd.io). ✗ → stop.
- Uploads are idempotent — you can't create duplicate `audio_id`s on retry.
- Match handling branches on `audio_id`, not on `artist`/`title`.
- You have an `audio_id` → metadata table for resolving custom matches.
- No-match (`result == null`) is handled the same for custom and public.

---

**Related**

- [Custom catalog vs. the public database](/resources/concepts/custom-vs-public-db)
- [Recipe: sample detection](/resources/recipes/sample-detection)
- [Result fields reference](/resources/reference/result-fields)
- [Upload audio endpoint (API reference)](https://docs.audd.io/upload_audio_endpoint)