Errors and limits
Error format
Every error answers with a non-2xx status and a JSON body:
{
"error": true,
"url": "/api/emails",
"statusCode": 422,
"statusMessage": "Suppressed after a bounce, complaint or unsubscribe, so not sent: ada@example.com",
"message": "Suppressed after a bounce, complaint or unsubscribe, so not sent: ada@example.com"
}
Server Error, so no internals leak.Branch on statusCode in your code, and log message for the person debugging it.
Status codes
| Status | Meaning here | Retry? |
|---|---|---|
400 Bad Request | The request is malformed: invalid JSON, a missing or invalid address, or content that breaks the rules of Send email, such as templateId and html in the same request. | No. Fix the request. |
401 Unauthorized | The API key is missing, not sent as Bearer <key>, unknown or revoked. See Authentication. | No. Fix the key. |
403 Forbidden | Authenticated, but not allowed to do this. In the dashboard this is a team role that does not permit the action, such as a Developer creating an API key. | No. |
404 Not Found | The template or email does not exist in this workspace, or the URL is wrong. | No. |
409 Conflict | The request clashes with what already exists, for example adding a domain the workspace already has. | No. |
422 Unprocessable Entity | The request is well formed but cannot be sent: the from domain is not verified in this workspace, or a recipient is on your suppression list. | No. Change the sender or the recipient. |
429 Too Many Requests | You hit the rate limit or your plan's monthly volume. | After Retry-After, or after the monthly volume resets or you upgrade. |
500 Internal Server Error | Something failed on our side. | Yes, with backoff. |
502 Bad Gateway | Amazon SES rejected the message. | Yes, with backoff. |
503 Service Unavailable | A dependency is temporarily unavailable. | Yes, with backoff. |
Rate limit
Each API key can make 10 requests per second. A request over the limit answers 429 with a Retry-After header that says how many seconds to wait before trying again:
HTTP/1.1 429 Too Many Requests
Retry-After: 1
Content-Type: application/json
Spread bursts out with a queue in your application rather than retrying in a tight loop. Every request counts, including Retrieve email calls, so prefer webhooks to polling.
Monthly volume
Every plan includes a number of emails per month. Once you reach your plan's volume, sending answers 429 until the volume resets or you move to a larger plan.
| Plan | Emails per month |
|---|---|
| Free | 3,000 |
| Starter | 25,000 |
| Pro | 75,000 |
| Business | 250,000 |
| Scale | 750,000 |
| Enterprise | 1,000,000 or more |
Prices and features are on the pricing page.
Retries
Retry only what can succeed on a second attempt: 429 and 5xx responses, and requests that failed in the network before any response arrived.
- Send every email with an
Idempotency-Key, and keep the same key across retries. A retry then cannot send the email twice, even when the first attempt reached us. - On
429, wait forRetry-Afterseconds. - On
5xxor a network error, back off exponentially with jitter: about 1, 2, 4, 8 seconds, up to a ceiling you choose. - Give up after a few attempts and record the failure so a person can look at it.
Never retry 400, 401, 403, 404 or 422 unchanged. The same request gets the same answer.