Blog/Sales & enrichment
5 min read

Email Bounce Rate and Spam Traps: What Verification Fixes

Cut your email bounce rate by verifying every address before you send, and learn which spam traps no verifier can catch. Live results from six calls.

Email Bounce Rate and Spam Traps: What Verification Fixes

Clean an email list of bounces before you send, in one prompt. Paste this into your agent:

Set up https://monid.ai/SKILL.md, and then use Monid to verify support@stripe.com and tell me whether it is safe to send, whether the domain accepts every address, and how many public pages list it.

One prompt checks an address. 1 min setup · 10-20 hrs saved.

Your bounce rate is climbing, or you are about to send to a list you did not build, and you want to know what a verifier will fix and what it will not. This guide covers the quickest way to run every address through an SMTP-level check, what the result contains (we ran six calls on 2026-10-16), and the part nobody puts in the sales page: bounces are a problem verification solves, spam traps are not. It runs through Monid, the OpenRouter for agent tools: one key, one balance, every tool priced per call. Monid sells the verifier described here.

What is the easiest way to lower your email bounce rate?

Verify each address before you send to it, and do it from inside your agent. Monid gives your agent a verifier endpoint that checks syntax, the domain, and whether the specific mailbox exists, so the addresses that would hard bounce never reach your sender.

  • Pay per result. A check costs $0.01225 on a definitive verdict (Verified 2026-10-16). Unknown results and disposable or webmail addresses are free. Buy calls as you need them: no monthly plan, no minimum top-up, and discovery and inspection cost nothing.
  • One install, every tool. The same key and balance also call the domain-sizing and people-search endpoints used later in this guide, so cleaning a list and sourcing one use one account.
  • Lives inside your agent. Install the CLI, add a key, paste the prompt above, and the verifier is there whenever your agent needs it. Setup is about a minute.

Checking 100 addresses costs about $1.23 at that price when every verdict is definitive (Verified 2026-10-16). The tools page for this category is people enrichment.

How do you verify a whole list before you send?

Run one verification per address and act on the full response, not on one field. The verifier takes a single email, so a list is a loop your agent writes for you; the email verification API guide covers the cheaper syntax-and-MX checks that sit before it.

RouteWhat you getCost shapeFits
Upload a CSV to a verifier dashboardA cleaned fileCredit pack or subscriptionA person cleaning a big list on a schedule
Write the SMTP checks yourselfWhatever your rules coverYour hoursAn engineer who wants no vendor
Monid, hunterio/email-verifierStatus, score and a check-by-check breakdown per addressPay per definitive verdictPre-send cleaning inside an agent or a script

For agents

Grab an API key at app.monid.ai, then paste this to your agent and hand it the key:

set up https://monid.ai/SKILL.md

It learns the discover, inspect, run workflow itself. More in the agent quickstart.

For humans

npm install -g @monid-ai/cli
monid keys add -k <your-api-key> -l main

The call

monid run -p hunterio -e /email-verifier --query '{"email": "support@stripe.com"}'

Size a domain before you spend anything on it. The count endpoint is free:

monid run -p hunterio -e /email-count --query '{"domain": "yourtarget.com"}'

On 2026-10-16 that call for stripe.com returned a total of 3,964 known addresses, 3,762 personal and 202 generic, with counts by department and by seniority. Slow mail servers keep a verification run in progress, so poll the run if the first response says so. Every verification we ran finished in under four seconds.

Can you find spam traps yourself with Python?

No, and neither can any verifier, because a spam trap is a working mailbox. It is an address that a mailbox provider or a blocklist operator runs to catch senders who do not manage their lists. Its syntax is valid, its domain has MX records, and an SMTP probe succeeds because the mailbox genuinely exists and accepts mail. Every check you can write in Python passes it.

What you can build yourself is the bounce half:

import re
import dns.resolver  # pip install dnspython

def basic_check(addr):
    if not re.match(r"^[^@\s]+@[^@\s]+\.[A-Za-z]{2,}$", addr):
        return "bad_syntax"
    domain = addr.rsplit("@", 1)[1]
    try:
        dns.resolver.resolve(domain, "MX")
    except Exception:
        return "no_mx"
    return "ok"

That catches typos such as a misspelled provider domain and dead domains. It cannot tell a real mailbox from an invented one at a healthy domain. For that you open an SMTP conversation and ask the server about the specific recipient, which means a port 25 connection from your own IP, and a server that sees many probes from one address can block it. A verifier does that from infrastructure built for it. Monid keeps the verifier endpoint maintained, so the SMTP step is one call and not a service you operate.

Rules differ by provider and region, so check the ones that apply to you. The practical defences are about where addresses come from and how long they have been silent, and they live in your data, not in a check.

What does a verification result look like, and what can you build with it?

One flat object per address, with a verdict, a 0 to 100 score, the breakdown behind both, and a list of public pages where the address appeared. These are the four addresses we ran on 2026-10-16:

support@stripe.cominvented mailbox, same domainhello@monid.aitest@mailinator.com
statusvalidinvalidvaliddisposable
score1000920
gibberishfalsetruefalsefalse
mx_recordstruetruetruefalse
smtp_servertruetruetruefalse
smtp_checktruefalsetruefalse
accept_allfalsefalsefalsefalse
sources (public pages)30000
Billed1 result1 result1 resultfree

The fields in each response are status, score, email, regexp, gibberish, disposable, webmail, mx_records, smtp_server, smtp_check, accept_all, block, verification (a date and a status), sources, and a deprecated result that the response itself tells you to stop using. There is no spam trap field, for the reason in the previous section.

The fields work at four levels: regexp and gibberish are syntax, mx_records and smtp_server the domain, smtp_check the mailbox, and disposable, webmail and block reputation. Only smtp_check separated the real Stripe address from the invented one, which passed both domain checks.

Three things worth building on top of it:

  • A send gate. Route every address into send, hold or drop from status, smtp_check and accept_all. The code is in the next section.
  • A source-aware segment. Keep the sources array with each address. An address you collected yourself and an address that sits on thirty public pages are different risks, and the next section says why. Verifying at the point of capture keeps the first kind clean from the start.
  • A sized campaign. Run the free count and people search first, then verify only the rows you will send to. The search returns masked rows with a verification.status before you pay to reveal anything:
monid run -p hunterio -e /multi-domain-search --query '{"company_name": "Stripe", "department": "sales", "verification_status": "valid", "limit": 3}'

It returned 3 rows for a total of 426 matches, free, each with an abbreviated name, position, department, seniority, provenance ("found" on all three), decision_maker, flags for whether a full name, phone and LinkedIn exist, and a reveal_handle. The address itself stays hidden until you reveal it.

📖 See also Hunter Domain Search vs Email Finder

What goes wrong when you try to clean a list of bounces and spam traps?

Six things, most of which we saw in the calls.

A clean verification is not a clean bill of health

The Stripe row scored 100 with every check green. A pristine trap, which is an address seeded where only a scraper will find it, would produce the same row, because the check sees a working mailbox. A recycled trap, a real address abandoned and later reactivated by the provider, was valid when you collected it and is valid now. Only typo traps, such as a misspelled provider domain, are caught by syntax and domain checks. So a 99 percent deliverable list can still contain traps, and the low bounce rate is exactly what makes people keep sending.

The defences that work are about provenance. Do not send to addresses you did not collect yourself, since bought and scraped lists are where pristine traps live. And stop sending to addresses that have been silent for a long time, since silence is the only signal a recycled trap gives. The length of that window is a number to set from your own engagement data. Complaint rate and blocklist status, not the bounce rate, tell you when traps are already hitting you.

The public-pages list is a clue about provenance

sources is the one field in the response that says something about how an address lives on the web. support@stripe.com came back with 30 public pages, from privacy policies to help articles, with extraction dates running from 2026-05-22 to 2026-10-16. The invented address and hello@monid.ai came back with none. An address published in thirty places is one that any scraper has already collected, which is the route pristine traps are built to catch.

The score carries something the booleans do not show

hello@monid.ai scored 92 and support@stripe.com scored 100 with identical results for every boolean: smtp_check true, accept_all false. The only visible difference between the two responses is sources, empty against 30 pages. Do not put a hard threshold on it. Use it to rank addresses inside a list, and set any cutoff from your own bounce data.

Catch-all domains void the verdict

accept_all is the field that quietly empties the mailbox check. When a domain accepts mail at every address, smtp_check cannot fail, so "valid" means the server said yes to everything. All our addresses returned accept_all: false, which is why their verdicts mean something. Route on it:

def route(v):
    if v["status"] == "disposable" or v["disposable"]:
        return "drop"      # a throwaway is not a prospect
    if v["accept_all"]:
        return "hold"      # unknowable; send only with an independent reason
    if not v["smtp_check"]:
        return "drop"      # the mailbox does not exist
    return "send"

The middle branch is the one people skip. A catch-all address is neither confirmed nor refuted, and treating it as deliverable is how a verified list still bounces.

Disposable addresses are free, and the fields look odd

test@mailinator.com returned status: disposable and cost nothing, since disposable and webmail verdicts are not billed. It also returned mx_records: false, smtp_server: false and a legacy result of "risky", so route on status and do not read the domain flags on a disposable address as a verdict on the provider.

Reading the bounce rate itself

A hard bounce is a permanent refusal, a mailbox that does not exist, and it is what verification prevents. A soft bounce is temporary, a full mailbox or a busy server, and is mostly noise unless it persists. Watch the trend more than the level: a rate that climbs over three sends is a list going stale. Hold yourself to the limits in your sender's current guidance.

📖 See also My Bounce Rate, Before and After

Which endpoint should I use for which job?

Use the verifier to remove hard bounces, and the two free endpoints to decide which addresses are worth verifying at all.

EndpointWhat it doesInputOutputBest forBilling
hunterio/email-verifierVerifies one addressAn emailStatus, score, 12 check fields, sourcesRemoving hard bouncesPer definitive result
hunterio/email-countSizes a domainA domain or companyTotals by type, department and seniorityDeciding whether a domain is worth workingFree per call
hunterio/multi-domain-searchSurveys people across companies, maskedCompany and person filtersMasked rows with verification.statusSourcing a list you choseFree per call

The last row is the one that touches the trap problem, because it changes where the list comes from instead of cleaning it afterwards: rows arrive with a verification status before you pay to reveal an address. Choosing a credit pack against a metered call is the fork to read if you clean lists in large batches.

When is another route a better fit?

Your list is bought or scraped. Verification will make it look clean and will not make it safe. The problem is provenance, and no per-address check reaches it. Source contacts you can account for.

Your complaint rate is the issue. If people mark you as spam, every address is real and every one is deliverable. That is a targeting and consent problem, and cleaning the list changes nothing.

You send to catch-all domains at volume. Verification returns accept_all: true and nothing more, so you fly blind by definition. A few such addresses are an ordinary risk; a large share of the list is a decision to send unverified mail.

You clean one very large file. A dashboard that takes a CSV fits a single big job better than one call per address.

And the disclosure: this is Monid's blog and Monid sells per-result access to the verifier described here. The honest summary of what we measured is that it did the job it can do, and the thing you are most afraid of is the thing it cannot detect.

Conclusion

To lower an email bounce rate, verify every address before sending and route on status, smtp_check and accept_all; to avoid spam traps, change where your addresses come from and when you stop sending to silent ones. Through Monid the verifier is one call at $0.01225 per definitive result (Verified 2026-10-16), with one key for everything else and no minimum top-up. In our runs it separated a real mailbox from an invented one at the same healthy domain, which is the case that drives hard bounces.

Two things matter more than the choice of verifier. A response of twelve green checks says nothing about traps, since a trap is a working mailbox. And accept_all decides whether the verdict means anything at all.

How we estimated the time saved: building and running your own list cleaner is roughly 5 to 10 hours for the SMTP probing, disposable and role rules and the retry handling, plus 5 to 10 hours for the routing and sunset logic and the first quarter of upkeep. That is 10 to 20 hours. The 1 minute is install, key and first call once you already have a Monid key.

Free next step: run monid run -p hunterio -e /email-count --query '{"domain": "yourtarget.com"}' on a domain from your list. It costs nothing and shows whether the addresses you hold look like what is known to exist there. Start at monid.ai.

FAQ

What are pristine, recycled and typo spam traps?

A pristine trap was never a person's address; it is seeded on pages so that anyone harvesting addresses collects it, which makes its presence in your list a sign of scraping. A recycled trap was a real mailbox, abandoned and later reactivated by the provider as a trap, so you collected it legitimately and it has since gone quiet. A typo trap is a misspelled version of a common domain, and it is the only kind that syntax and domain checks reliably catch.

Can an email verifier detect spam traps?

No. A spam trap is a working mailbox, so it passes syntax, MX and SMTP checks, and the verifier response has no trap field: the one we ran returned status, score and the check breakdown, and nothing about traps. The defences are sourcing contacts yourself and stopping sends to addresses that stay silent.

What should you do with catch-all addresses?

Hold them and decide on purpose. A catch-all domain returns accept_all: true, so the mailbox check cannot tell you whether the person exists. Send only when you have an independent reason to believe the address is real, such as a recent reply or a confirmed role, and keep them in their own segment so their results do not blur your numbers.

What is a good email bounce rate?

Lower is better, and the direction matters more than the level. A steady low rate is normal churn; a rate that rises across consecutive sends means the list is decaying faster than you clean it. Your email platform and mailbox providers publish the limits that apply to you, so use those instead of a number from a blog.

How much does it cost to verify an email list?

At $0.01225 per definitive result (Verified 2026-10-16), 1,000 addresses cost about $12.25 when every verdict is definitive, and less when some come back disposable, webmail or unknown, because those are free. The domain count and the masked people search used to size a campaign cost nothing.

Published September 2026.

spam trapsemail bounce rateemail verificationdeliverabilitylist hygiene