Errors
Every error response uses the same JSON envelope and an appropriate HTTP status code.
The error envelope
{
"error": {
"code": "NOT_FOUND",
"message": "Map not found"
}
}
| Field | Type | Notes |
|---|---|---|
error.code |
string | A stable, machine-readable code (see the table below). Branch on this, not on message. |
error.message |
string | A human-readable explanation. Wording may change; don’t parse it. |
error.help_url |
string, optional | A deep link to a dashboard page the user can act on. Present on 401 and 402. |
Status-code table
| HTTP | error.code |
Meaning | What to do |
|---|---|---|---|
400 |
BAD_REQUEST |
Malformed request: bad JSON, missing required field, neither coordinates nor a geocodable address, a bulk delete without confirm: true, or a bulk import over the plan’s row limit. |
Fix the request and retry. |
401 |
UNAUTHORIZED |
Missing, invalid, or revoked API key. | Check the Authorization header; request/rotate a key. Follow help_url. |
402 |
PLAN_REQUIRED |
Valid key, but the account is not on the enterprise plan (or a paid-only operation on a non-paid account). | Upgrade the account. Follow help_url. |
403 |
FORBIDDEN |
Authenticated but not allowed — e.g. a read_only key on a write (POST/PATCH/DELETE). |
Use a read_write key, or don’t attempt the write. |
404 |
NOT_FOUND |
The resource doesn’t exist or isn’t owned by your account. Because IDs are enumerable, “not owned” is reported as 404, not 403. |
Verify the id; confirm your account owns the map. |
429 |
RATE_LIMITED |
Per-key rate limit exceeded. | Back off until Retry-After; see Rate limits. |
500 |
INTERNAL_ERROR |
An unexpected server error. | Retry with backoff; if it persists, contact support. |
help_url on auth failures
On 401 UNAUTHORIZED and 402 PLAN_REQUIRED, the envelope may include an error.help_url
that deep-links the user to the page that resolves the problem — the request-an-API-key
page for 401, the upgrade-to-enterprise page for 402:
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key.",
"help_url": "https://www.zeemaps.com/user/settings?tab=api"
}
}
This is what lets a fresh client (including an MCP server) recover: surface the link, the user completes the request/upgrade in their dashboard, and the resulting key is pasted back in. See Authentication.
Handling errors well
- Branch on
error.code, not the HTTP status alone and never onmessagetext. - Treat
404as “missing or not yours” — don’t assume the map exists for another account. - Retry only
429(afterRetry-After) and500/502/503(with exponential backoff). Never blindly retry400/401/402/403. - Surface
help_urlto a human whenever it’s present.