Guide
Soft Bounce vs Hard Bounce Explained
A soft bounce is a transient 4xx failure the sender retries; a hard bounce is a permanent 5xx failure you suppress. Learn the code ranges, retry timing, and how to classify bounces from the terminal.
Written by Nick Barraclough Product Manager
Reviewed by Qasim Muhammad
Command references used in this guide: nylas email search and nylas email read.
What is the difference between a soft bounce and a hard bounce?
A soft bounce is a temporary delivery failure signaled by a 4xx SMTP code: the address is valid, but the receiving server can't accept mail right now. A hard bounce is a permanent failure signaled by a 5xx code: the address is invalid or blocked, and no retry will ever succeed.
SMTP has signaled failure with three-digit reply codes since RFC 821 in 1982, and the first digit carries the verdict. Replies like 421 (service unavailable), 450 (mailbox busy), and 452 (storage exceeded) are soft. Replies like 550 (no such user) and 553 (bad mailbox name) are hard. The enhanced status codes from RFC 3463 mirror the split as 4.x.x versus 5.x.x.
| Signal | Soft bounce | Hard bounce |
|---|---|---|
| SMTP reply range | 421, 450, 451, 452 | 550, 551, 553, 554 |
| Enhanced code class | 4.x.x (transient) | 5.x.x (permanent) |
| Typical cause | Mailbox full, greylisting, server down | No such user, dead domain, policy block |
| Sender action | Wait; the queue retries automatically | Suppress the address immediately |
Why does a soft bounce retry but a hard bounce never does?
A soft bounce retries because a 4xx reply is a promise that the condition is temporary, so the sending mail server keeps the message in its queue and tries again on a backoff schedule. A hard bounce never retries because a 5xx reply declares the failure permanent, and the sender reports it at once.
RFC 5321 spells out the timing. Section 4.5.4.1 says “the retry interval SHOULD be at least 30 minutes” and “the give-up time generally needs to be at least 4-5 days.” Greylisting exploits this on purpose: the receiver issues a deliberate 450, betting that spamware won't retry while a real mail server will. Only when the give-up timer expires does a soft bounce produce a final failure notice in your inbox.
During the retry window the failure is invisible to you: the message sits in the sending server's queue, and most soft bounces resolve without leaving a trace in your inbox. A notification appears only when the server emits an interim delay notice or gives up. The pair of raw SMTP replies below shows each verdict as the sending server records it.
# Soft bounce: the server says try again later (greylisting)
450 4.2.0 <user@example.com>: Recipient address rejected: Greylisted
# Hard bounce: the server says never (Gmail's no-such-user reply)
550 5.1.1 The email account that you tried to reach does not exist.When should a bounced address go on a suppression list?
A suppression list is a do-not-send list of addresses that bounced. Add an address after a single hard bounce — one 5.1.1 proves the mailbox doesn't exist. Keep soft-bounced addresses active, but suppress any address that soft-bounces on 3 consecutive sends spread across at least 72 hours.
The asymmetry protects both sides. Retrying a hard-bounced address burns sender reputation, since mailbox providers read repeated 5xx attempts as list-hygiene neglect; Google's SMTP error reference for Workspace documents exactly which 550 replies mean the account is gone. Suppressing after one soft bounce throws away a real recipient whose mailbox was briefly full. Watch the 5.2.2 edge case: a permanently over-quota mailbox is graded as a hard bounce even though quota sounds temporary.
The nylas email search command exports bounce notifications as JSON, which makes suppression candidates a one-line jq filter. Pull the last 30 days of bounces, then scan dates and subjects for addresses that fail repeatedly. The --after flag scopes the window so old, already-handled bounces stay out.
# Collect the last 30 days of Gmail bounce notifications as JSON
nylas email search "*" --from "mailer-daemon@googlemail.com" --after 2026-05-10 --json --limit 100 > bounces.json
# List date + subject to spot addresses that bounce repeatedly
jq -r '.[] | "\(.date | split("T")[0]) \(.subject)"' bounces.jsonHow do I find soft and hard bounces from the terminal?
Bounce notifications land in your inbox as ordinary messages from a daemon address, so the nylas email search command finds every one with a sender filter. Gmail bounces arrive from mailer-daemon@googlemail.com; Microsoft 365 and Outlook.com deliver non-delivery reports (NDRs) from postmaster addresses.
Microsoft's NDR documentation for Exchange Online describes the report format, and its subject line starts with the word Undeliverable, which gives you a second filter when postmaster addresses vary. Pass "*" as the query to match any subject. The --limit flag raises the default of 20 results and auto-paginates past 200 emails, so one command sweeps a large mailbox.
Scope the search when a campaign just went out: the --after flag limits results to bounces received since the send date, and --in INBOX keeps archived or filtered daemon mail out of the count. Comparing bounce volume in the first 24 hours against the next 4 days separates instant hard bounces from soft bounces that exhausted their retries.
# Gmail: every bounce notification from the mail daemon
nylas email search "*" --from "mailer-daemon@googlemail.com" --limit 50
# Microsoft: NDRs from postmaster, filtered by the Undeliverable subject prefix
nylas email search "*" --from "postmaster@outlook.com" --subject "Undeliverable" --jsonHow do I tell which bounce type a message is?
Every bounce notification carries an RFC 3463 enhanced status code on a Status: line inside its delivery-status part, and the first digit classifies it: 4 means soft, 5 means hard. The nylas email read command with --mime shows the raw RFC822/MIME message, including the message/delivery-status part that carries that line.
Grab a message ID from the search above, read the bounce as MIME, and extract the code with grep. The numeric code has been stable across providers since RFC 3463 was published in 2003, unlike the human-readable bounce prose, so a shell test keyed on the class digit works for Gmail and Microsoft alike. This is the five-line classifier promised in the TL;DR.
The Action: field in the same delivery-status part adds a cross-check. RFC 3464 defines failed for final bounces and delayed for interim notices, so a Gmail “Delivery Status Notification (Delay)” with Action: delayed is a soft bounce still in the retry queue, not a final verdict. Don't suppress an address over a delay notice; wait for the failed report that follows if all retries within the 4–5 day window run out.
# Read the most recent bounce and classify it by its Status: code
ID=$(nylas email search "*" --from "mailer-daemon@googlemail.com" --json --limit 1 | jq -r '.[0].id')
STATUS=$(nylas email read "$ID" --mime | grep -im1 "^Status:" | grep -oE "[45]\.[0-9]+\.[0-9]+")
case $STATUS in
4.*) echo "soft bounce ($STATUS): transient, the sender is still retrying" ;;
5.*) echo "hard bounce ($STATUS): permanent, suppress this address" ;;
esacNext steps
- AI Agent Email Bounce Detection and Retry — Deterministic bounce rules for AI agents
- Email bounce codes reference — decode 5.1.1, 5.2.2, 4.2.2, and the other RFC 3463 codes
- Handle email bounces with the CLI — automate suppression on hard bounces
- Email deliverability from the terminal — SPF, DKIM, and DMARC checks that prevent policy bounces
- SMTP reply codes explained — the 3-digit codes behind every bounce
- EmailEngine vs Nylas — compare self-hosted and hosted approaches to mailbox automation
- Twilio (SendGrid) vs Nylas — how bulk-sending platforms handle bounce suppression differently
- Full command reference — every flag and subcommand documented
- Primary sources: RFC 5321 (SMTP retry timing), RFC 3463 (enhanced status codes), RFC 3464 (DSN Action field), Google Workspace SMTP error reference, and Microsoft Exchange Online NDR documentation