BULK API

Thousands of personalized emails in one request

Stop looping your send endpoint one recipient at a time. Post an array, give each recipient their own template variables, and get one response back — sent from your own server and IP, billed per send, never per contact.

One call. Per-recipient everything.

The bulk endpoint takes an array of messages. Each entry carries its own recipient, subject, and merge variables — so every email is fully personalized without a request per person.

01 / BATCH

Up to 500 per request

Send a batch of up to 500 individually-addressed messages in a single POST. Chunk your list, fire the calls, done — no client-side rate-limit juggling between every recipient.

02 / MERGE

Per-recipient variables

Attach a data object to each entry — first name, order number, reset link, anything. Reference it in a stored template or inline HTML with {{ handlebars }} and mailstein renders it per recipient.

03 / RESULT

One response, per-message IDs

Get back an array of results in the same order you sent them, each with its own message ID or error. Persist the IDs, match webhook events, retry only the entries that failed.

SEND

Batch send, fully typed

The same emails resource you already use, with a batch method. One array in, one array out. Idempotent per entry, so a retried request never double-sends a recipient who already went through.

Transactional receipts, password resets, a 40,000-row broadcast — same endpoint, same auth, same logs on your own infrastructure.

// one call, every recipient personalized const { data } = await mailstein.emails.batch({ from: "receipts@boardpearls.com", template: "order-confirmation", messages: [ { to: "amir@example.com", vars: { name: "Amir", order: "BP-4821" } }, { to: "lena@example.com", vars: { name: "Lena", order: "BP-4822" } } // … up to 500 per request ] }) // data.results[] — one entry per message, in order data.results.forEach(r => console.log(r.id ?? r.error))

Built for volume without the gotchas

Batch sending is only useful if it stays deliverable and debuggable at scale. mailstein handles the parts that break naive bulk loops.

DELIVERABILITY

Warmed IP, signed on the way out

Every message in the batch leaves with SPF, DKIM, and DMARC applied automatically, from a dedicated IP with managed warm-up. Suppressed addresses (hard bounces, complaints) are skipped before send, so a stale row in your array can't cost you reputation.

A 30k batch with 200 dead addresses: the dead ones are dropped, reported back as suppressed, and never touch your sending IP.
BILLING

Per send, not per contact

A batch of 500 costs 500 sends — that's it. There's no per-contact marketing tax and no separate "broadcast product" to license. The same batch endpoint powers a 12-message receipt run or a 500k-recipient announcement.

Free tier covers 50 emails/month on one domain with no card; paid scales by volume — roughly $130 at 125k/mo, $520 at 700k/mo.

Partial failures don't sink the batch

ORDERED

Results map 1:1

results[3] is the outcome of messages[3]. No guessing which recipient a message ID belongs to when you reconcile against your database.

GRANULAR

One bad row, one error

A malformed address or missing variable fails only its own entry. The other 499 send normally and report success — the batch isn't rejected wholesale.

TRACEABLE

Webhooks per message

Delivered, opened, bounced, and complained events fire per message ID and land on your webhook — plus every send is queryable in your own message logs.

Send the whole list in one call.

Batch API, per-recipient variables, your own IP and logs. Start free — 50 emails a month, one domain, no card.

Start free