Rate Limits & Retries
SendMero limits how quickly your workspace can call the email API (or send over SMTP) and how many production recipients it can submit each day. These limits apply to the workspace, so creating another API key does not create a separate allowance.
These limits describe the implemented email submission API. A public endpoint and the email delivery integration have not been released. Follow the API overview and authentication setup before using an administrator-provided test endpoint.
Request rate
The default limit is 1,000 requests per 10 seconds per workspace (100 requests/second on average), shared across all its API keys and clients in each Cloudflare location.
Cloudflare's native limiter uses an approximate 10-second window and permits bursts; it does not enforce an exact 100-request cap in each second. Spread work over time and coordinate concurrent clients using the same workspace. A rate-limit rejection returns 429 rate_limit_exceeded with Retry-After: 10; wait at least that long before retrying.
After a request passes the API-key and request-rate checks, it uses one request slot. This includes:
- Production submissions to
POST /v1/messages. - Test-mode submissions.
- Repeated POSTs, including ones with the same content as an earlier request.
- Requests that subsequently fail sending-permission, recipient-allowance, or queue-processing checks.
For POST /v1/messages, header, body-size, JSON, and email-field validation run before API-key authentication. A request rejected at those stages does not consume the workspace's authenticated request allowance. An invalid API key also does not consume that allowance. Additional traffic protection can still reject requests independently, including before authentication. Those rejections also use HTTP 429; follow the returned Retry-After instead of assuming every request-limit response means a one-second wait.
The Suppressions API has its own limit of the same size, 1,000 requests per 10 seconds per workspace, so suppression calls never use up the limit for sending.
Daily recipient allowance
The daily allowance is configured for your workspace and uses UTC calendar days. It counts unique production recipients within each logical email submission, including to, cc, and bcc. Reply-to addresses do not count as delivery recipients.
For example, one email to three distinct recipients uses one request slot and three recipient units. Repeating the same recipient in to and cc does not add another recipient unit. Sending that person a separate email does use another unit.
| Operation | Uses request rate? | Uses new production recipient allowance? |
|---|---|---|
| New production submission | Yes, after the key/rate checks pass | Yes, for its unique recipients |
| Repeated production POST | Yes | Yes, if accepted; it is a separate submission |
| Successful test-mode submission | Yes | No |
| Message rejected by header, body-size, JSON, or email-field validation | No workspace slot; traffic protection still applies | No |
| Message rejected by sending-domain or recipient-allowance checks after authentication | Yes | No new recipient allowance |
A production submission can hold recipient allowance while its queue outcome is pending or unknown. Held and already accepted recipients both reduce the allowance available for new submissions. Each production POST has its own recipient allowance accounting. Repeating an already accepted request can queue another email and consume that allowance again. Duplicate queue processing of one submission is a separate internal concern and does not make repeated API requests safe.
If a message would take the workspace past a daily or monthly recipient limit or cap, the API returns 429 daily_limit_exceeded or 429 monthly_limit_exceeded. The whole request is refused (it is never partly accepted) and nothing is queued. Retry-After is the number of seconds until the limit resets: 00:00 UTC for a daily limit, 00:00 UTC on the 1st for a monthly one. Your current limits and usage are on the Usage page of the dashboard. Request-rate headers do not show the remaining recipient allowance.
Request size and other limits
| Limit | Value |
|---|---|
| Entire JSON request body | 1,000,000 UTF-8 bytes, inclusive |
| Body upload time | 15 seconds to read the body |
Supplied to + cc + bcc entries | 100 combined, including duplicates before normalization |
| Reply-to entries | 10 |
| Attachments | 20, within the total request-body limit |
| Subject | 998 UTF-8 bytes |
| Custom headers | 50 headers, with 16,384 UTF-8 bytes across complete serialized header lines |
| Tags | 10 pairs; each name at most 64 UTF-8 bytes and value at most 256 UTF-8 bytes |
| Metadata | 4,096 UTF-8 bytes for the complete serialized object |
The body limit includes JSON structure, escaping, and Base64-encoded attachment content. It is not a decoded-attachment allowance. Send UTF-8 JSON with Content-Type: application/json. The body must be uncompressed: omit Content-Encoding or use identity.
An oversized request returns 413 message_too_large; an upload exceeding the deadline returns 408 request_timeout. Reducing the encoded body size can help with either condition. The Send Email reference explains the fields and their validation rules; Attachments & Inline Images covers file encoding and size.
SMTP limits
SMTP uses the same workspace recipient allowances and credits. Because there is no JSON body, it has its own size and connection limits:
| Limit | Value |
|---|---|
| Message size, including attachments | About 90 KB (552 if larger) |
| Recipients per message | 100 (452 4.5.3 past that) |
| Open connections from one IP address | 20 (421 past that) |
| Rejected API keys | After 10 within 10 minutes, that IP address is refused for 15 minutes |
| Idle connection | Closed after 60 seconds |
A temporary SMTP reply (451, 421, 452) means try again later; mail libraries normally retry on their own. See SMTP.
Response headers
The API does not return X-RateLimit-* headers or exact remaining/reset counters. A request-rate rejection returns Retry-After: 10, meaning wait at least 10 seconds before retrying. The rate check runs before queue publication, so a 429 rate_limit_exceeded response has not queued that submission.
All responses include X-Request-Id and Cache-Control: no-store.
For example, a request-rate rejection returns HTTP 429 with Retry-After: 10 and this body:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Request limit exceeded.",
"request_id": "req_00000000000000000000000000000001"
}
}
The example delay is not a fixed value for every 429. The not_accepted outcome describes this request only. If you sent an earlier POST, this rejection says nothing about that earlier request.
Independent submissions
The API does not prevent duplicate POST submissions. Each request to POST /v1/messages is handled independently, even when the body exactly matches an earlier request. Successful requests receive fresh message and recipient IDs; repeated production requests can queue another email and consume recipient allowance again.
Track whether your application has already submitted an intended email, and coordinate concurrent producers before making the POST. No message-status lookup or search endpoint is available. Save the original submission response and any returned IDs.
Decide whether to retry
Inspect error.code, error.retryable, and, for POST errors, error.acceptance. Do not blindly retry a POST with an uncertain outcome. A missing HTTP response does not prove that the email was rejected.
| Acceptance | Meaning | Client action |
|---|---|---|
not_accepted | This request is known not to have reached acceptance. | Correct the cause, or retry after the specified delay if retryable is true. The next POST is a separate request. |
pending | This request has not reached a final result. | Do not automatically POST again. Keep its available message/request IDs and ask your administrator to investigate. |
unknown | The service cannot confirm whether this request was accepted. | Do not automatically POST again. Retain any message ID and the HTTP request ID for operator investigation. |
Errors during header, authentication, rate, or body validation describe the current request as not_accepted. They do not recover, cancel, or establish the outcome of any earlier request. A later not_accepted response must not be used as proof that an earlier timed-out POST failed.
| Response | Next action |
|---|---|
| No usable response, connection failure, or client timeout | Stop automatic POST retries. Contact your administrator with any message/request IDs and the available request details. The email may already be queued. |
408 request_timeout with not_accepted | Check upload size and connectivity. A new attempt is allowed after a delay; Retry-After may be absent. |
429 rate_limit_exceeded with not_accepted | Slow down all workspace clients and wait at least Retry-After before another attempt. |
429 sending_quota_exceeded with not_accepted | Wait for the indicated daily reset or arrange a higher allowance. Repeated immediate attempts will not help. |
503 sender_not_ready with not_accepted | Wait for Retry-After; ask your administrator to check sending readiness if it persists. |
503 service_unavailable with not_accepted and retryable: true | The request was rejected before acceptance. Follow Retry-After before another attempt. |
Any POST error with pending or unknown | These responses have retryable: false and no Retry-After. Investigate; do not create a replacement request automatically. |
| Other documented non-retryable errors | Correct the request, credentials, or permissions. Read acceptance before deciding to submit again. |
For known nonacceptance, use increasing retry delays with a small random variation, never less than Retry-After, and bound the number of attempts. Stop if an attempt has an uncertain outcome. Retry logic must not rely on HTTP status alone: two 503 service_unavailable responses can require different actions depending on acceptance.
There is no customer status lookup or recovery endpoint. Save message IDs and request IDs and ask your administrator to investigate unresolved submissions. A 404 on the removed lookup route does not prove nonacceptance. See Submission Outcomes. Queue acceptance is not a delivery receipt.
Related references
- Send Email — request fields, examples, and successful submission responses.
- Test Mode — test without using production recipient allowance.
- Submission Outcomes — interpret responses and investigate uncertain results.
- Error Codes — every current public error and how to resolve it.
- Authentication — API-key and workspace access requirements.