Email Bounce Analysis: How to Diagnose and Fix Bounce Problems
On this page
What Bounce Analysis Tells You
Every bounce message contains diagnostic information. Most senders glance at the bounce count and move on, but the bounce details reveal exactly what went wrong and what to do about it.
A systematic approach to bounce analysis answers:
- Which addresses should be permanently removed?
- Which bounces are temporary and will resolve themselves?
- Is there a pattern pointing to an infrastructure or reputation problem?
- Are specific domains, segments or sources causing disproportionate bounces?
Bounce Categories
Hard bounces
Hard bounces are permanent delivery failures. The address will never accept your email.
| Bounce code | Meaning | Action |
|---|---|---|
| 550 5.1.1 | User unknown / mailbox does not exist | Remove address immediately |
| 550 5.1.2 | Domain does not exist | Remove address immediately |
| 550 5.1.10 | Recipient not found | Remove address immediately |
| 551 5.1.6 | Address has moved, no forwarding | Remove address immediately |
| 550 5.7.1 | Delivery not authorised | Investigate; may be a block or a permissions issue |
Rule: Remove hard-bounced addresses after the first occurrence. Continuing to send to hard-bounced addresses damages your sender reputation.
Soft bounces
Soft bounces are temporary failures. The address exists but could not accept the message at that moment.
| Bounce code | Meaning | Action |
|---|---|---|
| 450 4.2.1 | Mailbox full | Retry in 24-48 hours; remove after 3 consecutive soft bounces |
| 451 4.7.1 | Greylisting | Retry in 15-30 minutes (most servers auto-retry) |
| 452 4.2.2 | Mailbox storage limit exceeded | Retry; remove if persists across 3 campaigns |
| 421 4.7.0 | Too many connections / rate limited | Slow down sending; this is a throttle, not a rejection |
| 450 4.1.1 | Recipient temporarily unavailable | Retry in a few hours |
Rule: Retry soft bounces up to 3 times across separate sends. If the same address soft bounces 3 times in a row, treat it as a hard bounce and remove it.
Block bounces
Block bounces occur when the receiving server actively refuses your email, usually because of reputation or content issues.
| Bounce code | Meaning | Action |
|---|---|---|
| 550 5.7.1 | Message rejected due to content or policy | Review email content; check if your domain or IP is blacklisted |
| 554 5.7.1 | Message refused; sending IP is on a blacklist | Check blacklists; request delisting |
| 550 5.7.25 | DMARC policy violation | Fix DMARC alignment |
| 550 5.7.26 | SPF authentication failed | Fix SPF record |
| 421 4.7.28 | Too many messages sent; rate limited by recipient server | Reduce sending volume to this domain |
Rule: Block bounces require you to fix the underlying problem (reputation, authentication, content) before retrying. Simply resending will make the problem worse.
Reading Bounce Messages
Anatomy of a bounce notification
A bounce notification (also called a non-delivery report or NDR) contains several useful fields:
From: Mail Delivery Subsystem <mailer-daemon@example.net>
To: sender@example.com
Subject: Delivery Status Notification (Failure)
The following message to <recipient@example.net> was
undeliverable.
Diagnostic-Code: smtp; 550 5.1.1 The email account that you
tried to reach does not exist. Please try double-checking
the recipient's email address for typos or unnecessary spaces.
Action: failed
Status: 5.1.1
Remote-MTA: dns; mx.example.net
| Field | What it tells you |
|---|---|
| Diagnostic-Code | The specific error message from the receiving server |
| Status | The SMTP enhanced status code (X.Y.Z format) |
| Remote-MTA | The receiving mail server that rejected the message |
| Action | Whether delivery failed, was delayed or was relayed |
SMTP status code structure
SMTP enhanced status codes follow the X.Y.Z format:
| First digit (X) | Meaning |
|---|---|
| 2 | Success |
| 4 | Temporary failure (soft bounce) |
| 5 | Permanent failure (hard bounce) |
| Second digit (Y) | Category |
|---|---|
| 0 | Other/undefined |
| 1 | Addressing (mailbox, domain) |
| 2 | Mailbox (full, disabled) |
| 3 | Mail system (server issues) |
| 4 | Network/routing |
| 5 | Mail delivery protocol |
| 6 | Content (message rejected) |
| 7 | Security/policy |
For example, 5.1.1 is a permanent (5) addressing (1) error: the mailbox does not exist.
Bounce Pattern Analysis
Looking at individual bounces is necessary but insufficient. Patterns across bounces reveal systemic issues.
By domain
| Pattern | What it means | Action |
|---|---|---|
| High bounce rate from one domain (e.g., 30%+ from company.com) | The company may have restructured, or your data for that company is stale | Re-verify all addresses at that domain |
| All addresses at a domain bounce with 550 | Domain may have shut down or changed email providers | Remove all addresses; verify domain is still active |
| Soft bounces from a major provider (Gmail, Outlook) | You may be rate-limited or flagged | Reduce volume to that provider; check reputation |
By list source
| Pattern | What it means | Action |
|---|---|---|
| One source produces 10%+ bounce rate | That source provides low-quality data | Stop using that source or add pre-send verification |
| Purchased list has high bounce rate | Purchased lists are frequently outdated | Verify all addresses before sending |
| Recently scraped list bounces heavily | The scraping may have captured false positives | Re-extract and verify; check extraction quality |
By time
| Pattern | What it means | Action |
|---|---|---|
| Bounce rate increases over time | List decay; addresses are going stale | Increase verification frequency |
| Sudden spike in bounces | Something changed: a domain migrated, a provider policy changed or your reputation dropped | Investigate the spike by domain and bounce code |
| Bounces clustered at send time | Rate limiting by the receiving server | Spread sends over a longer time window |
By bounce code
| Pattern | What it means | Action |
|---|---|---|
| Mostly 5.1.1 (user unknown) | Your list contains invalid addresses | Improve list acquisition and verification |
| Mostly 5.7.1 (policy rejection) | Your content or reputation is triggering blocks | Review content; check blacklists and authentication |
| Mostly 4.2.1 (mailbox full) | You are emailing abandoned or low-usage addresses | These addresses are likely disengaged; consider removing |
| Mostly 4.7.0 (rate limited) | You are sending too fast | Reduce sending speed and spread volume |
Building a Bounce Management Workflow
Step 1: Collect bounce data
Export bounce reports from your email sending platform. Most platforms provide bounce exports as CSV files containing:
- Bounced email address.
- Bounce code.
- Bounce message.
- Date and time.
- Campaign or send identifier.
Step 2: Categorise bounces
Sort each bounce into one of three categories:
def categorise_bounce(status_code):
"""Categorise a bounce by its status code."""
first_digit = status_code[0]
if first_digit == "5":
# Permanent failure
second_digit = status_code[2]
if second_digit == "1":
return "hard_bounce_address"
elif second_digit == "7":
return "block_bounce_policy"
else:
return "hard_bounce_other"
elif first_digit == "4":
return "soft_bounce"
else:
return "unknown"
Step 3: Apply rules
| Category | Rule |
|---|---|
| Hard bounce (address) | Remove immediately; add to suppression list |
| Hard bounce (other) | Investigate; may need manual review |
| Block bounce (policy) | Do not remove the address; fix the underlying issue first |
| Soft bounce (first occurrence) | Mark for retry |
| Soft bounce (second occurrence) | Mark for retry with extended delay |
| Soft bounce (third occurrence) | Remove; add to suppression list |
Step 4: Update your suppression list
Every address you remove due to bouncing should go onto a permanent suppression list. This prevents the address from being re-added if it appears in a future import:
def process_bounces(bounces, suppression_list, contact_list):
"""Process bounce records and update lists."""
for bounce in bounces:
category = categorise_bounce(bounce["status_code"])
if category.startswith("hard_bounce"):
suppression_list.add(bounce["email"])
contact_list.remove(bounce["email"])
elif category == "soft_bounce":
contact_list.increment_soft_bounce(bounce["email"])
if contact_list.soft_bounce_count(bounce["email"]) >= 3:
suppression_list.add(bounce["email"])
contact_list.remove(bounce["email"])
elif category == "block_bounce_policy":
contact_list.flag_for_review(bounce["email"])
Step 5: Analyse and report
After processing, generate a summary:
| Metric | Value |
|---|---|
| Total bounces this period | X |
| Hard bounces removed | X |
| Soft bounces (retry) | X |
| Soft bounces converted to hard (3rd occurrence) | X |
| Block bounces (needs investigation) | X |
| Bounce rate by domain (top 10) | Table |
| Bounce rate by source (top 5) | Table |
| Trend vs previous period | Up/down/stable |
Preventing Bounces
Prevention is more effective than treatment:
| Prevention method | When to apply |
|---|---|
| Verify before sending | Run all new addresses through an email verification service before the first send |
| Double opt-in | For inbound signups, require email confirmation |
| Regular list cleaning | Re-verify your list every 30-90 days |
| Monitor bounce rates per campaign | Catch problems early before they damage reputation |
| Track source quality | Measure bounce rate by acquisition source and stop using poor sources |
| Apply your suppression list to every import | Prevent previously bounced addresses from re-entering your list |
Pre-send verification with Email Extractor
When building prospect lists from files and documents, extract addresses with Email Extractor first to deduplicate, then run the extracted list through a verification service before sending. Extraction and deduplication are a necessary first step but do not replace verification. An address can be correctly formatted, present in a legitimate document and still bounce because the mailbox no longer exists.
Acceptable Bounce Rates
| List type | Acceptable bounce rate | Action threshold |
|---|---|---|
| Opt-in newsletter list | Below 0.5% | Investigate above 1% |
| Cold outreach (verified list) | Below 2% | Pause and re-verify above 3% |
| Cold outreach (unverified list) | Below 5% | Stop and verify entire list above 5% |
| Re-engagement campaign | Below 5% | Expected higher rate; monitor closely |
| Purchased list | Below 3% | Verify before use; stop if above 5% |
If your bounce rate exceeds the action threshold, stop sending until you identify and fix the cause. Continuing to send into high bounce rates accelerates reputation damage.