API
API errors and rate limits
By the MapsData team · Last updated
Every failure uses one envelope with a stable
code. Branch on the code, not on the status or the
message. Statuses are conventional and messages are written for humans and may
be reworded; codes are part of the contract.
{"error": {
"code": "lead_limit_reached",
"message": "This cycle's lead allowance is spent: 4,812 of 5,000 used.",
"field": "total_limit",
"details": {"leads_used": 4812, "lead_limit": 5000},
"request_id": "req_..."
}}fieldnames the offending input when one is to blame.detailscarries whatever is useful to act on, such as the suggestions for an unrecognised category or the figures behind a quota rejection.request_idis worth logging. Quote it to us and we can find the exact request.
Codes
Authentication and access
| Code | Status | What to do |
|---|---|---|
invalid_api_key | 401 | Check the header and the key. Do not retry with the same value. |
key_revoked | 401 | Create a new key in Settings. |
key_expired | 401 | Create a new key. |
insufficient_scope | 403 | The key lacks the scope named in details. |
api_not_available_on_plan | 403 | API access is on every paid plan: Starter, Pro, Scale and custom. |
api_disabled | 503 | The API is temporarily off. Retry after the interval given. |
Your request
| Code | Status | What to do |
|---|---|---|
validation_error | 422 | The body failed schema validation. field names the culprit. Unknown fields are rejected, so check for a typo. |
unsupported_query | 400 | Not a Google Maps category. Use the suggestions, or search /categories. |
unknown_regions | 400 | A region name was not recognised. Use full names, not abbreviations. |
missing_region | 400 | No valid region was supplied. |
missing_location | 400 | Give either states or cities. |
unresolvable_cities | 400 | None of the named cities exist in our index. |
unsupported_country | 400 | Use one of us, au, uk, ca. |
too_many_queries | 400 | Up to three categories per job. |
too_many_cities | 400 | Up to fifty cities per job. |
invalid_total_limit | 400 | total_limit must be a positive number of rows. |
total_limit_exceeds_max | 400 | Above your per-scrape ceiling. Split the job. |
invalid_max_emails_per_company | 400 | Must be 1 or more, or omitted entirely. |
invalid_cursor | 400 | Pass a next_cursor from a previous page, or omit it. |
client_forbidden | 403 | This key cannot use that client_id. See /clients. |
Your workspace
| Code | Status | What to do |
|---|---|---|
lead_limit_reached | 402 | This cycle's allowance is spent. It resets on your billing date. |
org_past_due | 402 | Payment failed, so new scrapes are paused. Existing results still download. |
org_cancelled | 403 | Subscription cancelled. Resume it in billing. |
org_blocked | 403 | The workspace is suspended. Contact us. |
queue_depth_exceeded | 409 | Too many scrapes already waiting. Let one start first. |
Jobs and files
| Code | Status | What to do |
|---|---|---|
job_not_found | 404 | No such job in this workspace. |
job_not_complete | 409 | The scrape has not finished, so no file exists yet. |
download_not_available | 404 | That format or scope does not exist for this job. Check the job's downloads flags. |
job_not_cancellable | 409 | Already finished. |
download_unavailable | 503 | Could not mint a link. Retry. |
Retries
| Code | Status | What to do |
|---|---|---|
idempotency_key_reuse | 409 | That key was used with a different body. Use a new key. |
request_in_progress | 409 | A request with that key is still being processed. Retry shortly. |
rate_limited | 429 | Back off for Retry-After seconds. |
Rate limits
Limits are per minute, in fixed windows.
| Scope | Limit | Applies to |
|---|---|---|
| Per key | 120 / min | Every request |
| Per workspace | 300 / min | Every request, across all your keys |
| Submissions | 10 / min | POST /jobs only |
An idempotent replay does not count against the submission limit, because it creates nothing.
In front of all of that sits an edge limit of 300 requests per minute per
source address, covering the REST API, the MCP server and the OAuth endpoints.
A runaway loop is
refused there before it reaches the application at all, and that response
carries no X-RateLimit-* headers because nothing authenticated
it.
Headers
Every response carries your current position:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1788387840X-RateLimit-Reset is a Unix timestamp for when the window rolls
over. On a 429 you also get Retry-After in seconds,
computed from that boundary rather than being a flat minute, so honouring it
resumes as early as possible.
Retrying well
- 429 and 503 are worth retrying. Wait for
Retry-After, then use exponential backoff with jitter. - 5xx is worth one or two retries with backoff.
- 4xx other than 429 will not succeed on retry. Fix the request.
- Put an
Idempotency-Keyon every submission you might retry. It is the difference between a retry and a duplicate scrape.
Build on MapsData
The REST API and the MCP server are included on every paid plan, with the same allowance as the app. Subscribe or start a trial, then create a key in Settings.
See plans and API access