POST /email_messages/batch. Each message in the batch is validated and processed independently — one bad message never blocks the rest — and up to 1,000 messages can be sent per request.
Batch sending is the right tool when you generate many distinct messages at once: a marketing campaign with per-recipient personalization, a batch of transactional notifications (invoices, receipts, shipping updates), or a nightly digest job. If you’re sending the same content to many recipients, a single regular send with multiple to entries is simpler.
This guide assumes you have a Telnyx account with an API key and a sender your account is permitted to send from — normally a verified sending domain, or the shared domain during onboarding. If you don’t, complete the Quickstart first, then return here. Replace
YOUR_API_KEY and the sender and recipient addresses in the examples with your own.curl
Limits
Each item in the
messages array mirrors the single-send message schema — same fields, same validation, with from and to required — except the single-send-only reply and forward threading fields (in_reply_to_message_id, reply_to_all, and forward_of_message_id), which are not accepted on batch items. Templates (template_id with template_variables), scheduled_at, attachments, headers, tags, metadata, and per-item sandbox_mode all work as in a single send. The one batch-level addition is sandbox_mode, which applies sandbox mode to every message in the request and overrides any per-message setting.
Batch requests are processed synchronously — the response reports the outcome of every message. Large batches take proportionally longer to process; the request timeout at the edge is 55 seconds. If you’re near the 1,000-message ceiling, keep payloads small, or split very heavy sends into multiple batch requests.
The 20 MB edge cap applies only to requests carrying an
Idempotency-Key. It is a cap on the encoded request body, while the per-message limits are decoded — and the two ceilings are independent. Even a single attachment-heavy message that is valid per-message (up to 25 MB decoded) can expand past 20 MB on the wire and be rejected at the edge on a keyed request. An unkeyed request bypasses the edge cap entirely, but then has no replay protection.Response
Every batch response uses207 Multi-Status, even when all messages succeed. The response contains a data array for created messages (which may be empty), an errors array for failed messages, and a meta summary:
errors carries the zero-based index of the failed message in your request array, a code, and a message. Fix the failed messages and re-send only those — the successful messages in data are already queued and need no re-submission.
Batch item error codes: bad_request, unprocessable_entity, not_found, forbidden, service_unavailable, validation_error, recipient_suppressed, and reputation_suspended. A per-message size violation (unprocessable_entity) fails only that message; the rest of the batch still processes. For the full per-code reference, see Error Codes.
Suppression is per-recipient, not per-message. A message whose recipients are all suppressed returns the
recipient_suppressed per-item error; other messages in the batch are unaffected. See Suppressions.Idempotency
Pass anIdempotency-Key HTTP header to safely retry an entire batch. Generate a unique UUID v4 for each logical batch request and reuse the same key only when retrying the identical request body:
curl
messages; there is no per-message key surface on the batch endpoint.
If a retried batch replays a stored response, the response includes the Idempotent-Replayed: true header. Reusing a key with a different body returns 422 with code 10027. See Idempotency for the full key lifecycle.
A keyed batch larger than 20 MB is rejected at the edge with
413 before reaching the Email API. If your batch needs both replay protection and heavy payloads, keep the encoded request under 20 MB — fewer, smaller messages per request — or send the heavy batch unkeyed and make retry safety your application’s responsibility.Rate limits
Batch requests are rate-limited per account on top of the general request limits. The response carries the Envoy draft-03 rate limit headers —x-ratelimit-limit, x-ratelimit-remaining, and x-ratelimit-reset (seconds until the window resets) — so a well-behaved client can pace itself without guessing:
429 Too Many Requests. Honor x-ratelimit-reset when present and fall back to exponential backoff with jitter when it’s not. Exact tiers depend on your account level — a 1M-message-per-day workload averages under one batch request per minute, so the limits rarely bind for steady senders. Contact support to confirm or raise your account’s limits.
Pace your requests with
x-ratelimit-remaining and x-ratelimit-reset rather than firing unbounded parallel batches. If your account’s allowance supports concurrency and you need higher sustained throughput than sequential requests deliver, contact support to confirm your limits rather than fanning out against 429s.Patterns
Personalize at scale with templates
Batch plus templates is the cleanest high-volume pattern: one template, one batch, per-recipient variables.curl
Schedule a batch
Every message in a batch can carry its ownscheduled_at — mixed immediate and scheduled sends in one request are fine. See Schedule a send.
Track outcomes per message
Each created message indata returns its own id. Use those IDs with GET /email_messages/{id} and GET /email_messages/{id}/events for per-message tracking, or configure webhooks to receive delivery and engagement events for every message in the batch.
The batch response’s
data entries are creation records — status: "queued" is the starting point, not the delivery outcome. Track delivery through events, exactly as with single sends.Retry only the failures
Theerrors[].index mapping makes partial retry mechanical. This complete example sends a batch, then resubmits only the failed messages with a new idempotency key:
What’s next
- Sending Email — full payload reference for each message
- Rate Limits & Quotas — all size and rate ceilings
- Templates — Liquid templating with per-recipient variables
- Webhooks & Events — delivery and engagement events for every message