The crate catalogue, typed end‑to‑end.
A medium-thickness client for crate's cluster-first public music-catalogue API (/api/v2) — artist / label / festival dossiers keyed on the canonical cluster_id, where master and bandcamp are dimensions of the artist dossier. Typed methods, automatic retries, sparse ?fields=, and errors that teach. Built so a human reaches a green result in 60 seconds — and an AI agent recovers from failures using the error object alone.
Key concepts — the catalogue's vocabulary
crate isn't a CRUD API — it's an intelligence layer over the world's music metadata. A few ideas unlock everything else, so learn these first.
Why crate
Typed off the live spec
Every parameter and response is generated from crate's OpenAPI contract — full autocomplete, no any, no hand-written drift.
Resilient by default
Auto-retry on 429/5xx with full-jitter backoff that honours Retry-After, a per-attempt timeout, and a whole-call deadline.
Default-rich, opt-out
One call returns the full dossier; pass { fields } to trim it. Deprecation/Sunset headers are surfaced and 308 redirects are followed for you.
Errors that teach
Typed exceptions carry .kind / .code, a human .hint, and a copy-pasteable .next — and they're JSON-safe.
Agent-native
Forgiving inputs, branch-on-code handling, and a runtime-discoverable surface via index(), CRATE_RESOURCES, CRATE_ERROR_REGISTRY.
Lean
Dual ESM + CJS, zero runtime dependencies, tree-shakeable, types bundled. Node 18+.
Methods
The full client surface. Filter by namespace or auth tier, search, then select a method for its signature, endpoint, return type, and a copy-paste example.
Errors
Every failure throws a CrateError subclass. Branch on err.kind (the discriminant) and err.code — never the message. Select a kind for its fields and codes.
| Status | Becomes | Notes |
|---|
| Guard | Narrows to |
|---|
Configuration
Pass options to new Crate(opts); override any retry/timeout knob per call via the final RequestOptions argument (which also accepts an AbortSignal).
| Option | Type | Default | Meaning |
|---|
signal?: AbortSignal plus timeout, maxRetries, maxBackoffMs, maxRetryAfterMs, totalDeadlineMs, headers.
Retries fire only on 429 / 500 / 503 / 504 and transport faults, with full-jitter exponential backoff capped at maxBackoffMs, honouring a server Retry-After (clamped by maxRetryAfterMs), and the whole call is bounded by totalDeadlineMs. All read methods are idempotent. Drag the controls to see the worst-case backoff envelope:
Auth tiers
3crate is key-first. The SDK enforces tiers locally, so a missing credential fails fast with a typed error instead of a confusing runtime 401.
Types
The exported types that shape requests and responses. Every one is generated from the OpenAPI spec and bundled with the package (no @types/… needed). Expand for the field shape.
Agent guide
AI agents are a first-class consumer. The SDK is built so an agent succeeds first-try and recovers from any failure using the error object alone — no external docs, no message parsing.
Forgiving inputs
resolve() / artist() take a bare string and infer it: URL → url, discogs:/mbid: → locator, 64-hex → cluster, else → name.
Branch on code, not message
Switch on err.kind then err.code. err.hint says what's wrong; err.next is a corrected call you can run.
Lossless handoff
JSON.stringify(err) preserves the teaching payload (a plain Error → {}). It omits .raw and reduces .cause to {name,message}.
Don't double-retry
The SDK already retries 429/5xx with backoff. On a 429 read err.retryAfter / err.rateLimit — don't wrap calls in your own loop.
null is an honest gap
artistOrNull() returns null, and resolve() a null cluster_id (HTTP 200, present:false) when data is absent. Only 4xx/5xx throw.
Opaque ids
cluster_id and discogs_master_id are strings — pass them through verbatim, never numericize.
// mirrored by examples/agent.ts + AGENTS.md + llms.txt — all type-checked in CI so they can't rot.