Errors
Last verified: 2026-05-03 (Task #334 — docs accuracy pass).
Every error response uses the same envelope. The HTTP status and
error.code together describe the failure class, error.message is
human-readable, error.details is structured (when present), and
error.request_id matches meta.request_id and the X-Request-Id
header.
{
"error": {
"code": "bad_request",
"message": "lat is required and must be a finite number",
"details": { "field": "lat" },
"request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11"
},
"meta": {
"request_id": "7c0f3c2c-9e4a-4d29-b4a2-9c2b4e9f5c11",
"generated_at": "2026-04-25T18:34:00.000Z"
}
}
Error codes
| Code | HTTP | When |
|---|---|---|
api_key_missing | 401 | No Authorization and no X-API-Key header. |
api_key_invalid | 401 | Key prefix doesn't resolve, or hash didn't match. |
api_key_revoked | 401 | Key explicitly revoked by an admin. |
api_key_expired | 401 | Key's expires_at has passed. |
scope_required | 403 | Key valid, but missing the scope this endpoint requires (details.required). |
account_suspended | 403 | Developer account is suspended. |
rate_limited | 429 | Generic rate-limit rejection. |
rate_limit_exceeded | 429 | Specific quota dimension exceeded for this period (details.dimension). |
billing_limit_reached | 402 | Plan flatly excludes this feature. Upgrade required (details.dimension). |
bad_request | 400 | Malformed or missing query / body fields (details.field when applicable). |
route_invalid | 400 | /v1/weather/route payload failed schema validation. |
timeline_item_invalid | 400 | /v1/weather/timeline rejected an individual item (details.index, details.field, details.issues). |
not_found | 404 | Resource doesn't exist or doesn't belong to the calling developer account. |
provider_unavailable | 502 | Upstream weather provider failed (DNS, connection, 5xx). Retry with backoff. |
weather_refresh_timeout | 504 | Upstream provider timed out or rate-limited us. Retry with backoff. |
internal_error | 500 | Unhandled server-side defect. Please report with the request_id. |
Retry guidance
| Class | Retryable? |
|---|---|
4xx validation (bad_request, route_invalid, timeline_item_invalid) | No. Fix the request. |
4xx auth (api_key_*, scope_required, account_suspended) | No. Fix the credentials. |
402 billing_limit_reached | No. Upgrade plan. |
429 rate_limited / rate_limit_exceeded | Yes, after the period reset. See X-RateLimit-Reset. |
5xx provider_unavailable / weather_refresh_timeout | Yes, with exponential backoff. |
500 internal_error | Yes, with backoff. Open an issue if it persists. |
Even on a 5xx, the meta.billing_units field is non-zero — provider
failures still bill (we paid the upstream cost or the timeout cost).
Pure validation 4xxs do not bill.
Always-included details
error.request_id is present on every error and matches the same
field in meta and the X-Request-Id response header. Quote it when
filing support tickets — it makes it possible for us to find the
exact log line.