Getting Started
Every non-2xx response uses a consistent JSON envelope with a stable error slug, a human-readable message, and an optional structured detail block for programmatic handling.
{
"error": "quota_exceeded",
"message": "Daily quota of 10000 requests exhausted. Resets at 2026-04-21T00:00:00Z.",
"detail": {
"quota": "day",
"limit": 10000,
"reset_at": "2026-04-21T00:00:00Z"
}
}error is a stable machine-readable slug — switch on it in your client code. message is safe to display to end users. detail is present when there is structured context (e.g. which quota was hit, which field failed validation).
| Status | Error slug | Meaning |
|---|---|---|
| 400 | invalid_request | Query parameter out of range or malformed (e.g. hours > tier cap). |
| 401 | invalid_api_key | Missing or malformed X-API-Key header. |
| 401 | api_key_revoked | Key was revoked. Generate a new one from Settings. |
| 401 | api_key_expired | Key passed its expires_at. Generate a new one. |
| 402 | feature_not_in_plan | Endpoint or parameter requires a higher tier (state-level, forecasts, longer history). |
| 422 | window_out_of_plan | Requested history is older than your plan's window. detail.allowed.earliest gives the oldest reachable timestamp; upgrade for deeper history. |
| 422 | per_call_row_cap_exceeded | A single response would exceed your plan's per-call row limit. Narrow the window or add filters. detail.allowed.max_rows_per_call gives the ceiling. |
| 403 | ip_not_allowed | Request IP not in the key's allow-list. |
| 404 | not_found | Unknown endpoint or unknown resource identifier (e.g. bad state slug). |
| 429 | quota_exceeded | Daily or per-minute quota hit. See Retry-After. |
| 429 | export_cap_exceeded | Free-tier rolling 30-day cumulative row-export ceiling reached. detail carries used / cap / resets_at; capacity frees as usage ages out, or upgrade for a higher limit. |
| 500 | internal_error | Unexpected server error. Safe to retry after a short backoff. Includes a trace_id for support. |
| 503 | upstream_unavailable | Underlying data source (SLDC, IEX) is temporarily unreachable. Retry with backoff. |
Feature gates
feature_not_in_plan includes detail.required_tier so you can render a precise upsell message. Example: asking for state-level fuel mix on Starter returns required_tier: "pro".
| Status | Retry? | Strategy |
|---|---|---|
| 400, 401, 402, 403, 404 | No | Fix the request, key, or tier. Retrying will fail identically. |
| 429 | Yes | Obey Retry-After. Exponential backoff with jitter. |
| 500, 502, 503, 504 | Yes | Exponential backoff with jitter. Cap at ~5 retries before surfacing. |