Send Batch Emails
Send up to 100 emails in a single API call. The attachments field is not supported in batch mode.
/emails/batchResend Compatibility: Compatible
Requires an API key with the emails:send permission.
Body Parameters
The request body is an array of email objects (max 100). Each email must provide at least one of html, text, or template.
fromstringSender email address. Required unless a template supplies a default from. To include a friendly name, use the format "Your Name <sender@domain.com>".
tostring | string[]requiredRecipient email address. For multiple addresses, send as an array of strings. The combined number of addresses across to, cc and bcc may not exceed 50.
subjectstringEmail subject. Required unless a template is used; when omitted, the template's default subject is used.
templateobjectSend this email with a saved template. The template must be published. Fields you pass explicitly (from, subject, html, text, reply_to) take precedence over the template's defaults.
idstringrequiredThe template ID (UUID) or alias. A template that does not exist returns 404 not_found.
variablesobjectVariable values keyed by variable name. Values may be strings or numbers; a template's fallback value is used when a key is omitted.
topic_idstringTopic ID (UUID) for subscription management. Each recipient is judged on its own: a contact who opted out of the topic is skipped, and an address that is not a contact is sent only when the topic's default subscription is opt-in. A topic that does not exist in your organization returns 404 not_found ("The specified topic was not found.").
htmlstringThe HTML version of the message.
textstringThe plain text version of the message.
bccstring | string[]BCC recipient email address(es).
ccstring | string[]CC recipient email address(es).
reply_tostring | string[]Reply-to email address. At most one address — the message carries a single Reply-To. Pass a string or a one-element array; two or more addresses are refused.
headersobjectCustom headers to add to the email.
scheduled_atstringSchedule the email to be sent later. ISO 8601 (2026-08-05T11:52:01.858Z) or natural language (in 1 min), up to 30 days ahead. A scheduled email is created but not sent until its time arrives, and can be cancelled until then.
tagsarrayCustom data passed in key/value pairs.
namestringrequiredThe name of the email tag. Max 256 characters.
valuestringrequiredThe value of the email tag. Max 256 characters.
Headers
Idempotency-KeystringheaderAdd an idempotency key to prevent duplicated batch sends. Max 256 characters. Expires after 24 hours.
x-batch-validationstringheaderHow per-item failures are reported: strict (default) or permissive.
In strict mode the batch is sent only if every email in it is valid — one bad item rejects the whole call and nothing is created. In permissive mode the valid emails are created and sent, and the rejected ones come back in the response's errors array.
One difference from Resend: a request body that fails schema validation — a missing from, a malformed address — is still rejected as a whole in permissive mode, with a 400 rather than an errors entry. Permissive reporting covers the per-item checks that run after the body parses: template resolution, topic lookup, sending-domain verification and content safety.
Limits and errors
- Recipients: at most 50 addresses per email across
to,ccandbcc. More than 50 intoalone fails schema validation and refuses the whole request with400 validation_error, in either mode. Over 50 only onceccandbccare counted is refused with400 validation_error; inpermissivemode that email becomes anerrorsentry instead. - Message size: at most 5 MiB per email — subject, HTML, text and headers (batch does not accept attachments). A larger email is refused with
400 validation_error; inpermissivemode it becomes anerrorsentry instead. - Monthly quota: counted per address across the batch. When the organization's remaining allowance cannot cover the batch, the whole request is refused with
403 monthly_quota_exceeded; nothing in it is sent. - Daily limit: free organizations are limited to 100 emails a day. Paid organizations are limited to 100 a day for their first 7 days without a verified domain, 1,000 a day for their first 7 days with a verified domain or from day 7 through day 30, and have no daily limit after 30 days. Over the limit:
429 daily_quota_exceeded. - Rate limit: requests are limited per API key and organization. A request that reaches the rate limiter carries
ratelimit-limit,ratelimit-remainingandratelimit-reset(seconds) — requests refused earlier (validation, quota) may not; a rate-limited request returns429 rate_limit_exceededwithretry-after(seconds).
Response Fields
dataarrayArray of objects containing the id of each created email. Failed emails have an empty id and an error field.
errorsarrayPresent only when x-batch-validation: permissive was sent, and present then even when empty. Each entry is { "index", "message" }, where index is the 0-based position of the rejected email in the request. When it is non-empty, data is no longer index-aligned with the request — the rejected items have no slot in it.
curl -X POST 'https://api.postflare.dev/emails/batch' \
-H 'Authorization: Bearer sk_xxxxxxxxx' \
-H 'Content-Type: application/json' \
-d '[
{
"from": "Acme <hello@acme.com>",
"to": ["user1@example.com"],
"subject": "Hello User 1",
"html": "<p>Hello User 1!</p>"
},
{
"from": "Acme <hello@acme.com>",
"to": ["user2@example.com"],
"subject": "Hello User 2",
"html": "<p>Hello User 2!</p>"
}
]'// Set RESEND_BASE_URL=https://api.postflare.dev in the environment before
// the process starts. Without it the SDK sends to api.resend.com, silently.
import { Resend } from 'resend';
const resend = new Resend('sk_xxxxxxxxx');
const { data, error } = await resend.batch.send([
{
from: 'Acme <hello@acme.com>',
to: ['user1@example.com'],
subject: 'Hello User 1',
html: '<p>Hello User 1!</p>',
},
{
from: 'Acme <hello@acme.com>',
to: ['user2@example.com'],
subject: 'Hello User 2',
html: '<p>Hello User 2!</p>',
},
]);Response
{
"data": [
{ "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" },
{ "id": "b2c3d4e5-f6a7-8901-bcde-f23456789012" }
]
}