Resend vs Mailgun: developer experience and what 100,000+ emails cost
Resend gets you to a first email with an API key, a verified domain and one SDK call. Mailgun's quickstart adds a sandbox domain and an inbox click to authorize a recipient before anything is delivered. On price, Resend is lower at every volume both vendors' sliders show: $35 against $75 at 100,000 emails a month, roughly $200 (derived below) against $215 at 250,000, and $350 against $400 at 500,000, so Mailgun costs about twice as much at 100,000 and 8 to 14 percent more from 250,000 up (215 ÷ 200 and 400 ÷ 350). Mailgun's case rests on breadth: 30 days of logs on Scale, inbound routing, 1,000 sending domains from Foundation up, and one dedicated IP bundled with Foundation at 100,000. Choose Resend for a new app on a modern stack and Mailgun when you need its retention, routing or longer plan ladder.


The first email: what each quickstart asks of you
Resend's Node.js guide lists two prerequisites, an API key and a verified domain, then one install command and one call. The sample sends from onboarding@resend.dev to delivered@resend.dev, a test pair that works before your own domain is verified; the same page warns against shipping that sender to production and lists bounced@resend.dev, complained@resend.dev and suppressed@resend.dev as test recipients that simulate those outcomes without hurting a real domain.
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
(async function () {
const { data, error } = await resend.emails.send({
from: 'Acme <onboarding@resend.dev>',
to: ['delivered@resend.dev'],
subject: 'Hello World',
html: '<strong>It works!</strong>',
});
if (error) {
return console.error({ error });
}
console.log({ data });
})(); Mailgun's quickstart sends from a sandbox domain. You copy a private API key, register your own address as an authorized recipient with one request, click the verification link Mailgun emails you, and only then send the message below. A successful call answers Queued. Thank you. The extra step protects the sandbox from abuse but means a teammate's address cannot be used until that person clicks. Accounts on the EU region use api.eu.mailgun.net instead of api.mailgun.net, per the send reference.
curl --user 'api:YOUR_API_KEY' \
https://api.mailgun.net/v3/YOUR_SANDBOX_DOMAIN/messages \
-F from='Test <postmaster@YOUR_SANDBOX_DOMAIN>' \
-F to='your-email@example.com' \
-F subject='Hello!' \
-F text='Test message' Both calls are short. The difference shows up around the edges: Resend's SDK returns a { data, error } object rather than throwing, accepts a React Email component through a react field in Node, and supports idempotency keys that expire after 24 hours. Mailgun's REST call is form-encoded, sends options through o: parameters such as o:testmode, o:tracking and o:sending-ip-pool, and lets you hand over a stored template by name.
Cost from 10,000 to 500,000 emails
| Monthly emails | Resend | Mailgun |
|---|---|---|
| 10,000 | $20 (Pro, includes 50,000) | $15 (Basic) |
| 50,000 | $20 (Pro) | $35 (Foundation) |
| 100,000 | $35 (Pro, slider) | $75 (Foundation, slider) |
| 250,000 | $200 (derived below) | $215 (Scale, slider) |
| 500,000 | $350 (Scale, slider) | $400 (Scale, slider) |
The $35 and $350 figures for Resend and the $75, $215 and $400 figures for Mailgun are slider readouts from October 7, 2026. Resend's own page shows only the 3,000 position on first load, so drag it to 100,000 to see Pro move to $35. There is no stop at 250,000 on Resend's slider. The $200 in the table is the Scale price at 200,000 emails plus 50,000 more at the $0.80-per-1,000 rate shown beside it: 160 + 50 × 0.80 = 200.
Overage is the trap on both sides. Resend lets a paid plan keep sending past its quota through pay-as-you-go at $0.90 per 1,000 extra emails on Pro. Staying on the 50,000 tier and sending 100,000 would cost 20 + 50 × 0.90 = 65, nearly double the $35 you pay by moving the slider. Mailgun's overage rates start at $1.80 per 1,000 on Basic, $1.30 on Foundation and $1.10 on Scale, and the slider again beats the arithmetic: Foundation with 50,000 extra emails at $1.30 would be 35 + 50 × 1.30 = 100, against $75 on the slider. Pick the plan that matches your real volume and treat overage as a safety net.
Resend's rates fall as you climb (extra emails drop from $0.90 to $0.80 at 200,000 emails, to $0.70 at 500,000 and to $0.65 at 1,000,000), and the Scale price at 1,000,000 emails is $650. Billing differs too: Resend bills monthly with no annual discount and no free trial, only a free plan, while Mailgun's Foundation and Scale plans start with a free first month. Full tables live on Resend pricing and Mailgun pricing.
What the 50,000-email plans include
| At the 50,000 level | Resend Pro | Mailgun Foundation |
|---|---|---|
| Log or data history | 30 days | 5 days of logs, 1 day of message content |
| Sending domains | 10, plus 100 more for $20 a month | 1,000 |
| Webhook endpoints | 5 | webhooks included, no endpoint count listed |
| Daily sending limit | none | none |
| Dedicated IP | Scale plan only, $30 a month | one included at 100,000, extras $59 each |
On Free, the two start closer than the headline suggests: Resend allows 3,000 emails a month capped at 100 a day with 3 domains, one webhook endpoint and 30 days of data retention, and Mailgun allows 100 a day with a single domain and one day of logs. Resend's Scale plan, at $90 for 100,000 emails, adds 1,000 domains, 10 webhook endpoints and a dedicated Slack channel, and its dedicated IP add-on is open to Scale customers exceeding 3,000 emails a day.
Webhooks and event data
Resend's event list is long: email.sent, email.delivered, email.delivery_delayed, email.bounced, email.complained, email.failed, email.opened, email.clicked, email.scheduled, email.suppressed and email.received, plus domain, contact and suppression events. Requests are signed with a per-endpoint secret and can be checked with Resend's SDK or Svix libraries, and retries use the same svix-id, which makes deduplication simple. Failed deliveries are retried on a schedule that runs from immediately, through 5 seconds, 5 minutes, 30 minutes, 2, 5 and 10 hours, to a final 10-hour wait; an endpoint that keeps failing is disabled and your team is emailed. Any message can be replayed from the dashboard (retries and replays).
Mailgun's webhooks use plain event names (accepted, delivered, temporary_fail, permanent_fail, opened, clicked, unsubscribed, complained), can be set at account or domain level, and are authenticated with an HMAC-SHA256 over timestamp and token using one account-wide signing key (securing webhooks). Inbound routes use the same signing. If you consume only delivery and bounce events, both are equally workable; if you want to react to scheduled, suppressed or delayed mail with distinct events, Resend exposes more of them.
DNS and authentication work
Resend tells you to add a subdomain such as notifications.example.com rather than the root domain, to paste the DKIM and SPF records it generates (TXT plus MX or CNAME), and to leave the Return-Path at its default send subdomain unless you choose another. It warns that a proxied CNAME, for instance Cloudflare's orange cloud, blocks verification, and says domains often verify in about 15 minutes but can take up to 72 hours (add a domain). Mailgun asks for an SPF TXT, a DKIM TXT, two MX records at priority 10 and a tracking CNAME, and quotes 24 to 48 hours for propagation (verification guide). The principles are covered in our SPF, DKIM and DMARC guide.
Who should pick which
Resend fits a team that wants the cheapest managed plan from 10,000 to 500,000 emails, writes in TypeScript or another language with an official SDK, builds templates in React, and values 30 days of data on every plan including Free. Its limits are the single dedicated-IP route (Scale only), 5 webhook endpoints on Pro and a rate limit that is set per team and does not rise with the plan.
Mailgun fits a team that needs inbound routes, long log retention on a plan it already pays for, a template builder, many sending domains for clients, or a dedicated IP at 100,000 emails without an add-on request. It costs more at every volume we could compare, and its entry plans keep a single day of logs.
If neither matches, read Resend vs Postmark, Postmark vs Mailgun, Resend alternatives and Mailgun alternatives.
Switching between them
- Verify a new sending subdomain at the destination and leave production DNS alone; two SPF records on one name make both fail, so merge any new
include:into the existing record. - Export suppressed, bounced and unsubscribed addresses from the old account and load them before the first live message.
- Port the send call. Resend takes
from,to,subjectandhtmlas one object, while Mailgun takes form fields and a domain in the URL. Add an idempotency key on Resend for any retried job. - Map webhook events.
deliveredbecomesemail.delivered,permanent_failmaps to a bounce andcomplainedtoemail.complained. Swap the HMAC check for Resend's signature verification, or the reverse. - Re-create templates and any inbound routes. Resend receives mail through its inbound feature, which counts against the same email quota.
- Shift traffic gradually and keep the old provider's records live until its queue is empty.
FAQ
Is Resend always cheaper than Mailgun?
Not at the smallest paid size. At 10,000 emails Mailgun Basic is $15 and Resend Pro is $20, although Resend's Free plan covers 3,000 emails. From 50,000 emails upward, Resend is lower on every price either vendor publishes.
Which has better log retention?
Resend keeps 30 days on all plans. Mailgun keeps 1 day on Free and Basic, 5 on Foundation and 30 on Scale.
Do both support dedicated IPs?
Yes, with different gates. Resend sells a $30 monthly add-on to Scale customers sending more than 3,000 emails a day. Mailgun bundles one IP with Foundation at 100,000 or Scale and charges $59 per extra IP per month.
Can I test without hurting my sender reputation?
Resend offers test recipient addresses that simulate delivered, bounced, complained and suppressed outcomes. Mailgun offers a sandbox domain limited to authorized recipients and an o:testmode option that processes a message without delivering it.