Errors
All errors follow a consistent envelope: machine-readable error code,
human-readable message, and a trace_id for support.
Error envelope
402 Payment Required
{
"error": "insufficient_credits",
"message": "Your account has 2 credits remaining but this operation requires 10.",
"trace_id": "afa-3f8a9e2b",
"_meta": {
"credits_remaining": 2,
"latency_ms": 4
}
}
Status codes
| Status | Error code | Meaning | Fix |
|---|---|---|---|
400 | bad_request | Malformed request | Check params against reference |
400 | missing_id | Required path param missing | Include the ID in the URL |
401 | unauthorized | Invalid or missing API key | Check Authorization: Bearer header |
402 | insufficient_credits | Credit balance too low | Top up credit pack or upgrade plan |
404 | track_not_found | No match in any catalog | Check spelling, try ISRC instead |
429 | rate_limit_exceeded | Too many requests this minute | Back off using X-RateLimit-Reset |
429 | concurrency_exceeded | Too many concurrent requests | Queue requests to match plan concurrency |
500 | internal_error | Unexpected server error | Retry with exponential backoff; report trace_id to support |
503 | service_unavailable | Temporary capacity issue | Retry after 1–2 seconds |
Retry strategy
Retry 429, 500, and 503 with exponential backoff. Do NOT retry 400, 401, 402, or 404 — those won't succeed on retry.
python retry helper
import time, requests
def call_with_retry(url, headers, max_retries=3):
for attempt in range(max_retries):
r = requests.get(url, headers=headers)
if r.status_code < 400:
return r.json()
if r.status_code in (429, 500, 503):
wait = 2 ** attempt # 1s, 2s, 4s
time.sleep(wait)
continue
# Do not retry on 400/401/402/404
r.raise_for_status()
raise Exception("max retries exceeded")
Reporting issues
Every response carries an _meta.trace_id. If you need help debugging,
include the trace ID when contacting support — we can pull the full request log
and response from it.