Batch sending
Send many distinct emails in one call with sendBatch — native bulk endpoints where the provider has one, graceful sequential fallback everywhere else.
Pro email.sendBatch() sends an array of distinct messages
in one call and returns one result per message, in order. It's a Pro feature —
it needs a verified license even on a free provider, so call await email.init() first.
const batch = await email.sendBatch([
{ to: "ada@example.com", subject: "Hi Ada", text: "…" },
{ to: "grace@example.com", subject: "Hi Grace", text: "…" },
]);
batch.results.length; // 2 — one SendResult per input, same order
batch.batched; // true if a native bulk endpoint was used, false if sequentialEach message is fully prepared first — managed templates and free
{{variable}} interpolation are applied per message before sending, exactly
like send().
Native vs. sequential
Not every provider has a bulk endpoint. sendBatch() handles both transparently:
- If the provider advertises
capabilities.batch, Radon calls its native bulk endpoint — one HTTP request for the whole array. - Otherwise Radon falls back to sequential per-message sends, preserving
order. Same result shape, same
SendResultobjects — just more round trips.
The batched flag on the result tells you which path ran:
const batch = await email.sendBatch(messages);
if (!batch.batched) {
// Sent one-by-one — the provider has no bulk API.
}The fallback is automatic, never silent
You never get a UnsupportedOperationError from sendBatch() for a
provider that lacks a bulk endpoint — the facade sends sequentially instead.
batched: false is how you know, not an exception.
Per-provider caps
Only three providers have a native bulk endpoint today; the rest fall back to sequential.
| Provider | Native batch | Per-request cap |
|---|---|---|
| Resend | Yes (/emails/batch) | 100 messages |
| Postmark | Yes (/email/batch) | 500 messages |
| Mailjet | Yes (Messages array) | Per Mailjet's own limits |
| Everything else | No — sequential fallback | — |
Respect the native cap
Radon sends your array in a single request to a native-batch provider — it
does not auto-chunk. Passing more than Resend's 100 or Postmark's 500 in one
sendBatch() call will be rejected by the provider. Split large lists into
chunks yourself.
Choosing a provider for the batch
Like send(), sendBatch() uses the default provider unless you name one:
await email.sendBatch(messages, { provider: "postmark" });Partial failures
With native batch, providers report per-message outcomes in each SendResult.
Postmark, for example, can return a per-row error inside an otherwise-200 batch —
Radon surfaces the failed rows via accepted / rejected and preserves the
provider's raw response on result.raw for inspection.
for (const r of batch.results) {
if (r.rejected?.length) {
console.warn("rejected:", r.rejected, r.raw);
}
}Full signature
email.sendBatch(messages: EmailMessage[], options?: { provider?: string }): Promise<BatchResult>ProSend many messages. Uses the provider's native bulk endpoint when available
(capabilities.batch), else sends sequentially. Requires a verified license.
Fires the onSent hook once per resulting message.
resultsSendResult[]One SendResult per input message,
in the same order.
batchedbooleantrue if a native bulk endpoint sent them in one request; false if Radon
fell back to sequential sends.
Next steps
Templates
Two template layers in Radon Email — free provider-agnostic {{variable}} interpolation, and Pro managed templates with a pluggable store.
Attachments & scheduling
Attach files as Buffers or base64, embed inline images with content IDs, and schedule sends with sendAt — plus which providers support each.