Errors
On this page
Every error the API returns, the body it comes with, and what to do about it. Branch on the HTTP status code first, then read the text as body.message ?? body.error.
Response shapes
Errors come in three shapes. All of them carry an X-Request-Id header; include it if you contact support about a specific request.
| Shape | Used for | Fields |
|---|---|---|
| Error | Most 4xx and every 5xx | code is FAILURE, message explains it, data is null. |
| Invalid input | A query parameter, path parameter or body field that fails validation | success is false, message is Please check your input and try again., data lists each problem. |
| Rate limited | 429 | code is RATE_LIMITED, details in data, plus status and error kept for older integrations. |
The full description of each shape is in the introduction.
Every error
| HTTP | Message | Where | What to do |
|---|---|---|---|
400 | Please check your input and try again. | Any endpoint with parameters or a body | Fix the input. data[].path points at the field, for example ["email"] or ["emails", 2]. Don't retry unchanged. |
400 | The request body is not valid JSON. | Batch, webhook management | Send a valid JSON body with Content-Type: application/json. |
400 | Insufficient credits. | Single, batch, bulk upload | Your balance can't cover the request. Top up in the dashboard; retrying won't help. |
400 | Please buy credits. | Single, batch, bulk upload | Your balance is empty. Top up in the dashboard. |
400 | Maximum 50 emails per request is allowed. | Batch | Split the list into chunks of 50. |
400 | File is required | Bulk upload | Send the CSV in a multipart field named file. |
400 | Invalid file type. Only text/csv are allowed. | Bulk upload | Set the file part's content type to text/csv. |
400 | File too large | Bulk upload | The file is over 50 MB. Split it. |
400 | Email column not found in the file. | Bulk upload | Add a header row with a column named email (or one of the accepted names). |
400 | No email addresses found in the file. | Bulk upload | The email column is empty. |
400 | Sandbox bulk uploads are limited to 1,000 rows. This file has … | Bulk upload with a test key | Use a smaller file in the sandbox. |
400 | Job not found. | Bulk status, bulk download | No job with this id in your organization (or a live job asked for with a test key, and the other way round). |
400 | Results are not available for this job. | Bulk download | The job is QUEUE or FAILED. |
401 | Authorization header is required | Every endpoint | Send Authorization: Bearer <your-api-key>. |
401 | Authorization header must be in format: Bearer <token> | Every endpoint | Fix the header: Bearer, one space, then the key. |
401 | API key is required | Every endpoint | Bearer was sent with no key after it. |
401 | Invalid API key | Every endpoint | No such key. Often a copy-paste error or a key that was regenerated. |
401 | API key is inactive | Every endpoint | The key is disabled. Enable it or use another key. |
401 | API key has expired | Every endpoint | Update its expiry or create a new key. |
404 | No check in progress for this email. | /single/result | You already collected the result, or no check was started for this address. |
429 | Rate limit exceeded. … | Every limited endpoint | Wait Retry-After seconds, then send again. See Rate limits. |
500 | Internal Server Error | Any endpoint | Retry with exponential backoff and jitter. |
None of the 400 and 401 errors fix themselves, so don't retry them in a loop. Retry only 429 (after Retry-After), 5xx and network failures. Rate limits has retry code in Node, Python and PHP.
Example bodies
400 · Invalid input
{
"success": false,
"message": "Please check your input and try again.",
"data": [
{
"validation": "email",
"code": "invalid_string",
"message": "Invalid email address",
"path": ["email"]
}
]
}400 · Error
Every other 400 uses the error shape, with the message from the table above:
{
"code": "FAILURE",
"message": "Insufficient credits.",
"data": null
}401 · Authentication
{
"code": "FAILURE",
"message": "Invalid API key",
"data": null
}Too many requests with an invalid, inactive or expired key from one IP address (30 per 15 minutes) turn into 429s for that address, even with a valid key, until the window passes. See Rate limits.
404 · No check in progress
{
"code": "FAILURE",
"message": "No check in progress for this email.",
"data": null
}429 · Rate limited
{
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Maximum 100 requests per minute per API key.",
"data": {
"policy": "api.single",
"retryAfterSeconds": 1,
"limit": 100,
"scope": "apiKey",
"blocked": false
},
"status": 429,
"error": "Rate limit exceeded. Maximum 100 requests per minute per API key."
}The response also carries Retry-After and the RateLimit-* headers. Rate limits explains every field.
500 · Server error
{
"code": "FAILURE",
"message": "Internal Server Error",
"data": null
}With a test key, the address error-429@… returns the 429 and error-500@… the 500 on demand, so you can test this handling without waiting for a real failure.
Not errors
A few answers look like problems but are normal results. Handle them as results, not errors: