Skip to main content

SparkPost (Bird) to AWS SES Migration

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.

Ask AI: ChatGPT Claude Perplexity Gemini

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.

Pricing: Bird vs AWS SES

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

VolumeBird / SparkPostAWS SESMonthly 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 APIAWS SES EquivalentNotes
POST /api/v1/transmissionsSendEmail / SendBulkEmailCore send operation
recipients[] with addressDestination.ToAddresses[]Direct mapping
content.from, content.subjectSource, Message.SubjectSame fields
content.html, content.textMessage.Body.Html, Message.Body.TextDirect mapping
substitution_data (template vars)Application-rendered HTML or SES template variablesMove rendering to app layer
options.click_tracking, open_trackingConfiguration Set with tracking enabledSame capability, config-level
campaign_id, descriptionConfiguration Set tags, message tagsFor event filtering and CloudWatch
REST API key authIAM access key + secret or SES SMTP credentialsIAM preferred for AWS-native apps
SMTP (smtp.sparkpostmail.com, port 587)email-smtp.[region].amazonaws.com, port 587Drop-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:

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

Recommended architecture to replicate Signals:

Signals FeatureSES Equivalent ArchitectureBuild Effort
Aggregate engagement metrics (sends, opens, clicks)SNS events → Lambda → DynamoDB aggregates → CloudWatch custom metricsMedium
Per-campaign open/click ratesTag messages with ConfigurationSet tags → filter SNS events by tag → aggregate in DynamoDBMedium
Engagement-based suppressionLambda pre-send check: query DynamoDB for last open/click timestamp → skip if >90 daysMedium–High
Bounce rate by domainSNS Bounce events → Lambda → group by recipient domain in DynamoDB → CloudWatch metricMedium
Spam trap monitoringNo direct equivalent — use third-party inbox monitoring (250ok, GlockApps, or Validity)External tool
A/B testingApplication-layer split: send variant A to 50% of recipients, variant B to other 50%; tag with ConfigurationSet tagsApplication-layer
Unengaged recipient suppressionScheduled Lambda: query DynamoDB for recipients with no open/click in 90+ days → add to suppression listMedium–High
Deliverability dashboardQuickSight or Grafana dashboard over aggregated DynamoDB/S3 dataHigh (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 featureSES equivalent
Subaccount API keyPer-subaccount IAM role with ses:SendEmail scoped via condition keys on ses:FromAddress or tags
Per-subaccount sending quotaPer-Configuration-Set sending throttle implemented in application code; SES-level account quota enforced globally
Per-subaccount reportingPer-Configuration-Set tagging, separate event destinations, separate Athena views
Per-subaccount IP pool (Enterprise)Per-Configuration-Set dedicated IP pool
Subaccount-level suppressionApplication-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:

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:

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.

Explore other technical comparisons:

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.


Frequently Asked Questions

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.

Need Help Migrating to AWS SES?

FactualMinds is an AWS Select Tier Partner specializing in email infrastructure migration. We handle domain verification, Configuration Set architecture, bounce handling, IP warming, and cutover.