Hard and soft bounces are not two sizes of the same thing
Every sending platform splits bounces into two columns: hard and soft. The split looks like a severity scale - one is bad, the other is less bad. It is not a severity scale. The two columns describe two different objects: a hard bounce is a statement about the mailbox, and a soft bounce is a statement about the moment.
What the codes actually say
| Response | What it is about | What it licenses you to do |
|---|---|---|
| 550 5.1.1 user unknown | The mailbox | Retire the address. The server looked and could not find it |
| 550 5.1.2 / 5.4.1 - domain or host not found | The domain, not the person | Retire the domain decision, then re-check whether the employer changed - 23% of contacts change jobs in a year (ZoomInfo, 2025) |
| 552 / 452 4.2.2 mailbox full | The moment | Nothing. Retry later; a full mailbox is a working mailbox with a storage problem |
| 421 / 450 greylisting and rate limits | Your probing pattern | Slow down and re-probe. The address was never judged |
| 5.x.x from a relay in the middle | The relay | Nothing about the recipient. A block on the path is not a verdict on the mailbox |
Only the first two rows are statements about whether the address can exist. The rest are statements about the attempt.
The expensive habit: scrubbing the moment as if it were the mailbox
Most pipelines act on a single boolean - did the send fail - and delete the contact. That turns three different events into one: a mailbox that never existed, a mailbox that was full on Tuesday, and a probe that was rate-limited. Two of the three deletes cost you a live contact.
- Nothing downstream reports the loss. There is no event attached to a contact that was never contacted, so the deleted-live-lead rate never appears on a dashboard.
- It compounds with decay. 40% of email addresses are dead within two years (NeverBounce) and 23% of contacts change jobs annually (ZoomInfo, 2025). A list is not static between cleans - it is moving while you decide.
- It hides the real problem. If most of your "hard" bounces come from one source or one domain, that is a sourcing finding. Deleting individual rows quietly files it as noise.
The rule that survives contact with reality
- Retire only on an explicit negative answer. An address leaves the list when a server said the mailbox does not exist - not when the attempt failed.
- Keep unresolved as its own state, with the date. A timeout or a rate limit with no timestamp is indistinguishable from a mailbox that died six months later.
- Re-probe before the send, not on a calendar. The reason to re-check is that a batch is going out; a monthly scrub checks dates, not deliverability.
- Watch the soft-bounce pile for a pattern. Repeated soft bounces on one domain usually mean a blocked path or an aggressive gateway, not a list of dead people.
- Log the domain-level failures separately. "Domain gone" and "person gone" lead to different fixes, and only one of them means find a new contact at the same company.
How this looks in a check that is already running
TAPAC probes the mailbox inside the search that found the contact, so a result arrives with one of three states rather than a boolean: deliverable, undeliverable, or unknown for catch-all domains - plus the time of the call and the source page or profile the address came from. That timestamp is what separates a contact that should be retried from one that should be retired.
npx -y @tapacapi/mcp # tools: tapac_find_contacts, tapac_status # or hosted: https://tapacapi.com/mcp # or REST: POST https://tapacapi.com/v1/contacts/search
Checked contacts carry a published 2-5% bounce rate against 10-35% for static, resold databases; TAPAC is pay-per-use at $0.10-0.50 per contact with 100 free searches.
Related: what a verification log should contain, when the mail server never answers, and cleaning a list as maintenance, not a cure.
← Back to TAPAC