Send Batch Emails

Send up to 100 emails in a single API call. The attachments field is not supported in batch mode.

POST/emails/batch

Resend 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.

fromstring

Sender 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[]required

Recipient 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.

subjectstring

Email subject. Required unless a template is used; when omitted, the template's default subject is used.

templateobject

Send 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.

idstringrequired

The template ID (UUID) or alias. A template that does not exist returns 404 not_found.

variablesobject

Variable values keyed by variable name. Values may be strings or numbers; a template's fallback value is used when a key is omitted.

topic_idstring

Topic 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.").

htmlstring

The HTML version of the message.

textstring

The 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.

headersobject

Custom headers to add to the email.

scheduled_atstring

Schedule 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.

tagsarray

Custom data passed in key/value pairs.

namestringrequired

The name of the email tag. Max 256 characters.

valuestringrequired

The value of the email tag. Max 256 characters.

Headers

Idempotency-Keystringheader

Add an idempotency key to prevent duplicated batch sends. Max 256 characters. Expires after 24 hours.

x-batch-validationstringheader

How 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, cc and bcc. More than 50 in to alone fails schema validation and refuses the whole request with 400 validation_error, in either mode. Over 50 only once cc and bcc are counted is refused with 400 validation_error; in permissive mode that email becomes an errors entry 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; in permissive mode it becomes an errors entry 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-remaining and ratelimit-reset (seconds) — requests refused earlier (validation, quota) may not; a rate-limited request returns 429 rate_limit_exceeded with retry-after (seconds).

Response Fields

dataarray

Array of objects containing the id of each created email. Failed emails have an empty id and an error field.

errorsarray

Present 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

Response
{
  "data": [
    { "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" },
    { "id": "b2c3d4e5-f6a7-8901-bcde-f23456789012" }
  ]
}