SES bounce rate: thresholds and how to fix it
Your SES bounce rate is the share of your mail that Amazon SES couldn't deliver. Keep it under 2%: at 5% AWS puts your account under review, and at 10% it can pause your sending. Complaints work the same way at far lower numbers. This guide covers how the rate is calculated, what to do in the first hour after a review notice, the pipelines that keep it low, and what to tell AWS support.
Thresholds
The thresholds AWS enforces
AWS watches two reputation metrics, bounce rate and complaint rate, and each has three states. The numbers below come from the SES Developer Guide's reputation metrics page. You'll find other figures on the open web, including a lower complaint pause threshold in an older knowledge-base article; the Developer Guide is the one to plan against.
| Metric | Healthy | Under review | Sending pause |
|---|---|---|---|
| Bounce rate | Below 2% (AWS's recommendation) | 5% or more | 10% or more |
| Complaint rate | Below 0.1% | 0.1% or more | 0.5% or more |
How AWS calculates your SES bounce rate
Bounce rate = hard bounces ÷ attempted deliveries × 100 Only hard bounces count: permanent failures such as a mailbox that doesn't exist or a mistyped domain. Soft bounces, like a full mailbox, don't. Missing or broken authentication shows up here too, because a receiving server that rejects unauthenticated mail outright returns a permanent failure. Our Amazon SES deliverability guide covers SPF, DKIM and DMARC alignment.
Three things about how the number behaves catch teams out:
- It's measured over a representative volume, not a time window. AWS uses a volume of mail that represents your typical sending, and that volume differs from account to account. A low-volume sender who sends one bad batch can carry it in the rate for days. Waiting for tomorrow doesn't wash it out; clean sending does.
- Your account-level suppression list doesn't count against you. SES drops sends to addresses on your own suppression list before attempting delivery, so they stay out of the reputation metrics. That's the lever most of this guide pulls.
- The global suppression list does. SES also keeps a suppression list across all its customers, which you can't see or query. A send to an address on it comes back as a bounce, and it counts.
Where You Stand
First, get the real numbers
The console rounds what you need to see. Ask the API for your enforcement status, then pull the two reputation metrics from CloudWatch:
# HEALTHY, PROBATION (under review) or SHUTDOWN (paused)
aws sesv2 get-account --query 'EnforcementStatus'
# The reputation metrics AWS enforces against (as fractions: 0.05 = 5%)
aws cloudwatch get-metric-statistics \
--namespace AWS/SES --metric-name Reputation.BounceRate \
--start-time 2026-10-01T00:00:00Z --end-time 2026-10-05T00:00:00Z \
--period 3600 --statistics Maximum
aws cloudwatch get-metric-statistics \
--namespace AWS/SES --metric-name Reputation.ComplaintRate \
--start-time 2026-10-01T00:00:00Z --end-time 2026-10-05T00:00:00Z \
--period 3600 --statistics Maximum If you send different kinds of mail through separate configuration sets, turn on reputation metrics for each set and check them one by one. A single noisy product line can drag the whole account into review while everything else is clean.
First-Hour Triage
The first hour after a review notice
You've had the email from AWS, and production is either still sending or has just been paused. Do these four things, in this order, before you write anything to AWS support.
1. Stop sending to your riskiest segments
The first thing a reviewer looks for is whether you acted. Pause anything you suspect is the source (re-engagement sends, imported lists, anything older than a year) at the queue or job layer of your application, not in SES. You want outbound volume to drop measurably within the next half hour. A support reply that says "we are investigating" while the same traffic keeps flowing gets nowhere.
2. Turn on account-level suppression for bounces and complaints
Accounts created after November 25, 2019 have this on by default. If yours is older, or you're not sure, run it; it's safe to repeat. From then on, any address that hard-bounces or complains is suppressed automatically, and sends to it no longer count against your reputation.
aws sesv2 put-account-suppression-attributes \
--suppressed-reasons BOUNCE COMPLAINT
# Expect "SuppressedReasons": ["BOUNCE", "COMPLAINT"]
aws sesv2 get-account --query 'SuppressionAttributes' 3. Suppress everything you already know is bad
Your own records almost certainly hold past bounces and complaints:
logged notifications, support tickets, a previous provider's
suppression export. Load all of it into the SES suppression list,
including addresses that bounced months ago. If they're still on a
send list, they're future bounces. For a handful of addresses, call put-suppressed-destination in a loop; for a large list, upload newline-delimited JSON to S3 and run
a bulk import job.
# A few addresses
while read email; do
aws sesv2 put-suppressed-destination --email-address "$email" --reason BOUNCE
done < known-bounces.txt
# A large list: one JSON record per line in S3, then an import job
aws sesv2 create-import-job \
--import-destination 'SuppressionListDestination={SuppressionListImportAction=PUT}' \
--import-data-source 'S3Url=s3://your-bucket/bounces.json,DataFormat=JSON' 4. Audit the last 30 to 90 days of bounces
Aggregate your bounce and complaint events and look for two things.
First, concentration: if most bounces come from one configuration set, campaign or signup
source, that's your culprit. Second, bounce type. SES classifies bounces as Permanent, Transient or Undetermined. A spike in Permanent.General or
Permanent.NoEmail means invalid addresses;
Permanent.Suppressed means you're
hitting the global suppression list; Transient.MailboxFull is recoverable.
Why Rates Spike
Why bounce and complaint rates spike
Roughly in order of how often each turns out to be the root cause:
- Sending to an old list. Addresses decay every year as people change jobs and abandon mailboxes. A list that hasn't been mailed in years carries a large share of dead addresses, and re-engaging it is the most common reason SES accounts go into review.
- Importing a list from a previous provider. Suppression lists don't move with you. The addresses SendGrid, Mailgun or Postmark were quietly filtering start bouncing on SES unless you import them.
- No confirmation step. A typo in a signup form becomes a hard bounce on every email you ever send to it. Double opt-in, or at least address validation, stops it at the door.
- Slow or hidden unsubscribes. If leaving takes more than one click, people press "report spam" instead. That's a complaint, and complaints weigh far more than unsubscribes.
- Transactional and marketing mail on one identity. Marketing complaints then hurt the password resets. Separate them by configuration set at minimum, and by sending subdomain ideally.
- A new From address or domain at full volume. Mailbox providers treat a new sending identity with suspicion; ramp it up gradually.
- Spam traps. A once-real address recycled as a trap doesn't bounce. You see it only as an unexplained reputation drop or in DMARC aggregate reports.
Most accounts that land in review fit one of the first four.
Bounce Pipeline
Build a bounce-handling pipeline that holds up
Account-level suppression is the safety net. The pipeline is what stops bad addresses in your application, before the net has to catch them. Attach a configuration set with an event destination to every send, and buffer the events before you process them:
SES send (configuration set with an event destination)
→ SNS topic
→ SQS queue (buffering and retry)
→ Lambda (parse, suppress, record)
→ your application's suppression table Don't skip the queue. Without it, a Lambda failure during a bounce burst loses those events for good, at exactly the moment you need them. This is the payload you parse:
{
"notificationType": "Bounce",
"bounce": {
"bounceType": "Permanent",
"bounceSubType": "General",
"bouncedRecipients": [
{
"emailAddress": "recipient@example.com",
"action": "failed",
"status": "5.1.1",
"diagnosticCode": "smtp; 550 5.1.1 The email account that you tried to reach does not exist."
}
],
"timestamp": "2026-05-13T10:00:00.000Z",
"feedbackId": "01000189..."
},
"mail": { "messageId": "...", "source": "you@example.com" }
} | Bounce type | What to do |
|---|---|
| Permanent | Suppress immediately in your application, and add the address to the SES suppression list so future sends are dropped before they count. |
| Transient | Count them per address. Suppress after repeated failures; three in 14 days is a reasonable starting rule. |
| Undetermined | Record for visibility and review weekly; don't auto-suppress on the first one. |
Within Permanent, the subtype
tells you where to look. General and NoEmail are invalid addresses;
a stream of NoEmail means your signup
flow is letting them in. Suppressed is the global list. OnAccountSuppressionList means your own list caught it, and seeing those in volume means your application
isn't checking suppressions before it sends.
That check belongs in your application. By the time SES drops a message you've already rendered it and made the API call, and SES leaves many undeliverable addresses off its list altogether: see why in the SES suppression list is not enough. A minimal table, checked before every send:
CREATE TABLE email_suppressions (
email TEXT PRIMARY KEY,
reason TEXT NOT NULL, -- 'bounce' | 'complaint' | 'manual'
bounce_subtype TEXT,
suppressed_at TIMESTAMP NOT NULL DEFAULT NOW(),
source TEXT -- campaign or configuration set
); Complaints
Complaints: the same pipeline, more urgency
The complaint thresholds are fifty times lower than the bounce ones: 0.1% means one complaint per thousand messages. Route complaint events through the same pipeline, with three differences:
- Suppress on the first complaint. There's no transient complaint. Someone who pressed "spam" doesn't get a second email.
- Read the first hundred individually. Which campaign, which template, which segment? Complaint patterns show exactly which content crossed the line.
- Fix the unsubscribe flow. Most complaint spikes are people who couldn't find, or didn't trust, the way out.
Treat the reported complaint rate as a floor. Complaints reach SES only from mailbox providers that run feedback loops, and Gmail doesn't report individual complaints back to senders, so your real rate is higher than the number SES shows.
AWS Support
What to tell AWS support
If your account is under review or paused, the reply in the support case is what changes the outcome. "Please reinstate us, we'll be more careful" doesn't work. Specific, checkable evidence does. A reply that works has four parts:
Root cause
One sentence with a date, a number and the mistake: "On May 9 we imported 18,400 addresses from our previous provider without its suppression list."
What you changed
The suppression import (with its job ID), account-level suppression turned on, the bounce and complaint pipeline, new alarms, and the campaign you paused. Dates and IDs AWS can verify from its side.
Current state
Your bounce and complaint rates over the last day of sending, and how many messages that covers.
Prevention
A mechanism, not a promise: a code change, or a list-import checklist enforced by automation.
Accounts that reply this way are often reinstated quickly. Accounts that send a general apology rarely are.
Monitoring
Alert before AWS does
Don't set your alarms at 5% and 0.1%. Those are AWS's review thresholds, so an alarm there tells you you're already in trouble. Set them at roughly half, so you have time to act:
# Bounce rate warning at 3%
aws cloudwatch put-metric-alarm \
--alarm-name ses-bounce-rate-warning \
--namespace AWS/SES --metric-name Reputation.BounceRate \
--statistic Maximum --period 3600 --evaluation-periods 2 \
--threshold 0.03 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ses-alarms
# Complaint rate warning at 0.05%
aws cloudwatch put-metric-alarm \
--alarm-name ses-complaint-rate-warning \
--namespace AWS/SES --metric-name Reputation.ComplaintRate \
--statistic Maximum --period 3600 --evaluation-periods 2 \
--threshold 0.0005 --comparison-operator GreaterThanThreshold \
--treat-missing-data notBreaching \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ses-alarms Two one-hour evaluation periods smooth out a single bad batch while still catching a trend. If you use several configuration sets, add the same alarms per set, since a noisy product line usually shows up there before it moves the account number. For the rest of what's worth watching, from delivery delays to rendering failures, see our guide to Amazon SES monitoring. Reputation also decides how much of your quota you can safely use; see Amazon SES sending limits.
SendOps
Or let SendOps watch the rate for you
Everything above is buildable: a pipeline, a suppression table, alarm math. SendOps connects to your AWS account through a CloudFormation stack with a scoped IAM role and no stored keys, reads your SES events, and sends through the SES in your account:
Suppression handled
Hard bounces and complaints are suppressed as they arrive, and addresses that keep failing are caught even when SES's own list misses them.
Alerts before review
Deliverability alerts watch bounce and complaint rates against thresholds you set below AWS's, so your team hears first.
Consent at send time
Broadcasts and workflows check consent and suppression when each message sends, so a stale segment can't walk you into a review.
FAQ
SES bounce rate, answered
What is a good bounce rate for Amazon SES?
Below 2%. AWS reviews an account at a 5% bounce rate and can pause sending at 10%, but a healthy sender sits well under the review line so one bad batch doesn't push it over. For complaints, stay well below the 0.1% review threshold; many teams alert themselves at 0.05%.
At what bounce rate does AWS pause SES sending?
AWS places an account under review at a 5% bounce rate or a 0.1% complaint rate, and can pause sending at 10% bounces or 0.5% complaints. These are account-level reputation metrics. A pause stops sending for the whole account in that region until AWS reinstates it.
How does AWS calculate the SES bounce rate?
Hard bounces divided by attempted deliveries, measured over a representative volume of your recent sending rather than a fixed time window. Soft bounces don't count. Sends to addresses on your own account-level suppression list are dropped before delivery and don't count either, which is why enabling that list matters.
Do soft bounces count toward the SES bounce rate?
No. Only hard (permanent) bounces count: addresses that don't exist, domains that don't accept mail, rejections the receiving server marks as permanent. Soft bounces such as a full mailbox are transient, but an address that soft-bounces repeatedly should still be suppressed.
How long does a high SES bounce rate take to come down?
There is no fixed reset. Because AWS measures over a representative volume of your sending, a low-volume sender can carry one bad batch in its rate for days. The rate falls as clean mail replaces the bad sends, so stop sending to the source of the bounces and keep sending to engaged recipients.
How do I get my SES account out of review?
Fix the cause, then reply to the AWS support case with evidence: a specific root cause, what you changed (suppression imports, account-level suppression, a bounce pipeline, alarms), your current bounce and complaint rates, and a prevention step enforced by process or code. Vague promises to be more careful don't get accounts reinstated.
Get Started
Hear about a bounce spike before AWS does
Connect your SES account and SendOps tracks bounce and complaint rates, suppresses failing addresses, and alerts your team early. Free to start.