DEV Community

Axel Freeman
Axel Freeman

Posted on Originally published at tapacapi.com

A hard bounce and a soft bounce do not describe the same object

Every sending platform splits bounces into two columns, hard and soft, and the layout makes them look like a severity scale. They are not two sizes of the same thing. A hard bounce is a statement about a mailbox. A soft bounce is a statement about a moment.

What the codes actually say

Response What it is about What it licenses
550 5.1.1 user unknown The mailbox Retire the address - the server looked and could not find it
550 5.1.2 domain not found The domain, not the person Retire the domain decision, then ask whether the employer changed
452 / 552 mailbox full The moment Nothing. A full mailbox is a working mailbox with a storage problem
421 / 450 greylisting, rate limits Your probing pattern Slow down and re-probe. The address was never judged
5.x.x from a relays in the path The relay Nothing about the recipient

Only the first two rows are statements about whether the address can exist. The rest are statements about the attempt.

The expensive habit

Most pipelines act on one boolean - did the send fail - and delete the contact. That collapses three different events into one: a mailbox that never existed, a mailbox that was full on Tuesday, and a probe that got rate-limited. Two of those three deletions cost you a live contact, and nothing downstream reports the loss, because there is no event attached to a contact that was never contacted.

Meanwhile the list keeps moving underneath the decision. 23% of contacts change jobs in a year (ZoomInfo, 2025) and 40% of email addresses are dead within two years (NeverBounce). A clean list is not a static object waiting to be cleaned; it is decaying while you decide what to delete.

The pattern matters too. If most of your "hard" bounces come from one source or one domain, that is a sourcing finding, not a list-quality finding - and deleting rows individually files it as noise.

The rule that survives contact with reality

  • Retire only on an explicit negative answer. An address leaves when a server said the mailbox does not exist - not when the attempt failed.
  • Keep unresolved as its own state, with a date. A timeout 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 validates dates, not deliverability.
  • Watch the soft 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 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.

What a checker can add

The published spread between checked and unchecked contacts is 2-5% bounce against 10-35% for static resold databases, and that gap is exactly this distinction: a check that answers about the mailbox, not about the attempt.

TAPAC probes the mailbox inside the search that found the contact, so each result arrives with one of three states - 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 worth retrying from one worth retiring.

npx -y @tapacapi/mcp        # tools: tapac_find_contacts, tapac_status
# hosted: https://tapacapi.com/mcp
# REST:   POST https://tapacapi.com/v1/contacts/search
Enter fullscreen mode Exit fullscreen mode

Pay-per-use, $0.10-0.50 per contact, 100 free searches.

Top comments (0)