Errors
When a request fails, the API answers with an HTTP status and a JSON body that names the problem. No credit is ever used for a request that ends in an error.
The error body
{
"error": {
"code": "insufficient_credits",
"message": "Not enough credits. Buy a pack to continue.",
"request_id": "req_7Hq2LmX9cV4bN8tR1zK6wD"
}
}codeis stable and machine-readable: branch on it.messageis for people and may change: do not parse it.request_ididentifies the request (also in theX-Request-Idheader). Quote it when you contact support.
429 and 503 answers also carry Retry-After: the number of seconds to wait before trying again.
Every error code
| Code | HTTP | When it happens | What to do | Retry? |
|---|---|---|---|---|
invalid_request | 400 | A parameter is missing, has the wrong type or is out of range, or the API key was sent in the URL. The message names the fields at fault. | Correct the request. Send the key in the Authorization header, never in the query string. | No: fix the request |
unauthorized | 401 | No API key was sent, the key is malformed, or it has been revoked or rolled. | Send a current key as Authorization: Bearer <key> (or X-Api-Key). Create a new key in the dashboard if needed. | No: fix the request |
insufficient_credits | 402 | The account has no credits left for this request (or not enough for a bulk job). | Buy a credit pack in the dashboard, then retry. Nothing was charged for the failed request. | After you fix the account |
forbidden | 403 | The key lacks the scope this endpoint needs (for example bulk), or the account is suspended or on a billing hold. | Use a key with the right scope, or check the notices in your dashboard. | After you fix the account |
forbidden_origin | 403 | The key is restricted to a list of browser origins and the request came from another Origin. | Call the API from an allowed origin, or update the allowed origins of the key. | No: fix the request |
not_found | 404 | The resource does not exist or belongs to another account. | Check the id. Ids are only visible to the account that created them. | No: fix the request |
conflict | 409 | The request does not fit the current state of the resource, such as starting a bulk job that is already running or downloading results before a job has finished. | Read the job again and act on its current status. | No: fix the request |
length_required | 411 | A bulk upload was sent without a Content-Length header (for example as a chunked stream). | Send the file with a known length. Most HTTP clients do this when you pass the whole file. | No: fix the request |
payload_too_large | 413 | The request body is over the limit: 100 MB for a bulk upload, 100,000 addresses for a JSON list. | Split the list, or upload large files in the dashboard, which takes files up to 1 GB. | No: fix the request |
rate_limited | 429 | The account or key sent more requests per second than its limit allows. | Wait for the number of seconds in Retry-After, then retry. Spread requests evenly over time. | Yes, after Retry-After |
abuse_throttled | 429 | Too many recent requests ended without a definite answer (free outcomes) compared with charged ones, so the account is slowed down for a while. | Wait for Retry-After. Avoid re-checking the same failing addresses in a tight loop. | Yes, after Retry-After |
key_daily_cap | 429 | This key reached the daily credit cap set for it in the dashboard. | Raise or remove the cap for this key in the dashboard, use another key, or wait until the cap resets the next day. | Yes, after Retry-After |
internal_error | 500 | Something failed on our side. No credit was used. | Retry later. If it keeps happening, contact support and quote the request_id. | Yes, with backoff |
service_unavailable | 503 | Validation is paused for a short time, for example while a dependency recovers. No credit was used. | Wait for Retry-After, then retry. In sign-up forms, accept the address and check it later. | Yes, after Retry-After |
upstream_capacity | 503 | A bulk job could not be admitted right now because validation capacity is limited. No credit was used. | Start the job again later; it stays in the ready state until then. | Yes, after Retry-After |
Retrying safely
- Retry
429and503after the number of seconds inRetry-After, with a cap on attempts (three is plenty). These answers are never charged, so retrying them cannot double-charge. - Back off when there is no
Retry-After: wait about half a second, then one, then two, adding some randomness so many clients do not retry at the same moment. - Do not retry
key_daily_capor longabuse_throttledwaits in a loop: they last until the next day or for an hour. - Do not retry
4xxerrors other than429without changing the request: the same request will fail again. - Network failures: a check that timed out on your side may have completed on ours. Retrying a validation is safe but may cost a second credit; reads (
GET) can always be retried.
The official SDKs follow these rules for you.
Errors in sign-up forms
On a sign-up form, an error from us should never stop a real person. Treat 402 insufficient_credits, 429 and 503 (and network failures) like an unknown result: accept the address, store it, and check it again later. Only an actual undeliverable result should ask the person to correct their address. See the sign-up form guide.
Results that are not errors
A check that could not reach a definite answer is not an error: it is a normal 200 response with the status unknown (free). Handle it as a result; see the unknown status.