Errors

Every error response wraps a structured payload in the same envelope as success. Status code is in the HTTP response; machine-readable code is in error.code; human-readable message is in error.message. The meta envelope is unchanged from success responses — same request_id you would have gotten on a 200.

Envelope

{
  "error": {
    "code": "TIER_RESTRICTED",
    "message": "Endpoint /api/v1/area requires a Starter plan or above.",
    "status": 403
  },
  "meta": {
    "request_id": "req_8aZpmK4qX0nT",
    "timestamp": "2026-05-20T14:22:08Z",
    "response_time_ms": 4
  }
}

Always log request_id with your error handler — it lets us trace the call in our logs if you open a support ticket.

Status codes

StatusCodeWhen
400INVALID_BODYMalformed JSON, missing required field, or schema violation in the request body.
401UNAUTHENTICATEDMissing or invalid X-API-Key. (Account endpoints: missing or invalid Supabase JWT.)
402QUOTA_EXHAUSTEDFree tier's monthly call cap (10) reached. Upgrade or wait for the calendar-month reset.
403TIER_RESTRICTEDEndpoint requires a higher tier (e.g., /area on Free, /portfolio on Free, /facility/{id}/report on Starter).
404NOT_FOUNDRegistry ID does not exist in our index, address could not be geocoded, or user has no API key row yet.
422VALIDATION_ERRORParameter out of range (e.g., radius_miles > 25) or batch size over the tier limit.
429RATE_LIMIT_EXCEEDEDMonthly call limit hit, or demo IP burst limit (5/hour), or regenerate throttle (1/minute).
429REGENERATE_THROTTLEDPOST /account/key/regenerate called within 60s of the prior regenerate. Inspect the `Retry-After` header for the wait.
500INTERNAL_ERRORServer-side failure. Retry with exponential backoff. Cache misses are never surfaced as 500.

Retry guidance

  • 429 with Retry-After: honor the header. See Rate limits.
  • 500: server-side failure. Retry once after ~1s; if it fails again, file an issue with the request_id. Cache misses are never surfaced as 500.
  • 4xx other than 429: do not retry blindly — fix the input. The message field is human-readable for triage.