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

Body Parameters

The request body is an array of email objects (max 100).

fromstringrequired

Sender email address. 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. Max 50.

subjectstringrequired

Email subject.

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(es).

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.

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.app/emails/batch' \
  -H 'Authorization: Bearer re_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>"
    }
  ]'
import { Resend } from 'resend';

const resend = new Resend('re_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" }
  ]
}