The crate API is designed to be consumed without a browser. Keyless entry for evaluation, a self-describing index, teaching errors with copy-pasteable fixes, and honest-gap semantics so agents never parse an HTML error page.
For the umbrella hosaka story (substrate, emergence model, the fleet): hosaka.fm/agents →
No API key needed for the first call. 100 req/hr/IP. CORS *. Fails closed on 503 when the substrate is busy — handle it explicitly, never silently.
Artist name → a capped preview dossier. Enough to evaluate the shape, test your parsing, and confirm the cluster resolves.
GET /api/v2/preview/artist?q={name}Accepts a name, Discogs/MusicBrainz id, or any artist URL (Bandcamp, SoundCloud, Spotify, a website). Returns the canonical cluster_id + slug + locators.
GET /api/v2/resolve?q={name}Discography, bookings, press, emergence signals, rights, credits, placements — assembled server-side. Add ?fields=identity,discography to trim to facets you need.
GET /api/v2/artist/{cluster_id}cluster_id is nullable — but off-master artists (no Discogs/MusicBrainz identity) now resolve too: the cluster is name-minted deterministically (matched_on: name_mint). Only name-dark inputs stay null, never an error — handle that case.
GET /api/v2The root index is public and keyless. It returns the cold-start recipe, 22 documented resources (each with an eli5, auth notes, and a copy-pasteable example), 15 catalogued errors, and 7 task-oriented recipes. An agent can bootstrap its understanding of the API surface from a single call before acquiring a key.
Each resource carries an eli5, auth requirement, and a verbatim example call with expected response shape.
Catalogued codes carry hint and doc_url; param when the code declares a source parameter; next when the handler can build a corrected call. error is the only guaranteed field.
Stepwise recipes for common agent tasks: name → dossier, genre discovery, Bandcamp tracklist, track-title search, tastemaker lookup, and more.
OpenAPI 3.1 spec: /api/v2/openapi.json (public, no key). Generate typed client in one line:
Error bodies are designed to be machine-actionable. error is the only field guaranteed on every error body. Catalogued codes (those in the ERROR_CATALOG) also carry:
hint — what to do next, in human termsdoc_url — deep link to this code's docs pageparam — the specific request parameter that caused the failure (only when the code declares one)next — a copy-pasteable, fully-formed corrected call (only when the handler can build one)Live example — a keyed endpoint with no key (curl -s 'https://crate.hosaka.fm/api/v2/artist/x'):
Branch on error (lowercase snake_case), never on HTTP status alone — several codes share a status code. The error field is the only field guaranteed on every error body.
Unresolved lookups return HTTP 200 with present:false or a field with state:"honest_gap" — never a 404. Agents never need to parse error pages or handle unexpected status codes for "no data found" conditions.
Dossier facets carry a per-facet state — present, honest_gap, or degraded — along with the producer and refresh cadence behind it. Your agent knows what it can rely on and at what freshness, without guessing.
Every keyed 2xx response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. 429 responses carry Retry-After. Calls that return an error or 429 are never billed.
| Tier | Burst/min | Concurrency | Included/mo |
|---|---|---|---|
free | 30 | 1 | 10,000 |
indie | 60 | 2 | 15,000 |
studio | 300 | 3 | 100,000 |
scale | 600 | 5 | 500,000 |
sync | 1,000 | 10 | 2,000,000 |
The keyless preview endpoint is IP-scoped at 100 req/hr regardless of tier. Overage is dark behind CRATE_OVERAGE_ENABLED — until flipped, the hard cap is a 429 (no surprise bills). See /pricing for the full billing model.
npm install -g @hosaka-fm/crate-cli (v0.3.7). stdout = untransformed API JSON. stderr = diagnostics. These channels never mix. Pipe stdout to jq, redirect stderr to a log — they will never cross.
| Code | Meaning | Notes |
|---|---|---|
0 | ok | success — including honest-gap (present:false) bodies; never exit 1 for "no results" |
1 | usage error | bad args/flags; the error names the corrected invocation |
2 | auth error | missing/invalid API key (HTTP 401/402) |
3 | invalid input | the API rejected the input (HTTP 400); its hint is passed through |
4 | rate-limited | HTTP 429 — Retry-After surfaced on stderr; the CLI never auto-retries, the agent decides |
5 | server error | upstream server error (HTTP 5xx) |
6 | network error | DNS failure, timeout, connection refused |
7 | config error | config file unreadable or corrupt |
crate publishes a set of surfaces designed for agent consumption:
npx openapi-typescriptlastModified datescrate robot-docs — paste-ready agent handbook for pasting into a system promptcrate capabilities — machine-contract JSON listing every available command and flagnpm install @hosaka-fm/crate (v1.16.0) — a typed wrapper over the same REST API, generated straight from the OpenAPI spec. Full docs at crate-sdk.hosaka.fm.
As of 2026-08-04, the crate API (v2=2.47.0) exposes 22 documented resources on the self-describing index (GET /api/v2, public) — including title-first track search (GET /api/v2/tracks, with ?fuzzy and ?source=mb|bandcamp|all) — 15 catalogued errors — each carrying hint and doc_url; plus param when the code declares a source parameter and next when the handler can build a corrected call. error is the only guaranteed field. Two keyless surfaces are available without an API key: the preview endpoint (GET /api/v2/preview/artist?q=, 100 req/hr/IP, fails closed on 503) and the OpenAPI spec (GET /api/v2/openapi.json). Unresolved lookups return HTTP 200 with present:false — never 404. v1 is frozen (deprecation shim). Calls are billed on successful 2xx only; errors and 429s are never billed.