Error format
All errors return a consistent JSON structure:Status codes
| Code | Name | Description |
|---|---|---|
400 | Bad Request | Invalid request body, missing required fields, malformed parameters |
401 | Unauthorized | Invalid, expired, or revoked API key |
402 | Payment Required | No credentials provided. Response includes payment_options with x402 and API key signup details |
403 | Forbidden | Valid credentials, but insufficient scopes for this operation |
404 | Not Found | Resource doesn’t exist or belongs to another tenant |
409 | Conflict | Duplicate resource (e.g., duplicate slug) |
429 | Too Many Requests | Rate limited. See Retry-After header for when to retry |
500 | Internal Server Error | Something broke on our end. Logged and tracked automatically |
501 | Not Implemented | Feature exists in the API surface but isn’t live yet |
402 Payment Required
The402 response is unique — it includes payment options so clients can self-serve:
Rate limiting
When rate limited, the API returns429 Too Many Requests with standard headers:
| Header | Description |
|---|---|
Retry-After | Seconds until the limit resets |
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
