Section 09 of 12
Errors
Every error is {"error": {"code", "reason", "message", "details"?}} with the real HTTP status. reason is the stable slug to branch on; code (QVST1-3404 style) is the support reference to quote; message is English text for humans and may change. Errors cost no units, except that 429 and 402 answers are counted in the per-key error figures on the account page.
Quavsit errors#
| Status | reason | code | When |
|---|---|---|---|
| 400 | invalid_location | QVST1-1400 | malformed near, bbox or radius |
| 400 | transit_query_invalid | QVST1-2400 | invalid parameter value or combination; details.reason = "cross_network" on journeys |
| 404 | place_not_found | QVST1-4404 | from or to could not be resolved |
| 404 | network_not_found | QVST1-1404 | unknown network slug |
| 404 | line_not_found | QVST1-2404 | unknown line id |
| 404 | stop_not_found | QVST1-3404 | unknown stop or area id |
| 404 | capability_unsupported | QVST1-5404 | the network has no such data (vehicles, native planner) |
| 409 | key_limit_reached | QVST1-1409 | plan's maximum number of keys reached (account API) |
| 429 | stream_limit_reached | QVST1-1429 | too many concurrent streams |
| 429 | spend_cap_reached | QVST1-2429 | monthly spend cap hit while in overage; details {spend_cap_cents, spent_cents, period} |
| 502 | transit_upstream_error | QVST9-1502 | operator feed failed and no cached copy is usable |
| 503 | transit_upstream_budget | QVST9-3503 | daily upstream quota exhausted and no cached copy |
| 503 | network_data_unavailable | QVST9-4503 | static data not ingested yet for that network |
| 503 | quavsit_unconfigured | QVST2-1503 | service misconfiguration on our side |
| 504 | transit_upstream_timeout | QVST9-2504 | operator feed did not answer in time |
place_not_found is a 404: the requested place could not be resolved; the reason is what to branch on.
Platform errors#
The identity and metering layer answers with its own prefixes; these are the ones an API client meets.
| Status | reason | code | When |
|---|---|---|---|
| 401 | authentication_required | SCTY3-1401 | no bearer key |
| 401 | invalid_token | SCTY3-2401 | unknown, revoked or non-API token |
| 402 | plan_required | MARL1-1402 | streams on a plan without them, or overage on a plan without it |
| 403 | insufficient_scope | SCTY3-2403 | wrong audience or scope, or not a personal key |
| 403 | email_verification_required | MAIL3-1403 | account email not verified |
| 404 | not_found | APIE1-0404 | unknown route |
| 429 | rate_limited | APIE1-0429 | per-minute burst exceeded; retry after the Retry-After header |
| 429 | quota_exhausted | APIE1-1429 | monthly units exhausted; details {plan, period, limit, used, remaining, resets_at, overage_available, upgrade_url} |
| 500 | internal_error | APIE2-1500 | unexpected failure on our side |
Handling advice#
- Branch on
reason, never onmessage. - On
rate_limited, wait forRetry-Afterseconds; onquota_exhausted, readdetails.resets_atand, ifdetails.overage_availableis true, consider enabling overage on the account page. - On
transit_upstream_*, retry once after a few seconds; the answer may come backstale(see freshness) rather than fail. - Keep the
codein your logs; it is what support asks for.
{"error": {"code": "APIE1-1429", "reason": "quota_exhausted", "message": "Monthly units exhausted.",
"details": {"plan": "free", "period": "2026-09", "limit": 5000, "used": 5000, "remaining": 0,
"resets_at": "2026-10-01T00:00:00Z", "overage_available": false, "upgrade_url": "https://my.quavern.com/account/quavsit"}}}