Skip to main content

Amazon SES Queued Sending

Amazon SES Queued Sending: Rate-Limited, Multi-Tenant Email on SQS and Lambda

Your application produces email in bursts, and SES accepts it at a fixed rate per second. We build the queue, the rate-limited consumer and the per-tenant isolation that sit between them, so spikes turn into a short backlog instead of throttling errors and lost messages.

  1. A send event arrives from the app or the campaign.
  2. Amazon SES is the send path.
  3. Authentication is SPF, DKIM, and DMARC on the sending domain.
  4. Delivery is an IP and a reputation you can see.
  5. Bounces and complaints return on the feedback loop.
  6. The mail that should reach an inbox does.

Last updated:

Built forAWS Solutions for DevOps & Platform EngineersFor CTOs
Industries servedRetail & eCommerceSaaS
Ask AI:ChatGPTClaudePerplexityGemini

The problem this solves

Amazon SES enforces a maximum send rate per second and a 24-hour quota per account in each Region. Applications rarely send at a steady rate. A campaign launch, a nightly digest or a burst of sign-ups calls SES faster than the quota allows, and SES rejects the excess with a ThrottlingException.

Teams usually notice this in one of four ways:

  • Lost or late email. In-process retries make the burst worse, and messages disappear when a container restarts mid-retry.
  • No backlog visibility. Nobody can answer “how many emails are waiting, and how old is the oldest one?”
  • One tenant hurts everyone. In a SaaS or agency setup, one customer’s bulk send uses up the shared rate, and one customer’s bad list pushes the account bounce rate toward the 5% level at which AWS places the account under review.
  • Quota increases go unused. AWS raises your limit and nothing in the code reads the new value.

Who this is for

A good fit if you:

  • Send in bursts that approach or exceed your account MaxSendRate
  • Run a multi-tenant product (SaaS, marketing platform, agency) on one SES account
  • Have mail that must not be lost, such as order confirmations, invoices or alerts
  • Are migrating to SES and want to stop calling the email API inline as part of the move

Probably not a fit if you:

  • Send a few thousand emails a day at a steady rate. Direct SendEmail calls with sensible retries are enough.
  • Need marketing campaign tooling (segmentation, a visual editor). That is an email marketing product, not a sending architecture.

How we build it

The architecture follows our published SES queued sending reference pattern. A typical engagement runs in four steps.

1. Discovery. We read your current send path, SES account quotas (GetAccount), configuration sets, event destinations and CloudWatch history. We map every producer: application handlers, batch jobs, third-party integrations. Then we agree on the mail streams that need separate treatment, usually transactional, time-critical and marketing.

2. Design. We choose standard or FIFO queues per stream. Standard is the default; FIFO is used when per-tenant ordering or fairness matters, with MessageGroupId set to the tenant. We size consumer concurrency and batch size from your measured per-call latency so the aggregate rate stays at roughly 70–80% of MaxSendRate. We also define the retry rules: which SES errors are retriable and which are permanent.

3. Build. Queues, DLQs, Lambda consumers, configuration sets, event destinations and alarms are delivered as infrastructure as code in your repository. Producers change in one place: they enqueue instead of sending.

4. Prove it. We load-test the burst you expect, check that throttling is absorbed and that nothing lands in the DLQ without a reason, and run the old and new paths side by side for one mail stream before cutting over the rest.

Tenant isolation and fair scheduling

Isolation is where most home-grown queues fall short. We separate tenants at three levels:

LevelMechanismWhat it protects
QueueFIFO MessageGroupId per tenant, or a queue per high-volume tenantOne tenant’s backlog cannot block another’s mail
SendingConfiguration set or SES tenant per customer or mail streamEvents, reputation metrics and dedicated IP pools stay separate
SuppressionTenant-scoped suppression where SES tenant management is usedA complaint storm against one tenant does not suppress sends for the rest

When one tenant’s bounce or complaint rate spikes, an alarm pauses sending for that configuration set only. Everyone else keeps sending.

Monitoring and operations

We add the alarms that answer the questions operations teams actually ask:

  • Is the backlog growing? SQS ApproximateAgeOfOldestMessage per queue
  • Are messages failing? DLQ depth and Lambda error counts
  • Are we hitting the quota? Throttling counts from the consumer
  • Is reputation at risk? SES Reputation.BounceRate and Reputation.ComplaintRate, alarmed below the levels at which AWS reviews the account

Each alarm comes with a runbook entry: what it means, what to check, and how to redrive or pause.

Deliverables

  • Architecture decision record covering queue type, sizing math, retry rules and isolation model
  • Infrastructure as code for queues, DLQs, consumers, configuration sets, event destinations and alarms
  • Producer changes, or a small client library your team calls instead of SES directly
  • Load-test results for your expected peak
  • Operations runbook and a handover session with your engineers

Outside the scope: email content, list acquisition and campaign strategy. Deliverability work such as authentication, IP warm-up and reputation recovery is covered by our Amazon SES deliverability service. Many clients combine the two.

Proof

We built the SES infrastructure behind TargetBay’s 200M+ emails a month. That work includes an SNS/SQS/Lambda event pipeline for bounces and complaints, dedicated IP warm-up and a migration from SendGrid. The TargetBay SES case study has the details.

Key Features

Burst absorption with SQS

Producers enqueue a small send request and return immediately. Campaign launches, batch jobs and sign-up spikes drain at a controlled rate, and nothing is lost when a worker restarts.

Consumer sized to your quota

Lambda event source mapping MaximumConcurrency and batch size are set from the MaxSendRate that the SESv2 GetAccount API reports, with headroom, and re-read on a schedule so quota increases get used.

Throttling and retry handling

ThrottlingException is treated as retriable through partial batch responses. Permanent failures such as suppressed addresses are logged and dropped, never retried in a loop.

Dead-letter queue and redrive

Messages that keep failing land in a DLQ with enough context to fix the cause, then go back to the source queue with SQS redrive.

Tenant isolation and fair scheduling

Per-tenant configuration sets or SES tenants keep events, reputation metrics and suppression scope separate. FIFO queues with MessageGroupId per tenant stop one large send from starving everyone else.

Backlog and reputation monitoring

CloudWatch alarms on queue age, DLQ depth, throttling counts, and SES bounce and complaint rates, with a runbook that pauses only the offending configuration set.

Why Choose FactualMinds?

We have run SES at high volume

We built the SES infrastructure TargetBay uses to send 200M+ emails a month, including the SNS/SQS/Lambda event pipeline for bounces and complaints. See the published case study.

Infrastructure as code, handed over

Queues, consumers, alarms and configuration sets ship as IaC in your repository, with a runbook your team owns after we leave.

Measured, not assumed

We size concurrency from your own per-call latency and quota, then load-test the burst you expect before production traffic depends on it.

Email specialists, not generalists

Queued sending is one part of our SES work, alongside deliverability and provider migration. Your queue design and your sender reputation get reviewed together.

Step-by-Step Guides

Implementation guides for this service from our team of AWS experts.

From Our Blog

In-depth guides and best practices from our certified AWS architects.

Frequently Asked Questions

Do we need queued sending if our volume is low?
Usually not. If your peak send rate stays well under your account MaxSendRate and losing an email on a process restart is acceptable, direct SendEmail calls are fine. Queued sending pays off when you have bursts (campaigns, digests, batch notifications), several tenants sharing one SES account, or mail that must not be lost.
Will a queue delay password resets and one-time passcodes?
Only if they share a queue with bulk mail. We give time-critical mail its own queue and a reserved share of the send rate, so a marketing backlog cannot delay a login code.
Can you add queueing to our existing SES setup without a rewrite?
In most cases, yes. The change is at the point where your application calls SES: it writes a message to SQS instead, and a new consumer does the sending. Templates, identities and configuration sets stay as they are. We usually run old and new paths side by side for one tenant or mail stream before switching the rest.
How is this different from your SES migration service?
Migration moves you from another provider (SendGrid, Mailgun, Postmark) to SES. Queued sending is the sending architecture on SES itself. Many migrations include it, because the move is a good time to stop calling the email API inline.
What does the queue-and-consumer layer cost?
Little next to SES. Our reference architecture works through a 10 million emails per month example: about $11 of SQS and Lambda against about $1,000 of SES sending at a-la-carte rates. The pattern page shows the assumptions behind that estimate.
How do you stop "Maximum sending rate exceeded" errors?
By keeping the aggregate call rate below MaxSendRate on purpose instead of hoping for it. The consumer concurrency and batch size are derived from the quota, throttled messages go back to the queue after the visibility timeout, and an alarm on throttling counts tells you when latency changes have pushed the rate up.

Stop losing email to throttling and bursts

Tell us your peak send rate, your SES quota and how many tenants share the account. We will tell you whether you need a queue, and what it should look like.