# How to reduce your email bounce rate

_2026-10-02 · Boundstone (https://boundstone.io/blog/reduce-email-bounce-rate)_


If you want to reduce your email bounce rate, the useful first move is to split "bounce" into two problems, because they have two different fixes. Most advice blurs them together, promises you a magic number, and skips the part where it admits what it cannot do. This post does the opposite: it shows you the bounces you can remove before you send, and it is honest about the ones that only the receiving mail server knows about.

## Hard bounces and soft bounces are not the same problem

A bounce is the receiving server declining your message. There are two flavours, and they call for opposite responses.

A **soft bounce** is temporary. The mailbox is full, the server is busy, the message is too large. Retry later and it may go through.

A **hard bounce** is permanent. The domain does not exist, there is no mail server behind it, or the address is garbage. Retrying does nothing except make you look careless.

That last part is the whole reason to care. Mailbox providers watch your hard-bounce rate as a proxy for how well you manage your list. A steady drip of permanent failures reads like you are mailing addresses you never checked, and your sender reputation — the thing that decides whether the *good* addresses even reach the inbox — pays for it. Soft bounces are noise. Hard bounces are the ones that compound.

## The hard bounces you can see coming

Some hard bounces are unavoidable: an address that was real yesterday and got deleted this morning is invisible to everyone until you mail it. But a meaningful share of them are sitting in your list right now, visible before you press send:

- **Malformed syntax** — `jane@@example`, a trailing comma, a fat-thumbed space. It was never a valid address.
- **A domain with no MX record** — the domain resolves, but nothing behind it accepts mail. Every message to it hard-bounces.
- **A disposable domain** — a throwaway inbox the signup used once and abandoned. It technically accepts mail, but it is dead weight and a spam-trap risk.

These are the avoidable hard bounces. You do not need anyone's permission or a paid provider to catch the first category — your language already does. The point is to catch all three *before* the send, not after the bounce report.

## Validate before you send

The pattern is a gate: check each address, and only queue the ones that clear syntax, have a mail server, and are not disposable. Here is that gate against Boundstone's email endpoint. A single request returns the three signals you need.

```python
import requests

API = "https://api.boundstone.io/v1/verify/email"
HEADERS = {"Authorization": "Bearer bs_live_YOUR_KEY"}

def should_send(email: str) -> bool:
    r = requests.post(API, json={"email": email}, headers=HEADERS, timeout=10)
    r.raise_for_status()
    data = r.json()

    # The avoidable hard bounces: bad syntax, no mail server, throwaway domain.
    if not data["valid_syntax"]:
        return False
    if not data["mx_found"]:
        return False
    if data["disposable"]:
        return False

    return True

for address in ["jane@example.com", "not-an-email", "user@mailinator.com"]:
    print(address, "->", "send" if should_send(address) else "skip")
```

A valid, deliverable-looking address comes back like this:

```json
{
  "valid_syntax": true,
  "domain": "example.com",
  "mx_found": true,
  "disposable": false,
  "role_account": true,
  "free_provider": false,
  "checks": {
    "performed": ["syntax", "mx", "disposable_list", "role_list"],
    "not_performed": ["smtp_mailbox", "catch_all"]
  }
}
```

`role_account` and `free_provider` are there as judgement calls, not hard filters. A role address like `support@` or `sales@` is a mailing-list decision, not a bounce risk, so this gate leaves it in. Note that `free_provider` is reported but is deliberately not listed under `checks.performed` — it is a hint about the address, not a deliverability verdict.

## What validation cannot promise — and why that is the useful part

Look again at `checks.not_performed`: `["smtp_mailbox", "catch_all"]`. This is not fine print. It is the reason you can trust the `valid_syntax` and `mx_found` above it.

Boundstone does **not** perform SMTP mailbox verification, and it does **not** perform catch-all detection. That means validation cannot prove a specific mailbox exists, and it cannot guarantee your message lands. `mx_found: true` tells you the domain is *ready to receive* mail; it does not tell you `jane` is a real account behind it. Only the receiving server knows that, and it will only tell you when you actually send — which is the moment the bounce happens.

So be precise about the win. Validating syntax, MX, and disposable status removes the hard bounces **you can see coming**. It does not remove the ones **only the receiving server can see**. Any tool that claims otherwise is either running live SMTP probes (which have their own reputation cost) or lying to you. Boundstone would rather show you the empty column than pretend it is full. If you want the longer version of that distinction, see [email validation vs verification](/blog/email-validation-vs-verification).

## Where the standard library stops and the API starts

Reach for the free option first, because it is often all you need. Python's `email.utils.parseaddr`, a decent regex, or a library like `email-validator` will catch malformed syntax for zero dollars and zero network calls. If your list problem is mostly typos and paste errors, stop there — you have handled the cheap, common majority for free.

The API earns its place at the next layer: live MX resolution, a maintained disposable-domain list, and one consistent response contract across every language your stack speaks, so the gate above looks the same in Python, Node, or Go. If you would rather see it than read about it, the keyless [email validator](/tools/email-validator) runs the exact same checks in the browser with no signup, and there is a full [validate-email-in-Python](/blog/validate-email-python) walkthrough if that is your stack.

## The short version

- **Split your bounces.** Soft bounces are temporary and mostly harmless; hard bounces damage sender reputation.
- **Kill the avoidable hard bounces before you send:** bad syntax, no MX record, disposable domain. Those three checks are one request.
- **Start with your standard library.** It catches malformed addresses for free.
- **Use the API for the layer past that:** live MX, disposable lists, one contract across clients.
- **Believe the honest limit.** Validation cannot prove a mailbox exists or guarantee delivery — `smtp_mailbox` and `catch_all` are `not_performed`, and that admission is exactly why the `valid_syntax` and `mx_found` next to it mean something.
