Alternatives
The SNDR alternative to Mailgun.
A Mailgun alternative engineered for teams that want the same multi-region delivery quality plus first-class observability, idempotency, and a webhook layer built to be replayed.
Mailgun pioneered transactional email infrastructure for developers and has continued to scale through the Sinch acquisition. SNDR's bet is on operational density — a single typed SDK, idempotent send by default, signed webhooks with retries and a replay button, and per-MTA delivery curves visible in the dashboard the moment something starts misbehaving. If you've outgrown Mailgun's UI or want a tighter feedback loop between production and your on-call engineer, this is the page for you.
Side by side
SNDR vs Mailgun
Every row links to where the underlying behavior is documented or implemented.
| Feature | SNDR | Mailgun |
|---|---|---|
| REST API | Single /v1 surface, idempotent send | /v3 messages API + multiple legacy endpoints |
| Official SDK | @rkiza/sndr — Node 18+, Bun, Deno, Workers, Edge | mailgun.js + community ports |
| Delivery network | Multi-MTA pool with weighted routing + circuit breakers | Multi-region IP pools with optional dedicated IPs |
| Webhook signatures | HMAC-SHA256 over timestamp.body, replay-defended | HMAC-SHA256 with timestamp |
| Webhook retries | 12 attempts, exponential backoff with jitter, replay-from-admin | 8 attempts, fixed retry windows |
| Idempotency | Header-driven, SDK auto-generates per call | Not first-class for /messages |
| Per-MTA delivery analytics | Built in | Aggregate per-pool |
| Inbound email | On the roadmap | Yes — mature inbound routes |
| Email validation | Standard syntax + MX check via suppression layer | Paid email validation API |
| Free tier | 3,000 emails / month, no card required | No persistent free tier (paid trial) |
| Billing currency | USD via Paddle (MoR) | USD direct billing |
Drawn from public documentation and pricing as of build time. We try to keep this fair — file an issue if anything is wrong.
Why teams switch
Where SNDR fits better than Mailgun.
Operational observability beats marketing claims
Mailgun’s aggregate dashboards are great for monthly reports. SNDR shows per-MTA, per-recipient-domain delivery curves in real time so an on-call engineer can isolate a misbehaving box in minutes, not days.
Replayable webhooks are not optional
When a customer integration breaks, the most valuable button is "re-fire this exact event with this exact payload." SNDR records every delivery — response code, headers, body — and replays on a single click.
Idempotency on send, by default
The SDK auto-generates an Idempotency-Key per logical send and reuses it across retries. A transient 5xx cannot double-deliver an order receipt. With Mailgun’s /messages endpoint you build this yourself.
Predictable free → paid transition
Mailgun’s post-2021 pricing model removed the persistent free tier. SNDR keeps 3,000 / month free forever, with clean Pro ($12) and Scale ($75) jumps and pro-rated upgrades.
Webhook signature freshness
Every signed payload includes a timestamp; the verifier rejects replays older than 5 minutes by default (tunable). Recovers most of the security posture that token-in-URL or basic-auth approaches lack.
A console that respects your time
Mailgun’s control panel is comprehensive but legacy-dense. SNDR’s admin focuses on the dozen things an operator actually does — replay, suspend with reason, watch MTA pool health, audit-log everything.
Migration
Moving off Mailgun's SDK.
The SNDR SDK uses the same primitives as most modern email clients — install, instantiate with a key, call emails.send. In most codebases the migration is a focused diff.
- 1
Install the SDK
Add @rkiza/sndr alongside mailgun.js — they coexist while you cut over.
pnpm add @rkiza/sndr - 2
Replace the send call
Mailgun’s messages.create() maps directly. Headers become a typed object; tags become structured key/value pairs for filterable analytics.
// before import formData from 'form-data'; import Mailgun from 'mailgun.js'; const mg = new Mailgun(formData).client({ username: 'api', key: process.env.MAILGUN_API_KEY! }); await mg.messages.create('mg.yourdomain.com', { from: 'me@yourdomain.com', to: 'a@b.com', subject: 'hi', html: '<p>hi</p>', }); // after import { Sndr } from '@rkiza/sndr'; const sndr = new Sndr(process.env.SNDR_API_KEY!); await sndr.emails.send({ from: 'me@yourdomain.com', to: 'a@b.com', subject: 'hi', html: '<p>hi</p>', }); - 3
Migrate webhook handlers
Mailgun posts form-encoded data; SNDR posts JSON. Verify with the shipped helper and switch your handler to the discriminated-union event shape.
import { verifyWebhookSignature } from '@rkiza/sndr'; app.post('/webhooks/sndr', express.raw({ type: 'application/json' }), (req, res) => { if (!verifyWebhookSignature(req.body, req.header('x-sndr-signature') ?? '', process.env.SNDR_WEBHOOK_SECRET!)) { return res.status(400).send('bad signature'); } const event = JSON.parse(req.body.toString('utf8')); switch (event.type) { case 'email.delivered': /* event.data.email_id, event.data.to */ break; case 'email.bounced': /* event.data.email_id, event.data.error */ break; case 'email.unsubscribed': /* event.data.addresses */ break; } res.status(200).send('ok'); }); - 4
Reuse your DNS
SPF can keep both providers’ include: directives during cutover. Add a fresh DKIM CNAME or TXT for the SNDR sending domain — Mailgun’s key stays put.
- 5
Shadow + cut over
Roll a percentage of sends through SNDR via a feature flag. When per-recipient-domain inbox rate matches Mailgun on the dashboard, flip the rest. Most teams complete the move in under a week.
Want a hand migrating from Mailgun?
We've helped teams move millions of monthly sends across providers without dropping a single delivery. Reach out and we'll walk through your stack.
No shade — Mailgun is a great product. SNDR just optimises for different things.
Start building
Your next message starts here.
Connect your domain, create an API key, and send with SNDR.