Getting Started
Errors & status codes
HTTP status codes, error response shapes, and the engine availability envelope.
Errors & status codes
Navvo uses conventional HTTP status codes and a consistent error body.
Status codes
| Code | Meaning |
|---|---|
200 OK | Successful GET. |
201 Created | Successful POST (NestJS returns 201 for POST handlers, even when nothing is persisted — e.g. routing). |
400 Bad Request | Invalid or missing parameters / body (validation failure). |
401 Unauthorized | Missing or unknown API key. |
403 Forbidden (access) | Key deactivated (API key revoked) or missing the required scope (code FORBIDDEN), or used from a website or IP address it is not allowed from (code KEY_WEBSITE_NOT_ALLOWED / KEY_IP_NOT_ALLOWED, see Restricting a key). Retrying won't help: fix the key. |
403 Forbidden (quota) | The key is over its per-minute or per-day quota, or the per-IP limit (code QUOTA_EXCEEDED). details gives limit, window, observed and, for a minute window, retryAfterSeconds, also sent as Retry-After. See Rate limits. |
404 Not Found | Route does not exist. (Note: some lookups return 200 with a null body instead — see below.) |
429 Too Many Requests | Your IP address sent too many requests to one endpoint (code RATE_LIMITED). Wait for the seconds in the Retry-After header, then retry. |
502 / 503 | A Navvo dependency (tiles, imagery) is unavailable. |
An unlimited key never receives QUOTA_EXCEEDED.
Error body
Errors use the standard NestJS exception shape:
{
"statusCode": 403,
"message": "API key missing required scopes: routing",
"error": "Forbidden"
}
Validation errors (400) may return message as an array of field-level messages:
{
"statusCode": 400,
"message": ["email must be an email", "password must be longer than or equal to 10 characters"],
"error": "Bad Request"
}
Two behaviors worth knowing
1. The "available" envelope (engine endpoints)
Endpoints backed by a Navvo engine — Geocoding, Routing, and parts of Traffic — do not return a 5xx when the engine is unreachable. Instead they return a normal 200/201 with an available: false (or ok: false) flag and echo your request back:
{
"provider": "navvo",
"available": false,
"path": "route",
"request": { "locations": [ { "lat": 31.95, "lon": 35.91 } ], "costing": "auto" }
}
Always check the
available / ok boolean on engine responses before reading data. A 200 does not guarantee a usable result.2. "Not found" can be 200 null
A few lookup endpoints (notably GET /places/{slug}) return HTTP 200 with a null body when the resource doesn't exist, rather than a 404. Treat an empty/null body as "not found".
Handling errors
const res = await fetch("https://navvo.io/api/v1/search?q=cafe", {
headers: { "x-api-key": key },
});
if (res.status === 429) {
// Throttled — wait for Retry-After (seconds) before sending to this endpoint again.
const waitSeconds = Number(res.headers.get("retry-after")) || 1;
throw new Error(`Rate limited; retry in ${waitSeconds}s`);
}
if (res.status === 401 || res.status === 403) {
// Auth/scope/quota problem — branch on err.code (e.g. QUOTA_EXCEEDED), don't retry blindly.
const err = await res.json();
throw new Error(`${err.statusCode} ${err.code}: ${err.message}`);
}
if (!res.ok) {
// 400/5xx
throw new Error(`Request failed: ${res.status}`);
}
const data = await res.json();