> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lettr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch Sending

> Send the same email to up to 50 recipients per Lettr API call, personalize content with merge tags, and split larger lists into batches

When you need to send the same email to many recipients — newsletters, announcements, reports, or notifications — batch sending lets you do it efficiently. Each API call accepts up to 50 recipients, and you can personalize content per recipient using merge tag variables. For lists larger than 50, you split recipients into batches and send them sequentially or in parallel.

## Multiple Recipients Per Request

Pass an array of up to 50 email addresses in the `to` field. Lettr delivers a separate copy of the email to each recipient:

```javascript theme={null}
await lettr.emails.send({
  from: 'you@example.com',
  to: [
    'alice@example.com',
    'bob@example.com',
    'carol@example.com'
    // ... up to 50 recipients
  ],
  subject: 'Team Announcement',
  html: '<p>Important update for the team!</p>'
});
```

## Personalization with Substitution Data

Use `substitution_data` to provide merge tag values that apply to all recipients in a batch. This is useful for shared variables like campaign names or dates:

```javascript theme={null}
await lettr.emails.send({
  from: 'you@example.com',
  to: ['john@example.com', 'jane@example.com', 'bob@example.com'],
  subject: 'Your monthly report',
  html: '<p>Here is the report for {{month}}.</p>',
  substitution_data: {
    month: 'January'
  }
});
```

## Batch Limits

| Limit                      | Value                                        |
| -------------------------- | -------------------------------------------- |
| Max recipients per request | 50 (combined to, cc, bcc)                    |
| Max requests per minute    | See [Rate Limits](/api-reference/rate-limit) |

## Sending to Large Lists

For recipient lists larger than 50, chunk the list into batches and send each batch with a separate API call. Adding a short delay between batches helps you stay within [rate limits](/api-reference/rate-limit):

```javascript theme={null}
const recipients = [...]; // Array of 500 recipients
const batchSize = 50;

for (let i = 0; i < recipients.length; i += batchSize) {
  const batch = recipients.slice(i, i + batchSize);

  await lettr.emails.send({
    from: 'you@example.com',
    to: batch,
    subject: 'Newsletter',
    html: '<p>Newsletter content...</p>'
  });

  // Optional: Add delay between batches to respect rate limits
  await sleep(100);
}
```

## Parallel Batch Sending

For faster throughput, send multiple batches concurrently using `Promise.all`. This reduces total send time proportionally to the number of parallel batches, but be mindful of your rate limits:

```javascript theme={null}
const recipients = [...]; // Large recipient list
const batchSize = 50;
const parallelBatches = 5;

const batches = chunkArray(recipients, batchSize);

// Process batches in parallel groups
for (let i = 0; i < batches.length; i += parallelBatches) {
  const parallelGroup = batches.slice(i, i + parallelBatches);

  await Promise.all(
    parallelGroup.map(batch =>
      lettr.emails.send({
        from: 'you@example.com',
        to: batch,
        subject: 'Newsletter',
        html: '<p>Content...</p>'
      })
    )
  );
}
```

## Response for Batch Sends

The response indicates how many recipients were accepted or rejected:

```json theme={null}
{
  "message": "Email queued for delivery.",
  "data": {
    "request_id": "7582751837467401763",
    "accepted": 48,
    "rejected": 2
  }
}
```

<Note>
  Rejected recipients may be on your suppression list or have invalid email addresses. Check your suppression list and validate addresses before sending.
</Note>

## Best Practices

<AccordionGroup>
  <Accordion title="Validate Addresses First">
    Run email addresses through basic format validation and check them against your local suppression list before including them in a batch. Each rejected address in a batch send still counts against your rate limit, and high rejection rates can signal list quality issues to Lettr.
  </Accordion>

  <Accordion title="Use Personalization">
    Personalized emails (using the recipient's name, relevant data, or tailored content) achieve significantly higher open and click rates than generic messages. Use `substitution_data` to customize merge tags without making separate API calls.
  </Accordion>

  <Accordion title="Respect Rate Limits">
    Add a delay between batches proportional to your API rate limit. If your limit is 300 requests per 5 minutes, spacing batches 1 second apart lets you send to 15,000 recipients per 5-minute window (300 batches × 50 recipients) without hitting limits.
  </Accordion>

  <Accordion title="Handle Partial Failures">
    A batch send can partially succeed — some recipients are accepted while others are rejected (due to suppression or validation). Always check the `accepted` and `rejected` counts in the response and log rejected addresses for investigation rather than silently dropping them.
  </Accordion>

  <Accordion title="Use Webhooks for Delivery Status">
    With batch sends, polling the API for each email's delivery status is impractical. Set up webhooks to receive `message.delivery`, `message.bounce`, and `engagament.open` events, which let you track outcomes for every recipient asynchronously.
  </Accordion>
</AccordionGroup>

## Tracking Batch Emails

Attach metadata to batch sends so you can query all emails from a specific batch later. Include a unique `batch_id` along with any campaign or timing information relevant to your use case:

```javascript theme={null}
const batchId = `batch_${Date.now()}`;

await lettr.emails.send({
  from: 'you@example.com',
  to: batchRecipients,
  subject: 'Newsletter',
  html: '<p>Content...</p>',
  metadata: {
    batch_id: batchId,
    campaign: 'january-newsletter',
    send_date: new Date().toISOString()
  }
});
```

You can then use the `request_id` returned from each batch send to look up emails via the [Email History](/learn/sending/email-history) API. Metadata is also included in [webhook payloads](/learn/webhooks/introduction), allowing you to correlate delivery events with your batch data.
