Errors
Every failure carries a stable machine-readable type and a message worth putting in a log.
Shape
{
"error": {
"type": "invalid_parameter",
"message": "Unknown parameter: stat. Supported: procedure, limit."
}
}Branch on type, never on message. Messages are written for humans and will be reworded; types are part of the contract and will not change without a version bump.
Types
| Type | Status | Meaning |
|---|---|---|
| missing_parameter | 400 | A required parameter was absent. The message names it. |
| invalid_parameter | 400 | A parameter was unrecognised or malformed. Unknown parameters are rejected rather than ignored, so a typo fails loudly instead of silently returning the wrong rows. |
| unauthorized | 401 | The key is missing, malformed, or revoked. |
| not_found | 404 | The resource exists as a concept but has no data for these arguments. |
| rate_limit_exceeded | 429 | Too many requests. Back off until the time in X-RateLimit-Reset. This is also what you get at your monthly ceiling — we stop rather than bill an overage. |
| internal_error | 500 | Our fault. Safe to retry with backoff. |
Which are worth retrying
- 429 and 500 — retry with backoffTransient. The same request will likely succeed later.
- 400 and 401 — do not retryThe request is wrong, or the key is. Retrying burns quota and changes nothing. Fix the caller.
- 404 — dependsThe endpoint is valid but has no data for those arguments. Retrying helps only if you expect the data to land later.