---
title: Migrating from SparkPost (Bird) to AWS SES
description: Migration guide from SparkPost (now Bird) to AWS SES. Email services, configuration, delivery reliability, and costs.
url: https://www.factualminds.com/compare/sparkpost-to-aws-ses/
publishDate: 2025-07-01
updateDate: 2026-06-16
---

# Migrating from SparkPost (Bird) to AWS SES

> SparkPost was acquired by MessageBird and rebranded as Bird. Many teams are using the disruption as an opportunity to re-evaluate their email infrastructure. This guide covers the migration to AWS SES — pricing, API mapping, analytics alternatives, and what you will need to build.

<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/)

SparkPost was acquired by MessageBird in 2021 and rebranded as Bird in 2023. For many teams, the acquisition is a forcing function: if you chose SparkPost for focused email infrastructure and now find yourself on a multichannel platform with a different pricing model and product direction, it is a reasonable time to re-evaluate. AWS SES offers significantly lower costs and native AWS integration for teams already in the AWS ecosystem.

## The Acquisition Context

SparkPost was built by the engineering team behind Message Systems (PowerMTA), which was the gold standard in high-volume email infrastructure. The technical foundation is strong. The organizational disruption is real. Bird (the combined entity) is building toward a unified multichannel messaging platform — email, SMS, WhatsApp, push notifications. For teams that chose SparkPost specifically for email deliverability and want an email-focused infrastructure provider, the fit has changed.

By 2026 the Bird platform has fully absorbed the SparkPost product line — the `sparkpost.com` domain redirects to Bird, the legacy Transmission API and webhooks still work, but new investment and account management sit on the multichannel platform. Pricing tiers are listed against Bird's catalog, not SparkPost's legacy plans, and the platform-fee structure tends to make per-email cost less predictable than it was on standalone SparkPost — particularly for teams who don't use the multichannel features. That trend is one of the reasons we still see SES migrations driven by this acquisition in the FactualMinds pipeline.

## Pricing: Bird vs AWS SES

Bird's pricing structure is more complex post-acquisition and includes platform fees that go beyond per-email costs.

| Volume           | Bird / SparkPost          | AWS SES       | Monthly Savings |
| ---------------- | ------------------------- | ------------- | --------------- |
| 10,000 emails    | ~$45/month (starter plan) | $1.00/month   | $44.00          |
| 50,000 emails    | ~$45/month (included)     | $5.00/month   | $40.00          |
| 100,000 emails   | ~$85/month                | $10.00/month  | $75.00          |
| 500,000 emails   | ~$250/month               | $50.00/month  | $200.00         |
| 1,000,000 emails | ~$450/month               | $100.00/month | $350.00         |

SES charges $0.10 per 1,000 emails. Bird charges vary based on plan tier and include platform fees for the multichannel product whether you use those features or not. At high volume, SES is consistently 4–9x cheaper.

## API Migration: SparkPost Transmission API → SES

SparkPost's Transmission API and SES SendEmail API are structurally different but functionally equivalent.

| SparkPost Transmission API                | AWS SES Equivalent                                  | Notes                              |
| ----------------------------------------- | --------------------------------------------------- | ---------------------------------- |
| `POST /api/v1/transmissions`              | `SendEmail` / `SendBulkEmail`                       | Core send operation                |
| `recipients[]` with `address`             | `Destination.ToAddresses[]`                         | Direct mapping                     |
| `content.from`, `content.subject`         | `Source`, `Message.Subject`                         | Same fields                        |
| `content.html`, `content.text`            | `Message.Body.Html`, `Message.Body.Text`            | Direct mapping                     |
| `substitution_data` (template vars)       | Application-rendered HTML or SES template variables | Move rendering to app layer        |
| `options.click_tracking`, `open_tracking` | Configuration Set with tracking enabled             | Same capability, config-level      |
| `campaign_id`, `description`              | Configuration Set tags, message tags                | For event filtering and CloudWatch |
| REST API key auth                         | IAM access key + secret or SES SMTP credentials     | IAM preferred for AWS-native apps  |
| SMTP (smtp.sparkpostmail.com, port 587)   | `email-smtp.[region].amazonaws.com`, port 587       | Drop-in SMTP credential swap       |

For bulk sends to large recipient lists, map SparkPost's `recipients[]` array to SES `SendBulkEmail` with a `Destinations[]` array. The SES bulk send API supports up to 50 destinations per call; loop and batch for larger lists.

## SparkPost Signals → SES + Custom Analytics

SparkPost Signals is one of the platform's strongest differentiators. It provides:

- Aggregate engagement metrics (open rate, click rate, bounce rate by campaign, domain, sending IP)
- Engagement-based suppression (automatic suppression of chronically unengaged recipients)
- Spam trap monitoring
- A/B testing for subject lines and content

SES does not have an equivalent analytics product. You build it from the event stream.

**Recommended architecture to replicate Signals:**

| Signals Feature                                     | SES Equivalent Architecture                                                                                          | Build Effort      |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------- |
| Aggregate engagement metrics (sends, opens, clicks) | SNS events → Lambda → DynamoDB aggregates → CloudWatch custom metrics                                                | Medium            |
| Per-campaign open/click rates                       | Tag messages with ConfigurationSet tags → filter SNS events by tag → aggregate in DynamoDB                           | Medium            |
| Engagement-based suppression                        | Lambda pre-send check: query DynamoDB for last open/click timestamp → skip if >90 days                               | Medium–High       |
| Bounce rate by domain                               | SNS Bounce events → Lambda → group by recipient domain in DynamoDB → CloudWatch metric                               | Medium            |
| Spam trap monitoring                                | No direct equivalent — use third-party inbox monitoring (250ok, GlockApps, or Validity)                              | External tool     |
| A/B testing                                         | Application-layer split: send variant A to 50% of recipients, variant B to other 50%; tag with ConfigurationSet tags | Application-layer |
| Unengaged recipient suppression                     | Scheduled Lambda: query DynamoDB for recipients with no open/click in 90+ days → add to suppression list             | Medium–High       |
| Deliverability dashboard                            | QuickSight or Grafana dashboard over aggregated DynamoDB/S3 data                                                     | High (one-time)   |

## Subaccounts and Sending Streams: SparkPost → SES Configuration Sets and IAM

SparkPost subaccounts are one of the platform's strongest enterprise features — separate API keys, isolated quotas, isolated reporting, and (on Enterprise) isolated IP pools. SES does not have a "subaccount" concept, but you can replicate the model by combining Configuration Sets, IAM, and dedicated IP pools.

| SparkPost subaccount feature        | SES equivalent                                                                                                    |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Subaccount API key                  | Per-subaccount IAM role with `ses:SendEmail` scoped via condition keys on `ses:FromAddress` or tags               |
| Per-subaccount sending quota        | Per-Configuration-Set sending throttle implemented in application code; SES-level account quota enforced globally |
| Per-subaccount reporting            | Per-Configuration-Set tagging, separate event destinations, separate Athena views                                 |
| Per-subaccount IP pool (Enterprise) | Per-Configuration-Set dedicated IP pool                                                                           |
| Subaccount-level suppression        | Application-layer suppression keyed on `(subaccount_id, recipient)` — SES account suppression is global           |

The most important consequence: SES account-level suppression is shared across all your sending streams. If subaccount A's marketing complaint causes a hard suppression, subaccount B's transactional sends to the same address are also suppressed. If the subaccount model in SparkPost was specifically about per-tenant suppression isolation (e.g., a multi-tenant SaaS where one customer's bad list cannot block another customer's sends), application-layer suppression keyed on tenant is the only way to preserve that semantic on SES.

## Templates: SparkPost Substitution Data → Application-Layer Rendering

SparkPost templates support Mustache-style substitution (`{{variable}}`) plus conditional blocks (`{{#if condition}} ... {{/if}}`), looping (`{{#each items}}`), and stored snippets. SES templates support only basic variable substitution with no conditionals or loops, and most teams skip the SES template feature entirely after migration.

The recommended pattern is to move template rendering to application code:

- **React Email** for JavaScript and TypeScript projects
- **MJML + Handlebars** for framework-agnostic templating
- **Liquid** for Ruby-shop teams that already use it elsewhere

Migration steps:

1. Export each SparkPost stored template via `GET /api/v1/templates/{id}` — capture both the `html` and `text` parts.
2. Translate the SparkPost-specific syntax to your chosen template engine. Most syntactic constructs map cleanly; the pieces that need attention are:
   - `{{render_dynamic_content(...)}}` — replace with conditional logic in code
   - `{{snippet(...)}}` — replace with shared component imports
   - Substitution data passed via `substitution_data` becomes function arguments or React props
3. Re-test rendered output across major email clients. Outlook on Windows is the client most likely to break a SparkPost-exported template because of its idiosyncratic CSS rendering.
4. Add the template to version control next to the code that triggers it.

Two engineering wins from moving template rendering to application code: types catch missing variables at build time, and templates can be previewed locally without sending real mail.

## Step-by-Step SparkPost → SES Migration

A clean SparkPost-to-SES cutover for a transactional product domain takes one to three weeks. A full migration including Signals analytics replication, subaccount isolation, and stored-template porting takes four to eight weeks.

**Phase 1 — Inventory and SES setup (Week 1)**

1. Catalog every SparkPost feature in active use — Transmission API, SMTP, stored templates, subaccounts, webhooks, Signals, A/B testing, suppressions.
2. Verify the sending domain in SES. Publish DKIM, SPF, DMARC, and a custom MAIL FROM subdomain. Leave Bird/SparkPost records in place for the parallel-send window.
3. Move out of the SES sandbox by submitting a production access request. Expect 24–48 hours.
4. Create one Configuration Set per send category — `transactional`, `notifications`, `marketing` — and one per multi-tenant subaccount if applicable. Wire each to a Kinesis Firehose event destination writing to S3.

**Phase 2 — Template and code refactor (Week 1–2)**

1. Export every stored template from SparkPost. Translate to React Email, MJML, or your chosen engine.
2. Wrap every send call site behind a `sendEmail()` helper that accepts a template, recipient, subject, category. Add a feature flag to route by category for phased rollout.
3. Replace SparkPost SDK calls with AWS SDK SES calls. Map `recipients[]` and `substitution_data` to `Destination.ToAddresses[]` and template props.
4. Implement per-send checks: account-level suppression cache, per-tenant suppression for multi-tenant senders, frequency cap, idempotency key.

**Phase 3 — Suppression import (Week 2)**

1. Export hard bounces and complaints from SparkPost via `GET /api/v1/suppression-list` (paginate; lists with millions of entries take time to export).
2. Bulk-load to the SES account-level suppression list via `PutSuppressedDestination`. This is the most-overlooked step in SparkPost migrations and the cause of most early reputation regressions.
3. For multi-tenant senders, mirror the suppression to the application-layer per-tenant store keyed on `(tenant_id, email)`.

**Phase 4 — Signals replacement and event pipeline (Week 2–3)**

1. Subscribe each Configuration Set to Kinesis Firehose with dynamic partitioning by `category/year/month/day/hour`.
2. Build the Lambda + DynamoDB aggregation pipeline described in the Signals architecture table above for engagement metrics, per-domain bounce rates, and engagement-based suppression.
3. Stand up the smallest viable replacement for the Signals dashboard: a Lambda or Node.js API that queries Athena over Firehose-written S3 data.
4. Migrate Signals A/B test logic to application-layer split sending tagged with Configuration Set tags.

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

1. Flip the feature flag for transactional traffic. Keep SparkPost running for 24–48 hours as fast rollback.
2. Watch CloudWatch dashboards and SparkPost reports in parallel. Investigate any divergence immediately.
3. After 7 clean days, scale down SparkPost plan, rotate API keys, remove SparkPost's SPF include from DNS.
4. After 30 clean days, close the SparkPost account.

## Common Migration Challenges

**Webhook batching mismatch.** SparkPost webhooks deliver batches of events in a single POST — up to 1MB or 1,000 events per delivery. SES via SNS delivers one event per notification. Code that processed SparkPost webhook batches by iterating an `events[]` array needs refactoring to handle one-event-per-invocation Lambdas. The throughput difference is rarely an issue (Lambda concurrency handles it), but the parsing code is unrecognizable across the two patterns.

**Substitution data with engagement tracking.** SparkPost can rewrite links in a template with per-recipient tracking parameters derived from `substitution_data`. SES tracking redirects work the same way at the Configuration Set level but do not interpolate per-recipient template variables into the redirect URL. If your template uses `<a href="{{cta_url}}?email={{address}}">`, the personalization happens at the application render layer, not at SES tracking time.

**A/B testing logic.** SparkPost's A/B testing is a platform feature that splits sends across variants and reports on engagement per variant. SES has no equivalent. Move A/B logic to application code: split the recipient list at send time (50/50, 25/75, etc.), tag each send's Configuration Set with the variant name, and aggregate engagement metrics by tag from the event pipeline. The reporting is more work than SparkPost's built-in but is more flexible — you can multivariate-test arbitrary template, send-time, or sender-name variants.

**Subaccount-scoped credentials.** SparkPost subaccount API keys are scoped to a single subaccount. SES uses IAM, which can be scoped per-application but not per-tenant out of the box. For multi-tenant senders, the cleanest pattern is one IAM role per tenant tier with `ses:SendEmail` allowed only for `Tags/tenant_id` matching the role's tenant scope, plus application-layer enforcement before the SES call. This is more setup than SparkPost subaccounts but produces equivalent isolation.

**Signals engagement-based suppression timing.** SparkPost suppresses unengaged recipients automatically once Signals decides they are dead weight. The first 60 days after migration, your SES sending list still contains every address Signals would have suppressed because the data lives in SparkPost, not in your application. Export Signals engagement history before cutover and use it to prepopulate the engagement-based suppression layer in the new contact store.

**Apple MPP open inflation.** SparkPost reports already filter some MPP traffic in Signals. After migration to SES with raw event capture, you see every prefetch event individually. Filter MPP opens (identifiable by the `User-Agent: Mail/MPP` and Apple-owned IP ranges) before feeding engagement data into the engagement-based suppression layer.

**SMTP credential format.** SparkPost SMTP uses the literal string `SMTP_Injection` as the username. SES SMTP uses a generated SMTP user (looks like `AKIA...`) and password derived from an IAM user via the SES console or the `convert_iam_to_smtp_password` algorithm. The SDK and CLI handle this; if you have legacy infrastructure using the literal `SMTP_Injection`, plan for a hard credential change.

## Deliverability Discipline After the Cutover

SparkPost's underlying infrastructure is strong — built by the team behind Message Systems / PowerMTA — and inherits good defaults. SES exposes the same control surface but does not opinionate. Three operational habits separate teams that maintain SparkPost-grade placement on SES from teams that watch placement decay over six months.

**Stream isolation through Configuration Sets and IP pools.** Run separate Configuration Sets — and ideally separate dedicated IP pools — for transactional, product activity, and marketing traffic. SparkPost gave you this through subaccounts and binding groups; SES gives you the same isolation through Configuration Sets pointing to dedicated IP pools. A complaint spike on a marketing broadcast cannot reach password-reset deliverability if the IPs are isolated.

**Engagement-based send eligibility.** SparkPost Signals did this for you automatically. On SES you build it: maintain a `last_engaged_at` timestamp per recipient updated nightly from open and click events, and 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.

**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. SparkPost's enterprise tier had seed-list testing built in; on SES it is a small tool you stand up yourself or buy from a third party (250ok, GlockApps, Validity).

**Authentication discipline.** Gmail and Yahoo's 2024 sender requirements treat unauthenticated mail above 5,000 messages per day as effectively undeliverable in 2026. Publish SPF (`include:amazonses.com`), enable Easy DKIM, advance DMARC from `p=none` to `p=quarantine` to `p=reject` over four to six weeks of clean aggregate reports, set a custom MAIL FROM subdomain for explicit SPF alignment, and add BIMI once enforcement is stable and you have a Verified Mark Certificate.

## Production Event Pipeline: Replicating Signals at Scale

The Signals replacement table earlier in this guide covers the per-feature mapping. The full production pipeline that delivers Signals-grade analytics on SES is:

```
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   ←  Signals-style dashboard / suppression service / alerts
```

Subscribe each Configuration Set to a Firehose delivery stream with dynamic partitioning. 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 exposes the endpoints SparkPost users miss most:

- `GET /messages?recipient=foo@bar.com` — Signals-style activity log
- `GET /campaigns/:id/metrics` — open, click, bounce, complaint by `campaign_id` Configuration Set tag
- `GET /deliverability?domain=gmail.com&days=7` — per-receiver placement signals (Signals' engagement health by domain)
- `GET /bounces?subType=Suppressed&days=1` — operational alerting feed
- `GET /engagement/:recipient` — per-recipient engagement history for engagement-based suppression decisions
- `POST /webhooks/slack` — bounce/complaint fan-out

**Filter automated traffic before the data hits engagement logic.** 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. Without filtering, your "open rate" double-counts machine activity and engagement-based suppression — the feature SparkPost users miss most after migration — gradually suppresses real subscribers. Tools that score SES events for human vs. automated activity sit naturally between the Firehose stream and the API layer; the bot vs. human signal is what makes the engagement-based suppression layer actually accurate.

## 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/)
- [Resend to AWS SES](/compare/resend-to-aws-ses/)
- [Elastic Email to AWS SES](/compare/elastic-email-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, and SparkPost 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

### What happened to SparkPost?
SparkPost was acquired by MessageBird in 2021 and rebranded as Bird (formerly MessageBird) in 2023. The combined platform covers email, SMS, WhatsApp, and other messaging channels under a unified brand. Existing SparkPost customers have been migrated to the Bird platform, which retains the core email infrastructure but operates under new pricing, branding, and product direction. Many SparkPost customers — particularly those who chose SparkPost specifically for email and do not need multichannel messaging — have used the acquisition and rebranding as a trigger to evaluate alternatives, including AWS SES.

### Is Bird (SparkPost) good for transactional email?
Bird retains SparkPost's underlying email infrastructure, which was built by former Message Systems engineers and is technically strong. Deliverability is good, the Transmission API is well-documented, and SparkPost Signals (now part of Bird Analytics) provides sophisticated engagement analytics. The concerns are organizational rather than technical: pricing complexity has increased post-acquisition, the product roadmap now prioritizes multichannel features over email-only capabilities, and some customers report support quality changes. For teams that need email only and are already on AWS, the acquisition is a reasonable trigger to migrate to SES.

### How do I migrate from SparkPost to AWS SES?
The migration involves four main tracks: (1) Domain and IP setup — verify your sending domain in SES, warm dedicated IPs if needed; (2) API migration — map SparkPost Transmission API calls to SES SendEmail/SendBulkEmail; (3) Event pipeline — replace SparkPost webhooks with SES Configuration Set event publishing to SNS, then subscribe Lambda or an HTTP endpoint; (4) Analytics — replace SparkPost Signals with a custom event aggregation pipeline using SNS, Lambda, and a data store of your choice (DynamoDB, RDS, or S3 + Athena). The API migration is the fastest part; building the analytics replacement takes the most time if you relied heavily on Signals engagement data.

### Does AWS SES have analytics like SparkPost?
Not out of the box. SparkPost Signals provides engagement analytics, engagement-based suppression (automatically suppressing unengaged recipients), and A/B testing. SES provides per-event delivery notifications via SNS (delivery, bounce, complaint, open, click) but no aggregated analytics UI. To replicate Signals functionality: route SNS events to Lambda → store in DynamoDB or S3 → visualize in QuickSight or Grafana. Engagement-based suppression requires a custom Lambda that checks open/click history before each send and skips unengaged addresses. This is buildable but requires engineering investment. If engagement analytics are a core requirement and you lack the bandwidth to build the pipeline, evaluate whether Bird or another full-featured ESP better fits your current needs.

### Is SparkPost being discontinued?
No. SparkPost's email infrastructure continues to operate under the Bird brand. The SparkPost.com domain now redirects to Bird, and existing accounts and APIs continue to function. What changed is the brand, the pricing model, and the product direction — Bird is positioning as a multichannel customer engagement platform, not a focused email infrastructure provider. The API endpoints, SMTP relay addresses, and webhook formats from SparkPost continue to work, but new features are built around the Bird platform's multichannel vision rather than email-only improvements.

---

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