---
title: Migrating from Resend to AWS SES: A Practical Guide
description: Migration guide for engineers moving from Resend to AWS SES. React Email portability, Audiences and Broadcasts replacements, pricing math, and the full event pipeline you will own after the cutover.
url: https://www.factualminds.com/compare/resend-to-aws-ses/
publishDate: 2026-04-27
updateDate: 2026-06-16
---

# Migrating from Resend to AWS SES: A Practical Guide

> Resend earned its reputation on a clean SDK, React Email, and a developer experience that makes 5,000-message-per-month senders feel like first-class citizens. Once volume climbs past the included Pro tier or AWS consolidation enters the picture, SES becomes the next stop. The full migration runbook — React Email portability, Audiences and Broadcasts replacements, the SES event pipeline you will own, and the deliverability discipline that keeps inbox placement steady through the cutover.

<div class="quick-answer">

**Quick Answer:** AWS SES is usually the better fit when you need lower unit email cost, AWS-native eventing, and tighter deliverability controls at scale.

</div>

## Freshness Check (June 2026)

In this cycle, SES updates include tenant-level suppression lists, inbox placement metrics, and email validation capabilities that materially improve migration operating models.

This page was refreshed against official AWS announcements and service documentation published in the last 12 months. Confirm region support, quotas, and pricing before final architecture sign-off.

- [AWS What's New](https://aws.amazon.com/about-aws/whats-new/)
- [SES announcement updates](https://aws.amazon.com/about-aws/whats-new/2026/06/amazon-ses-tenant-level-suppression-lists/)

> **TL;DR.** If you are migrating only transactional, plan 1–2 weeks on the SES side and keep React Email as-is. If you also need to replace Audiences and Broadcasts, plan an additional 1–2 engineering weeks for the contact store + bounce-sync layer described below.

Resend launched in 2023 with a clear bet: developers writing React in 2026 want to write email in React, see template previews instantly, and ship from a typed SDK without thinking about MIME boundaries or SMTP timeouts. The bet paid off — Resend is the email vendor of choice for new SaaS teams, indie shipped projects, and AI-era startups that grew up on Vercel and Linear. AWS SES is where those teams move when the platform engineering function arrives, when finance starts asking about per-vendor spend, or when a parent product on AWS consolidates infrastructure under one cloud.

This guide is written for the engineer running that migration.

## Where Resend and SES Sit in the Market

Resend is a managed email API designed around modern developer experience. It ships with an opinionated React Email integration, a polished dashboard, generous tooling, and pricing that targets early-stage teams. Operationally it sits on top of multiple sending backends — AWS SES is publicly disclosed as one of them — and abstracts the sending pool, IP rotation, and feedback loops away from you.

AWS SES is a raw email-sending service. There is no template editor, no dashboard analytics beyond reputation metrics, no contact list manager, and no campaign scheduler. What you get is a high-throughput send API, configuration sets for routing and tagging, an event firehose, and IAM-controlled access. The product is a primitive; the value comes from what you build around it.

The migration is fundamentally about deciding to own more of the stack in exchange for control, observability, and lower per-message cost.

## The Pricing Curve

Resend's pricing is designed for two segments: free for early development, Pro for shipped products under 50,000 sends per month, then a step into marketing-tier pricing for senders running broadcasts.

| Volume           | Resend                     | AWS SES       | Monthly Difference  |
| ---------------- | -------------------------- | ------------- | ------------------- |
| 3,000 emails     | $0 (Free tier)             | $0.30         | Resend +$0.30       |
| 50,000 emails    | $20/month (Pro)            | $5.00/month   | $15.00 SES savings  |
| 100,000 emails   | ~$45/month (Pro + overage) | $10.00/month  | $35.00 SES savings  |
| 500,000 emails   | ~$200/month (custom tiers) | $50.00/month  | $150.00 SES savings |
| 1,000,000 emails | ~$350/month (custom tiers) | $100.00/month | $250.00 SES savings |

At low volume the cost gap is small enough that Resend's developer experience pays for itself. The crossover where SES becomes obvious typically lands somewhere between 200,000 and 500,000 messages per month — the volume where engineering time to build event pipelines and broadcast tooling is recovered within one to two quarters of saved spend.

Two non-pricing drivers usually accelerate the decision:

- **IP control** — Resend manages the sending pool for you. If a noisy neighbor on the shared pool damages reputation, your inbox placement suffers and there is no escalation path other than "wait for it to recover." SES dedicated IPs at $24.95 per IP per month give you a reputation signal that is yours to manage.
- **AWS-native consolidation** — IAM-based credentials replace API keys, KMS handles encryption of suppression data, VPC endpoints keep traffic off the public internet, and CloudWatch unifies email metrics with the rest of your platform telemetry.

## API Migration: Resend SDK → AWS SES SDK

The Resend SDK and the AWS SDK for SES are both well-typed and idiomatic. The shape of a send call is conceptually identical; only the surface changes.

| Resend                                      | AWS SES Equivalent                                 | Notes                                                                                     |
| ------------------------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `resend.emails.send()`                      | `SendEmailCommand` via `@aws-sdk/client-ses`       | Core send operation. Same `from`/`to`/`subject` shape.                                    |
| `react: <Email />` shorthand                | `render(<Email />)` from `@react-email/render`     | Render in app code, pass HTML and text to SES.                                            |
| `tags: [{ name, value }]`                   | `Tags: [{ Name, Value }]`                          | Direct mapping. Used for CloudWatch breakdowns and event filtering.                       |
| `headers: { 'X-Entity-Ref-ID': '...' }`     | `Headers` array on `SendEmailCommand`              | Custom headers pass through unchanged.                                                    |
| `attachments: [{ filename, content }]`      | `SendRawEmailCommand` with MIME-encoded attachment | SES requires the raw MIME path for attachments. Use Nodemailer or `mimetext` to assemble. |
| API key in `Authorization: Bearer` header   | IAM credentials via SDK or SES SMTP credentials    | Prefer IAM roles in production (no key rotation).                                         |
| Webhook signing via `Svix-Signature` header | SNS message signature verification                 | SNS subscriptions sign messages; verify with the AWS SDK helper.                          |
| `resend.emails.batch.send()` (up to 100)    | `SendBulkEmailCommand` (up to 50 destinations)     | Loop and batch for larger lists.                                                          |

A typical Resend-to-SES code change for a transactional send:

```typescript
// Before — Resend
import { Resend } from 'resend';
import VerifyEmail from './emails/verify';

const resend = new Resend(process.env.RESEND_API_KEY);

await resend.emails.send({
  from: 'Acme <hello@acme.com>',
  to: user.email,
  subject: 'Verify your email',
  react: <VerifyEmail name={user.name} url={verifyUrl} />,
  tags: [{ name: 'category', value: 'verification' }],
});

// After — AWS SES
import { render } from '@react-email/render';
import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
import VerifyEmail from './emails/verify';

const ses = new SESClient({ region: 'us-east-1' });
const html = await render(<VerifyEmail name={user.name} url={verifyUrl} />);
const text = await render(<VerifyEmail name={user.name} url={verifyUrl} />, {
  plainText: true,
});

await ses.send(
  new SendEmailCommand({
    Source: 'Acme <hello@acme.com>',
    Destination: { ToAddresses: [user.email] },
    Message: {
      Subject: { Data: 'Verify your email' },
      Body: {
        Html: { Data: html },
        Text: { Data: text },
      },
    },
    ConfigurationSetName: 'transactional',
    Tags: [{ Name: 'category', Value: 'verification' }],
  })
);
```

The SES version is more verbose, but every concept maps one-to-one. React Email components, props, and styling are unchanged.

## React Email Stays — That Is the Point

The single biggest concern engineers raise about leaving Resend is template portability. React Email has no Resend lock-in. The library is open source, owned by the Resend team but designed from day one to be provider-agnostic. Components, layouts, Tailwind classes, dark mode handling, and plain-text rendering all continue to work after the migration.

What you keep:

- Every component in `@react-email/components` — `Html`, `Body`, `Container`, `Section`, `Button`, `Heading`, `Text`, `Hr`, `Tailwind`
- Storybook or `react-email preview` local rendering for design iteration
- TypeScript prop types on email templates
- Plain-text generation via the `plainText: true` render option

What you lose:

- The `react:` send shortcut — you render the component in application code instead
- Resend dashboard preview history per send

A clean pattern is to put a thin `sendEmail()` helper in your shared infrastructure module that wraps the SES SDK and accepts a React Email component. Every send site in the application then looks like `sendEmail({ to, subject, component: <VerifyEmail ... /> })`, and you have a single place to add tagging, idempotency, and suppression logic.

## Replacing Resend Audiences and Contacts

Resend Audiences holds your contact list and tracks subscribe/unsubscribe state. SES has no equivalent — you build the contact store yourself. The model is small and the build is one to two engineering weeks.

A working contact store on AWS for a team in the 100K–1M sends-per-month range:

- **DynamoDB table** keyed on `audience_id#email` with attributes for `status` (subscribed, unsubscribed, bounced, complained), `subscribed_at`, `unsubscribed_at`, `metadata` (the `data` payload from Resend Audiences), and a `gsi_status` GSI for status-based queries.
- **List-Unsubscribe handler** — a Lambda fronted by API Gateway that flips the contact's `status` to `unsubscribed` when a recipient hits the one-click unsubscribe link required by Gmail and Yahoo for any sender above 5,000 messages per day.
- **Subscribe endpoint** — same Lambda pattern, double-opt-in flow recommended for marketing audiences to keep complaint rate low.
- **Bounce/complaint sync** — SES SNS events feed into the same table; hard bounces and complaints flip status automatically.

Migration data flow:

1. Export each Resend Audience via the Resend API (`GET /audiences/:id/contacts`)
2. Import into DynamoDB with status preserved (subscribed, unsubscribed, etc.)
3. Verify your unsubscribe URLs in existing emails resolve to the new endpoint or set up a temporary redirect
4. Cut over send code to read from the new contact store

Building this is one to two engineering weeks for a focused team — meaningfully more if you also need a marketing-team-friendly admin UI to manage contacts, in which case you should evaluate whether keeping marketing on Resend (or a marketing-specific platform like Customer.io or Loops) and only moving transactional to SES is the right architecture.

## Replacing Resend Broadcasts

Broadcasts are scheduled, audience-targeted email campaigns with engagement reporting. The SES equivalent is a small orchestration layer:

```
EventBridge Scheduler (cron-style trigger)
  ↓
Step Functions state machine
  ↓ Distributed Map over audience contacts (parallelism: 100)
  ↓
Lambda (per-contact send)
  ↓ render React Email → SendBulkEmailCommand → SES
SES → recipient
  ↓
SES Configuration Set → Kinesis Firehose → S3 (raw events)
  ↓
Athena queries → broadcast_id → engagement metrics
```

Step Functions Distributed Map is the right primitive — it parallelizes the per-contact send up to 10,000 concurrent executions and gives you per-batch retry semantics out of the box. Compared to a hand-rolled SQS-driven sender, you get a visual execution view, structured failure handling, and a single execution ARN per broadcast that ties together logs, metrics, and replay.

Per-broadcast engagement reporting (open rate, click rate, bounce rate, complaint rate) comes from tagging every send with a `broadcast_id` Configuration Set tag, then aggregating SNS or Firehose events filtered by that tag. The result is a Postmark-style or Resend-style activity view that you own end-to-end.

## Domain Verification, DKIM, and the Authentication Stack

Resend handles domain verification through a simple "add these DNS records" UI and signs all outbound mail with their managed DKIM keys. SES asks you to do the same work but exposes more knobs. In 2026 — Gmail, Yahoo, and Microsoft now treat unauthenticated mail from any sender above 5,000 messages per day as effectively undeliverable — getting authentication right before the first production send is non-negotiable.

**SPF.** Add `include:amazonses.com` to your sending domain's TXT record. If you keep Resend running during the cutover window, your record looks like `v=spf1 include:_spf.resend.com include:amazonses.com -all` until you fully cut over. Watch the 10-DNS-lookup limit; chained `include:` directives silently break SPF and are the most common authentication regression during a phased migration.

**DKIM.** Verify the domain in SES, enable Easy DKIM, and publish the three CNAME records SES generates. Easy DKIM rotates keys automatically. During parallel sending, Resend's DKIM selector and SES's three CNAMEs coexist without conflict — both signatures validate independently.

**DMARC.** Publish `_dmarc.yourdomain.com` with `v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com` from day one to start collecting aggregate reports. Move to `p=quarantine` after two to four weeks of clean reports, then to `p=reject` once every legitimate sender — transactional, marketing, calendar invites, vendor notifications — is aligned. Gmail and Yahoo's bulk-sender requirements made enforcement mandatory; `p=none` meets the minimum but only enforcement actually blocks spoofing.

**Custom MAIL FROM domain.** Set up a custom MAIL FROM subdomain (e.g., `mail.acme.com`) and publish the SES-provided MX and SPF records for it. This makes SPF alignment with your visible From address explicit, which removes a class of subtle DMARC failures that surface only after `p=reject` enforcement.

**BIMI.** Once enforcement is stable and you have a Verified Mark Certificate from Entrust or DigiCert, publish a BIMI record so your logo renders next to messages in Gmail, Apple Mail, and Yahoo. BIMI does not directly improve placement but the trust signal lifts open rates measurably for transactional and lifecycle email.

## Step-by-Step Migration Plan

A clean Resend-to-SES cutover for a single product domain takes one to three weeks of focused engineering work. The phases below assume you are migrating both transactional and marketing traffic; pure transactional cutover is the first three phases only.

**Phase 1 — Inventory and parallel infrastructure (Week 1)**

1. Audit every send site in the codebase. Group by send category — transactional (verification, password reset, MFA, receipts), product activity (notifications, mentions), marketing (broadcasts, newsletters).
2. Verify your sending domain in SES. Publish DKIM and SPF records. Leave Resend records in place.
3. Create one Configuration Set per send category. Wire each to an SNS topic (or directly to Kinesis Firehose, see below). Tag every Configuration Set with `category`.
4. Move out of the SES sandbox by submitting a production access request. Expect 24–48 hours.
5. Stand up the contact store (DynamoDB + Lambda) if you used Resend Audiences.

**Phase 2 — Send code refactor (Week 1–2)**

1. Wrap every send call site behind a `sendEmail()` helper that accepts a React Email component, recipient, subject, and category.
2. Add a feature flag (e.g., `EMAIL_PROVIDER=resend|ses|both`) so you can route by category. Start sending non-critical categories (internal alerts, low-value notifications) through SES first.
3. Render React Email components in application code. Verify the rendered HTML matches what Resend was producing. Pixel-perfect parity is the bar for transactional templates.
4. Implement the per-send checks: account-level suppression cache, per-category preference store, frequency cap, idempotency key.

**Phase 3 — Event pipeline (Week 2)**

1. Wire every Configuration Set to Kinesis Data Firehose, partition by `year/month/day/category` in S3.
2. Build the smallest viable replacement for the Resend dashboard: a Lambda or Node.js API that queries Athena for recipient timeline, per-category metrics, bounce and complaint detail.
3. Add CloudWatch alarms on bounce rate (>2%), complaint rate (>0.1%), and send-rate anomalies (10x deviation from 7-day rolling average).
4. Verify SNS subscription signature on every webhook handler. Resend used Svix; SES uses SNS message signing — different validation library, same security guarantee.

**Phase 4 — Marketing migration (Week 2–3, optional)**

1. Build the broadcast orchestration layer (EventBridge Scheduler + Step Functions Distributed Map).
2. Migrate Audience data from Resend export to DynamoDB.
3. Run a small broadcast (1,000 recipients) end-to-end to validate sending, tracking, and unsubscribe handling.
4. Scale up gradually over 7–10 days, monitoring per-domain placement.

**Phase 5 — Cutover and decommission (Week 3+)**

1. Flip the feature flag for transactional traffic. Keep Resend running for 24–48 hours as fast rollback.
2. Watch CloudWatch dashboards and Resend dashboards in parallel. Investigate any divergence immediately.
3. After 7 clean days, remove Resend SDK from dependencies, rotate the API key, and remove Resend's SPF include from DNS.

## Common Migration Challenges

**Attachment encoding.** Resend's SDK accepts attachments as Base64 strings or buffers; SES `SendEmail` does not support attachments at all. You must use `SendRawEmailCommand` with a MIME-encoded message body. Use `mimetext` or Nodemailer's compose function to assemble the raw message — do not roll your own MIME serialization.

**Webhook signature drift.** Resend signs webhooks with Svix. SES signs SNS messages with X.509 certificates served from `*.amazonaws.com`. The SDK helper `verifyMessageSignature` validates incoming SNS notifications; do not skip the verification step. Public SNS HTTP endpoints with weak validation are an active target for spoofed bounce-event attacks that poison your suppression list.

**Apple MPP open inflation.** Resend's dashboard reports raw open events. After migration to SES, your event pipeline shows opens too — but Apple Mail Privacy Protection prefetches every image in a recipient's inbox, inflating the open rate by 20–60% with non-human signal. Filter MPP opens (identifiable by the `User-Agent: Mail/MPP` and Apple-owned IP ranges) before feeding engagement data into segmentation logic.

**Suppression list import latency.** Resend exposes hard-bounced and complained addresses through their API but historical data is limited. Export everything you can on the day of cutover and bulk-load it into the SES account-level suppression list via `PutSuppressedDestination`. Anything not on the list will hard-bounce and rebuild reputation damage before SES auto-suppresses.

**Rate limits and concurrent send patterns.** Resend's send API tolerates bursts well. SES enforces a per-second send rate (`MaxSendRate`) tied to your account quota. Code that fired 1,000 sends in parallel against Resend will throttle on SES until the quota grows. Smooth bulk sends through SQS or `SendBulkEmail` rather than Promise.all over a contact list.

**Domain verification across regions.** Resend is region-agnostic from your perspective. SES verification is per-region — verifying `acme.com` in `us-east-1` does not verify it in `eu-west-1`. If you split sending across regions for latency or data residency, publish DKIM and DMARC records for each region separately.

## Deliverability Discipline After the Cutover

Resend operates the sending pool, the feedback loops, and the throttling for you. After migration, all three become your responsibility. Three operational habits separate teams that maintain Resend-grade inbox placement on SES from teams that watch their open rate decay over six months.

**Stream isolation.** Run separate Configuration Sets — and ideally separate dedicated IP pools — for transactional, product activity, and marketing traffic. A complaint spike on a marketing broadcast cannot reach password-reset deliverability if the IPs are isolated. Application code selects the correct Configuration Set per send category; tag every send with `category` for downstream filtering.

**Engagement-based send eligibility.** Maintain a `last_engaged_at` timestamp per recipient updated nightly from open and click events. Suppress recipients with no engagement in 90 days from marketing streams (180 days for transactional). Gmail and Microsoft weight recent positive engagement heavily — a smaller, hotter list lifts placement for the entire domain, typically from the mid-80s to the mid-90s within 30 days.

**Bot and prefetch filtering.** Apple MPP, Microsoft Defender link scanning, Gmail image proxies, and corporate security gateways all generate engagement events that have nothing to do with a human reading the message. Filter these before feeding signals into engagement-based suppression logic; without filtering, engagement-based suppression suppresses your real subscribers.

**Per-domain placement testing.** Send to a small panel of monitored seed inboxes (Gmail, Outlook, Yahoo, iCloud) on every major broadcast. Inbox vs. promotions vs. spam folder placement is your earliest warning system — much earlier than the bounce rate metric, which only spikes after reputation damage is already done.

## Production Event Pipeline: SES → Kinesis Firehose → S3 → Node.js API

Resend's hosted dashboard gives you a clean event view with searchable activity logs. After cutover you need to replicate that surface to keep observability parity. The architecture most production SES senders converge on is streaming the SES event firehose into S3 and exposing a thin query API.

```
SES Configuration Set
   ↓ (event destination)
Kinesis Data Firehose
   ↓ (60-second buffer or 5 MB)
S3 (Parquet, partitioned by year/month/day/category)
   ↓
Athena   ←  Node.js API   ←  Dashboard / suppression service / Slack alerts
```

Subscribe each Configuration Set to a Firehose delivery stream. Enable dynamic partitioning so events split by category, sending IP, or domain at write time. Lifecycle the bucket: Standard for 30 days (hot analytics window), Standard-IA at 30 days, Glacier Flexible Retrieval at 180 days. Raw events are the cheapest part of the stack and the most useful during deliverability investigations.

The Node.js API layer is small — a Fastify or Hono service, an Athena query helper, and four to six endpoints:

- `GET /messages?recipient=foo@bar.com` — Resend-style activity view
- `GET /broadcasts/:id/metrics` — open, click, bounce, complaint by broadcast
- `GET /deliverability?domain=gmail.com&days=7` — per-receiver placement signals
- `GET /bounces?subType=MailboxFull&days=1` — operational alerting feed
- `POST /webhooks/slack` — bounce/complaint fan-out
- `POST /webhooks/replay/:event_id` — re-process a single event for debugging

Filtering bot and prefetch traffic at this layer (or upstream of it) is what makes the engagement metrics actionable. Open and click events from corporate security scanners and image proxies should be tagged `automated` and excluded from engagement-based suppression decisions; otherwise, engagement-based suppression gradually suppresses real subscribers whose mail clients prefetch links. Tools that score SES events for bot vs. human activity in real time fit naturally between the Firehose stream and the API layer; teams that skip this filtering often discover the gap only after a quarter of degraded marketing placement.

## Related Comparisons

Explore other technical comparisons:

- [SendGrid to AWS SES](/compare/sendgrid-to-aws-ses/)
- [Mailgun to AWS SES](/compare/mailgun-to-aws-ses/)
- [Postmark to AWS SES](/compare/postmark-to-aws-ses/)
- [SparkPost to AWS SES](/compare/sparkpost-to-aws-ses/)

## Why Choose FactualMinds for Your Email Migration

FactualMinds is an **AWS Select Tier Consulting Partner** specializing in email infrastructure migration. We have executed SendGrid, Mailgun, Postmark, SparkPost, and Resend to AWS SES migrations and know exactly where teams get stuck.

- **Email migration experts** — we handle domain verification, DKIM, bounce architecture, IP warming
- **Assessment-first approach** — we map your current state before writing a line of infrastructure code
- **Zero-downtime cutover planning included** — no failed deliveries during migration
- **AWS Select Tier Partner** — [verified on AWS Partner Network](https://partners.amazonaws.com/partners/001aq000008su2EAAQ/Factual%20Minds)

---

## FAQ

### Why would I leave Resend for AWS SES?
The two common triggers are price and platform consolidation. Resend's Pro plan covers 50,000 emails per month at $20, then steps to roughly $90 for 100,000 marketing-eligible emails — meaningfully more than SES's flat $0.10 per thousand once you cross 100K. Beyond pricing, teams already running production workloads on AWS prefer to consolidate billing, IAM, and observability under one cloud. Resend is excellent for the first one to two years of a SaaS — fast to integrate, well-documented, no AWS account paperwork — but the AWS-native control surface (IAM, KMS, VPC endpoints, CloudWatch) becomes the deciding factor once your platform engineering team is in place.

### Does Resend run on AWS SES under the hood?
Resend has been transparent that AWS SES is one of the upstream providers in their multi-vendor sending stack. That is good news for migration: the underlying deliverability mechanics are similar, and IPs that send your Resend mail today and the SES IPs you will use after migration sit on similar reputation infrastructure. The catch is that the IPs and reputation that delivered your Resend traffic are not yours — they belong to the Resend pool. After cutover you start cold on either the SES shared pool (suitable for most senders) or your own dedicated IPs (which require warming). Plan IP-warming the same way you would migrating from any other provider.

### How do I migrate React Email templates from Resend to AWS SES?
React Email is provider-agnostic — it renders MJML or HTML from React components and has no Resend-specific dependencies. The migration is changing the SDK call. Replace `resend.emails.send({ react: <Email /> })` with the `@react-email/render` helper and `@aws-sdk/client-ses` `SendEmailCommand`. Render the React component to HTML and plain text in your application code, then pass both to SES. Templates, components, and previews all continue to work. The only Resend-specific thing to remove is `Resend` SDK imports; everything else — `Tailwind`, `Section`, `Container`, `Button` from `@react-email/components` — is portable.

### What is the SES equivalent of Resend Audiences and Broadcasts?
There is no native equivalent. Resend Audiences (contact lists with subscribe/unsubscribe handling) and Broadcasts (scheduled campaign sends with engagement reporting) are application-layer features built on top of the send API. To replicate them on SES you store contacts and preferences in DynamoDB, RDS, or Postgres, build a small admin surface for subscribe and unsubscribe state, and trigger sends from EventBridge Scheduler or Step Functions. For most teams this is a one-time engineering investment that pays back in a few months versus the per-message cost difference. Teams that primarily rely on Resend for marketing broadcasts and lack engineering bandwidth often stay on Resend for marketing while moving transactional traffic to SES.

### How does Resend pricing compare to AWS SES at scale?
Resend Free covers 3,000 emails per month and 100 per day — fine for prototypes. Pro at $20 per month covers 50,000 emails. Beyond that, Resend's marketing-tier pricing scales to roughly $90 for 100,000 broadcast emails and custom-priced beyond. SES is flat at $0.10 per 1,000 emails with no plan tier — $5 for 50K, $10 for 100K, $100 for 1M. At 50,000 emails per month the savings are modest ($15 per month). At 1,000,000 the gap is in the hundreds. The cost calculus is rarely the only factor at small volume; AWS-native integration, IP control, and event pipeline ownership matter more for teams sending past 200,000 messages per month.

---

*Source: https://www.factualminds.com/compare/resend-to-aws-ses/*
