Skip to content

Error handling

Errors always return JSON with a stable code field. Use code for branching in clients, not the HTTP status or the human message (the human message can change).

{
"error": "Human-readable explanation.",
"code": "rate_limited",
"details": {
"limit": 10,
"window_seconds": 3600
}
}
HTTPcodeWhenRetry?
400invalid_queryMissing / malformed query params.No — fix the request.
400invalid_slugSlug path param failed validation.No.
401auth_requiredNo Authorization header on a gated endpoint.No — attach the header.
401auth_invalidHeader malformed or key not recognised.No.
401auth_revokedKey has been revoked.No — provision a new key.
404not_foundSlug / id not in the registry.No.
429rate_limitedHit the public 10/hr or per-plan ceiling.Yes — wait until X-RateLimit-Reset.
500server_errorUnhandled upstream failure.Yes — exponential backoff, 3 attempts.
  • 4xx — fix the request, don’t retry. The same bad input will always 4xx.
  • 429 — sleep until X-RateLimit-Reset (Unix epoch seconds), retry once. If you hit 429 again, you’re under-provisioned — upgrade or authenticate, don’t loop.
  • 5xx — exponential backoff up to 3 attempts (1s, 2s, 4s). If a 5xx persists past 4 seconds, you’re better off surfacing a failure state than holding the UI hostage.
async function igRequest(path, init = {}, attempt = 0) {
const r = await fetch('https://api.igregulator.io' + path, init);
if (r.ok) return r.json();
const body = await r.json().catch(() => ({ code: 'parse_error' }));
const code = body.code ?? 'unknown';
if (r.status === 429) {
const reset = Number(r.headers.get('X-RateLimit-Reset')) * 1000;
const sleepFor = Math.max(0, reset - Date.now());
if (sleepFor < 5 * 60_000) {
await new Promise((res) => setTimeout(res, sleepFor + 1_000));
return igRequest(path, init, attempt + 1);
}
}
if (r.status >= 500 && attempt < 3) {
await new Promise((res) => setTimeout(res, 2 ** attempt * 1_000));
return igRequest(path, init, attempt + 1);
}
const err = new Error(body.error ?? r.statusText);
err.status = r.status;
err.code = code;
err.details = body.details;
throw err;
}