SMTP bounce codes explained, from 550 5.1.1 to 421 4.7.0

published · September 3, 2026

What each SMTP reply and enhanced status code means, which are hard bounces, and how a verification handshake maps them to smtp_reason values.

An SMTP bounce code is the three-digit reply a receiving mail server gives when it refuses a message, usually followed by a dotted enhanced status code that says why. 550 5.1.1 means the mailbox does not exist and is a hard bounce; 452 4.2.2 means the mailbox is full and is a soft bounce; 421 4.7.0 means the server is throttling you and says nothing about the mailbox at all. Reading the second number is what turns a bounce log into a decision.

How are SMTP reply codes structured?

RFC 5321 defines the three-digit reply. The first digit is the verdict, the second the category, the third a detail.

First digit Meaning What a sender does
2xx Success Continue
3xx More input needed (only 354 after DATA) Send the content
4xx Transient failure Queue and retry later
5xx Permanent failure Stop and bounce

The second digit narrows it: x0x syntax, x1x informational, x2x connection, x5x mail system. In practice the second digit of the plain code is coarse, which is why almost every modern server appends an enhanced status code.

What are enhanced status codes?

RFC 3463 adds a class.subject.detail triple after the reply code, and it is the part worth parsing.

Class Meaning
2.x.x Success
4.x.x Persistent transient failure: retrying may succeed
5.x.x Permanent failure: retrying will not help
Subject Area
x.0.x Other or undefined
x.1.x Addressing: the recipient or sender address
x.2.x Mailbox: full, disabled, over quota
x.3.x Mail system: the receiving system itself
x.4.x Network and routing: cannot reach the next hop
x.5.x Mail delivery protocol: bad commands or sequence
x.6.x Message content or media
x.7.x Security or policy: authentication, spam, rate limits

A 5.1.1 is therefore a permanent addressing failure, and 4.7.1 a transient policy failure. The subject tells you whether the problem is the address, the mailbox, the network or your own reputation, and that is the axis a list-hygiene decision turns on.

Which codes will you actually see?

Reply Meaning Bounce type What it says about the address
250 2.1.5 Recipient accepted none The server will take mail for it; on a catch-all server that is all it proves
550 5.1.1 Bad destination mailbox address, "user unknown" hard The mailbox does not exist. Remove the address
550 5.1.2 Bad destination system address hard The domain part is wrong or unroutable
550 5.1.3 Bad destination mailbox syntax hard The address itself is malformed
550 5.2.1 Mailbox disabled, not accepting messages hard The account exists but is closed or suspended
552 5.2.2 Mailbox full, permanent hard Over quota and not expected to clear; treat as risky at best
452 4.2.2 Mailbox full, transient soft Over quota right now; retry in days
450 4.2.1 Mailbox temporarily unavailable soft Often a hold or migration; retry
450 4.7.1 or 451 4.7.1 Try again later, policy soft Greylisting: the server wants a retry from the same sender
421 4.7.0 Too many connections or messages soft Throttling of the sender; nothing about the address
421 4.3.2 System not accepting network messages soft The server is shutting the session; retry later
451 4.4.1 or 4.4.2 Connection or transmission failure soft Network trouble on the receiving side
550 5.7.1 Delivery not authorized, message refused hard for this message A policy rejection: blocklist, reputation, missing authentication. The mailbox may be fine
550 5.7.26 Unauthenticated email is not accepted hard for this message The sender failed SPF or DKIM alignment; fix authentication, not the list

Two rules of thumb follow. A 5.1.x or 5.2.1 reply is about the address and justifies removing it. A 5.7.x reply is about you, the sender, and removing the address would hide a problem you need to fix.

What is the difference between a hard and a soft bounce?

A hard bounce is a permanent 5xx failure about the recipient: the mailbox or domain does not exist. Sending platforms suppress hard-bounced addresses automatically and mailbox providers count repeated hard bounces against your reputation. A soft bounce is a transient 4xx failure: the mailbox is full, the server is busy, the message is too large. Senders retry soft bounces for a day or two and then give up.

The line blurs at the edges. A 552 5.2.2 is permanent by class but describes a full mailbox, and a 550 5.7.1 is permanent for this message but says nothing about the recipient. Classifying purely by the first digit gets both wrong, which is why the enhanced code matters.

How does a verification handshake use these codes?

A verifier never sends DATA, so it only sees replies up to RCPT TO. That is enough to classify the mailbox, and Spaw reports the classification in smtp_reason next to the verdict.

Server reply during the handshake smtp_reason Verdict
5.1.1 user unknown at RCPT TO no_mailbox undeliverable, reason mailbox_not_found
5.2.1 mailbox disabled disabled undeliverable, reason mailbox_disabled
5.2.2 or 4.2.2 mailbox full mailbox_full risky, reason mailbox_full
4.7.x come back later greylisted rests on the other signals, smtp_checked: false
No reply within the time limit timeout rests on the other signals
Connection refused on port 25 no_connect rests on the other signals
Host unreachable mx_unreachable rests on the other signals
5.7.x policy rejection or any ambiguous refusal unverified, never guessed rests on the other signals

Only an explicit "no such user" becomes no_mailbox. A policy rejection during verification is not evidence about the mailbox, so Spaw leaves mailbox_exists at null rather than turning a reputation problem on the verifier's side into a bounce prediction for you.

curl https://spaw.co/api/v1/email \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"email": "[email protected]"}'
{
  "success": true,
  "data": {
    "email": "[email protected]",
    "deliverable": "undeliverable",
    "reason": "mailbox_not_found",
    "risk_score": 100,
    "smtp_checked": true,
    "mailbox_exists": false,
    "smtp_reason": "no_mailbox",
    "// 20 more fields": "see the endpoint reference"
  },
  "meta": { "credits_used": 0, "credits_remaining": 10, "cache_hit": false }
}

Undeliverable answers are always free, and the address is added to your suppression list so batch and bulk runs answer it without another handshake.

What should you do with bounce codes from your own sends?

Feed them back. The codes your sending platform records after a real delivery attempt are stronger evidence than any handshake, in both directions: a delivery proves the mailbox, a 5.1.1 disproves it. Post the outcome to the feedback endpoint with the diagnostic string, or point your provider's bounce webhook at Spaw and let the adapters for Postmark, Amazon SES, Mailgun and SendGrid map their event types. Hard bounces join your suppression list, deliveries turn later lookups into confirmed mailboxes, and the per-domain bounce rate feeds the confidence score for catch-all addresses.

What to do next

Related

markdown version: /guides/smtp-bounce-codes-explained.md

Verify addresses the same way

The Spaw API runs every check described here on each lookup, with an SMTP handshake that never sends mail. 10 free lookups a month, no card required.

Get your API key

More guides