Bulk sending: batches & broadcasts
This guide covers reaching many recipients: the batch endpoint, POST /v1/email/batches, and where audience-targeted broadcasts fit today. If you only ever send one message at a time, start with sending email instead.
When to use what
- A handful of independent messages at once — use a batch. One request carries up to 100 complete message objects and saves you 100 round trips.
- High-volume sending from your own loop — calling the single-send endpoint repeatedly is a perfectly good architecture; batches don't make a message cheaper or faster to deliver, they just amortize the HTTP overhead. The real volume lever is rate limits: batch requests draw from the email_batch rate-limit group, separate from the email_send group used by single sends, so batching raises how many messages you can hand over per unit of wall-clock time.
- One message to a stored audience — that's a broadcast, covered below. Batches are not broadcasts: there is no shared content definition, no audience targeting, and no per-recipient personalization. Every message in a batch is a self-contained payload you assemble yourself.
Batch sends
POST /v1/email/batches takes a JSON array of 1–100 message objects. Each item is a complete, independent send request — its own from, to, subject, content, and optionally its own category, ip_pool, tags, and metadata. The item schema is exactly the single-send payload, so everything in sending email applies per item, including the category default of transactional.
Code example
curl -X POST https://us1.platform.bird.com/v1/email/batches \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: batch-2026-06-10-001" \
-d '[
{
"from": "hello@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"subject": "Your receipt",
"html": "<p>Thanks for your order.</p>"
},
{
"from": "hello@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"subject": "June product news",
"html": "<p>What shipped this month.</p>",
"category": "marketing"
},
{
"from": "alerts@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"subject": "Usage threshold reached",
"text": "You have used 80% of your quota."
}
]'All-or-nothing validation
Every item is validated before any item is queued. If one message fails — a field-level validation error, an unverified sender domain, all of an item's recipients suppressed — the entire batch is rejected with a 422 and nothing is sent. A batch never partially succeeds at accept time, so you never have to work out which half of a failed request went through; fix the offending item and resubmit the whole array.
The 202 response
A successful batch returns 202 Accepted with one entry per message, in submission order:
Code example
{
"data": [
{ "id": "em_019c1930687b7bfa...", "status": "accepted", "category": "transactional" },
{ "id": "em_019c1930687c4e21...", "status": "accepted", "category": "marketing" },
{ "id": "em_019c1930687d9b02...", "status": "accepted", "category": "transactional" }
]
}Each child is a regular message from this point on: track it by its em_ ID through GET /v1/email/messages/{id}, its recipient and event endpoints, and webhooks — exactly as if it had been sent individually. The same async model applies: 202 means durably accepted, and per-recipient delivery outcomes arrive afterwards.
Idempotent retries
Send an Idempotency-Key header with the batch (as in the example above). If the request succeeded but you never saw the response, replaying it with the same key returns the original result — the same child message IDs, with an Idempotency-Replay header — instead of sending every message again. With up to 100 messages per request, the cost of an accidental duplicate is multiplied, so treat the key as required in production. See idempotency.
Attachments are not supported
Batch items cannot carry attachments — any item that includes them rejects the whole batch with a 422 (AttachmentsNotSupportedOnBatch). Attachments are a reserved field on the single-send endpoint too in v1; when they ship, they will be a single-send capability, not a batch one.
Broadcasts
A broadcast is one message sent to a stored audience — Bird resolves the audience's current members (minus suppressions) into the recipient set at send time, instead of you enumerating addresses. Today, audience broadcasts are available from the Bird dashboard; they are not yet part of the public API. The broadcast_id and audience_id fields you may notice in the send vocabulary are reserved — including either in an API send returns a 422 for now, so SDKs and integrations converge on the names before launch.
Until the broadcast API ships, the two API-side options for reaching many recipients are batch sends and fanning out over the single-send endpoint from your own loop. When the public surface lands, the API reference will carry it — don't build against the reserved fields in the meantime.
Next steps
- Sending email — the per-item payload in full: fields, limits, tags vs metadata
- Categories — marketing vs transactional and what each does to suppression policy
- Idempotency — key format, retention, and replay semantics
- API reference — full batch request and response schemas