---
name: email-verification-best-practices
slug: email-verification-best-practices
description: This skill should be used when the user asks to "verify emails before sending", "clean an email list", "reduce bounce rate", "validate email addresses", "check if emails are valid", "set up email verification", "compare email verification tools", "verify a cold outbound list", "prevent bounces on cold email", or any variation of verifying, validating, or cleaning email addresses before sending B2B outbound.
category: general
---

# Email Verification Best Practices

Email verification checks whether an email address exists, accepts mail, and is safe to send to before you actually send. It's the last gate between your list and your reputation. Every bounced email damages your sender domain reputation. A 5% bounce rate triggers spam filters. A 10% bounce rate can get your domain blacklisted. Verification costs $0.003-0.01 per email. Reputation recovery takes weeks. The math is obvious.

The principle: verify every email before it enters a sequence. No exceptions. Not "most emails." Not "emails from purchased lists." Every email, every time, regardless of source.

## How Email Verification Works

### The 5-step verification process

Every verification tool runs these checks in order. The result is a composite verdict based on all five.

| Step | What it checks | How | What it catches |
|------|---------------|-----|----------------|
| 1. Syntax check | Is the format valid? (user@domain.tld) | Regex validation | Typos, malformed addresses, missing @ signs |
| 2. Domain check | Does the domain exist and have mail servers? | DNS MX record lookup | Fake domains, expired domains, typosquatted domains |
| 3. Disposable check | Is this a temporary/throwaway email service? | Match against known disposable domain list | Guerrilla Mail, Temp Mail, Mailinator, 10MinuteMail |
| 4. Role-based check | Is this a shared/role address, not a person? | Pattern matching (info@, sales@, support@, admin@) | Shared inboxes that shouldn't receive personal outbound |
| 5. SMTP verification | Does this specific mailbox exist on the server? | Connects to mail server, simulates delivery without sending | Invalid mailboxes, deactivated accounts, full inboxes |

### Verification result categories

| Result | What it means | Action | Typical % of list |
|--------|-------------|--------|-------------------|
| Valid | Email exists, server accepts mail, mailbox is active | Send | 70-85% |
| Invalid | Email doesn't exist or server permanently rejects | Do not send. Remove from list | 5-15% |
| Catch-all | Server accepts mail to any address. Can't confirm specific mailbox | Send with caution. See catch-all rules below | 5-20% |
| Disposable | Temporary email service. Person used a throwaway | Do not send. Remove | 1-3% |
| Role-based | Shared inbox (info@, sales@, support@) | Do not send for cold outbound. May be fine for marketing | 2-5% |
| Risky | Possible spam trap, full mailbox, or greylisted | Do not send | 1-3% |
| Unknown | Server timed out or refused connection. Can't determine | Retry once. If still unknown, treat as risky | 2-5% |

---

## When to Verify

### Verification triggers

| Scenario | When to verify | Why |
|----------|---------------|-----|
| New list import | Before importing into CRM | Prevent bad data from entering the system |
| Before sequence enrollment | Before adding contacts to an outbound sequence | Last gate before sending. Catches recently deactivated emails |
| Monthly re-verification | All contacts in active sequences or nurture | Emails decay at 2-3% per month (people leave jobs). Re-verify monthly |
| After enrichment | After email finder returns results | Finders have 80-95% accuracy. Verification catches the misses |
| Before event follow-up | Before emailing event attendee lists | Event lists often have typos and outdated emails |
| Before re-engagement campaigns | Before emailing contacts inactive 6+ months | Long-inactive contacts have the highest deactivation rate |

### Verification timing rules

- **Verify as close to send time as possible.** An email verified 30 days ago may have been deactivated since. For high-stakes sends (ABM, enterprise), verify within 48 hours of sending
- **Bulk verify on import. Re-verify on enrollment.** Import verification catches the obvious invalids. Enrollment verification catches the recently deactivated
- **Never skip verification because "these emails came from a trusted source."** CRM data decays. Enrichment tools have 5-15% error rates. Event platforms have typos. Verify everything

---

## Verification Tool Comparison

### Major tools

| Tool | Price per verification | Accuracy | Speed (bulk) | API | Free tier |
|------|----------------------|----------|-------------|-----|-----------|
| NeverBounce | $0.003-0.008 | 97%+ | Fast (100K in < 1 hour) | Yes | 1,000 free |
| ZeroBounce | $0.005-0.008 | 98%+ | Fast | Yes | 100 free |
| Bouncer | $0.005-0.008 | 97%+ | Fast | Yes | 100 free |
| BriteVerify (Validity) | $0.008-0.01 | 97%+ | Medium | Yes | None |
| Hunter (built-in) | Included with Hunter plan | 93-95% | Medium | Yes | 25 free |
| Snov.io (built-in) | Included with Snov plan | 90-93% | Medium | Yes | 50 free |
| EmailListVerify | $0.002-0.004 | 95%+ | Fast | Yes | 100 free |
| MillionVerifier | $0.001-0.003 | 94-96% | Fast | Yes | None |

### Tool selection

| Your situation | Best tool | Why |
|---------------|----------|-----|
| Need highest accuracy, enterprise compliance | ZeroBounce or BriteVerify | Highest accuracy. SOC 2 compliant. Enterprise support |
| High volume, budget-conscious | NeverBounce or EmailListVerify | Low cost per verification. Good accuracy at scale |
| Already using Hunter or Snov.io | Built-in verification | No additional tool needed. Good enough for most use cases |
| Need real-time API verification | NeverBounce or ZeroBounce | Both have fast, well-documented APIs for real-time checks |
| Lowest possible cost | MillionVerifier or EmailListVerify | Sub-$0.003 per verification. Slightly lower accuracy |

### Selection rules

- **Accuracy above 95% is the minimum bar.** Below that, you're accepting a 5%+ false-positive rate (marking valid emails as invalid) or false-negative rate (marking invalid emails as valid). Both are expensive
- **API access is essential for automation.** If you're verifying on form submit, on CRM import, or in a workflow, you need API access. Bulk-upload-only tools are fine for one-time cleans but not for ongoing hygiene
- **Test with 500 known-good and 500 known-bad emails.** Measure true positive rate, true negative rate, and unknown/catch-all rate against your ground truth. Every tool claims 97%+ accuracy. Test it yourself

---

## Catch-All Domain Handling

Catch-all domains are the hardest verification challenge. The server says "yes, I accept mail for any address," so verification tools can't confirm whether a specific mailbox exists.

### Catch-all prevalence

- ~15-25% of B2B domains are catch-all
- More common with smaller companies, custom mail servers, and Microsoft 365 tenants configured to accept all mail
- Less common with large enterprises (they typically reject invalid addresses)

### Catch-all decision framework

| Signal | Confidence level | Action |
|--------|-----------------|--------|
| Catch-all + email from trusted finder (Apollo, Hunter, Clearbit) | Medium-high | Send. Monitor bounce. Remove on first bounce |
| Catch-all + email from pattern guess (firstname.lastname@domain) | Medium | Send with caution. Limit to 10% of any send batch |
| Catch-all + email from unknown or purchased list | Low | Do not send. Attempt re-find with a different tool first |
| Catch-all + email from manual LinkedIn research | Medium-high | Send. LinkedIn-sourced data is usually accurate |

### Catch-all rules

- Never batch more than 20% catch-all addresses in a single send. If 40% of your list is catch-all, split into batches and spread over days
- Track bounce rates separately for catch-all vs verified-valid addresses. If catch-all bounces exceed 5%, tighten your catch-all policy
- Remove catch-all addresses on first hard bounce. Don't retry. A hard bounce on a catch-all domain means the pattern guess was wrong
- Re-verify catch-all addresses monthly. Some catch-all domains switch to rejection-based configs over time. A previously catch-all domain may become verifiable

---

## Bounce Rate Management

### Bounce rate targets

| Metric | Target | Warning | Critical |
|--------|--------|---------|----------|
| Hard bounce rate | < 2% | 2-3% | > 3% |
| Soft bounce rate | < 5% | 5-8% | > 8% |
| Total bounce rate | < 3% | 3-5% | > 5% |
| Spam complaint rate | < 0.1% | 0.1-0.3% | > 0.3% |

### Bounce types

| Type | What it means | Action |
|------|-------------|--------|
| Hard bounce | Email address doesn't exist. Permanent failure | Remove immediately. Never retry. Mark as invalid in CRM |
| Soft bounce | Temporary failure (full inbox, server down, rate limited) | Retry once after 24 hours. If still bouncing, treat as hard bounce |
| Block bounce | Sender's IP or domain is blocked by recipient's server | Deliverability problem. Check sender reputation, DNS, and warmup status |

### When bounce rate exceeds targets

```
Bounce rate > 3%?
│
├── Check list source
│   └── Purchased list or unverified import? → Re-verify entire list.
│       Remove invalids. This is a data quality problem
│
├── Check verification coverage
│   └── Were all emails verified before sending? → If no, start verifying.
│       If yes, your verification tool may have low accuracy. Test a different tool
│
├── Check catch-all ratio
│   └── More than 25% of bounces from catch-all addresses? → Tighten
│       catch-all policy. Reduce catch-all volume per batch
│
├── Check email age
│   └── Sending to contacts found 3+ months ago? → Re-verify before send.
│       Email addresses decay at 2-3% per month
│
└── Check domain reputation
    └── Using a new or recently warmed domain? → Reduce send volume.
        Warm up slower. Check SPF/DKIM/DMARC configuration
```

---

## Verification Workflow Integration

### Workflow 1: Verify on CRM import

```
CSV upload / API import
  → Pre-import: bulk verify entire list
  → Filter: remove Invalid, Disposable, Risky
  → Flag: mark Catch-All with "catch_all = true" property
  → Import: only Valid + Catch-All (flagged) into CRM
  → Log: record verification date and result per contact
```

### Workflow 2: Verify on sequence enrollment

```
Contact added to sequence
  → Check: last_verified_date > 30 days ago?
  → If yes: re-verify via API before enrollment
  → If result = Valid: enroll
  → If result = Invalid: remove from sequence, update CRM
  → If result = Catch-All: enroll with catch-all flag, monitor bounce
  → If result = Unknown: hold, retry in 24 hours
```

### Workflow 3: Monthly re-verification

```
Scheduled monthly (1st of month)
  → Query: all contacts with lifecycle stage = Lead or MQL
    AND last_verified_date > 30 days ago
  → Bulk verify
  → Update CRM: verified_status, last_verified_date
  → Remove from active sequences: any contact now Invalid
  → Alert RevOps: report of newly invalid contacts
```

### Workflow 4: Real-time verification on form submit

```
Form submitted (demo request, content download)
  → API call: verify email in real-time (< 3 seconds)
  → If Valid: process normally (route, nurture, alert)
  → If Invalid: show form error "Please enter a valid work email"
  → If Disposable: show form error "Please use your work email"
  → If Catch-All: process normally (can't determine in real-time)
  → If Unknown/Timeout: process normally, verify async later
```

**Real-time verification rules:**
- Timeout must be < 3 seconds. If verification takes longer, accept the email and verify asynchronously. Don't make the user wait
- Don't block form submission on "Unknown" results. Network issues cause unknowns. Blocking creates a bad user experience for a valid email
- Show user-friendly errors, not technical messages. "Please enter a valid work email" not "SMTP verification failed with 550 response"

---

## Data Hygiene Integration

### Verification properties in CRM

| Property | Type | Values | Purpose |
|----------|------|--------|---------|
| `email_verified_status` | Picklist | Valid, Invalid, Catch-All, Risky, Unknown, Unverified | Current verification state |
| `email_last_verified_date` | Date | Auto-set on verification | Tracks freshness |
| `email_verification_source` | Picklist | NeverBounce, ZeroBounce, Hunter, Manual | Tracks which tool verified |
| `email_catch_all_domain` | Checkbox | Yes/No | Flags catch-all for separate monitoring |
| `email_bounce_count` | Number | 0+ | Tracks cumulative bounces |

### Automation rules

- Contacts with `email_verified_status = Invalid` should be automatically removed from all active sequences
- Contacts with `email_bounce_count >= 2` should be flagged for manual review and removed from outbound
- Contacts with `email_last_verified_date > 90 days` should be re-verified before any new enrollment
- New contacts with `email_verified_status = Unverified` should not be enrollable in sequences until verified

---

## Cost Analysis

### Verification cost vs bounce cost

| Scenario | Verification cost | Bounce cost | Net savings |
|----------|------------------|-------------|-------------|
| 10,000 emails, no verification, 8% bounce rate | $0 | 800 bounces. Domain reputation damaged. 2-4 week recovery. Lost pipeline during recovery | Negative. Recovery cost far exceeds verification cost |
| 10,000 emails, verified at $0.005 each, 1% bounce rate | $50 | 100 bounces. Within healthy range. No reputation impact | $50 spent. Reputation protected. Pipeline uninterrupted |
| 1,000 ABM emails, verified at $0.008 each, 0.5% bounce rate | $8 | 5 bounces. Pristine reputation for high-value sends | $8 spent. Maximum deliverability on accounts that matter most |

**The verification ROI is effectively infinite.** $50-100 per month in verification costs protects a sender reputation that takes weeks to rebuild and thousands of dollars in lost pipeline to recover.

---

## Pre-Send Checklist

Before any outbound send:

- [ ] All emails verified within the last 30 days
- [ ] Invalid, Disposable, and Risky addresses removed
- [ ] Catch-all addresses flagged and limited to < 20% of the batch
- [ ] Role-based addresses (info@, sales@) excluded from personal outbound
- [ ] Bounce rate from last send was < 3% (if not, investigate before sending again)
- [ ] SPF, DKIM, and DMARC configured on sending domain
- [ ] Sending domain is warmed up (if new)
- [ ] Unsubscribed contacts excluded
- [ ] Previous hard-bounced contacts excluded

---

## Anti-Pattern Check

- Skipping verification because "these emails came from Apollo/Hunter." Even the best finders have 5-15% invalid results. Verify everything. The $50 cost protects your domain reputation
- Treating catch-all as "valid." Catch-all means "we can't tell." Send with caution, monitor bounces, remove on first hard bounce. Don't treat catch-all the same as verified-valid
- Verifying once and never re-verifying. Email addresses decay at 2-3% per month. A list verified 6 months ago has 12-18% decay. Re-verify monthly for active contacts, quarterly for nurture lists
- Sending to contacts that previously hard-bounced. A hard bounce is permanent. The email doesn't exist. Never retry. Mark as invalid in CRM and exclude from all future sends
- No verification properties in CRM. Without `email_verified_status` and `email_last_verified_date`, you can't automate verification workflows or report on data quality. Add these properties on Day 1
- Accepting 5%+ bounce rate as normal. Below 2% is healthy. 2-3% is a warning. Above 3% is damaging your domain. If bounce rate exceeds 3%, stop sending and diagnose before continuing
- Real-time verification blocking form submissions for > 5 seconds. If the verification API is slow, accept the email and verify asynchronously. A blocked form loses the lead entirely
- Not tracking bounce rates separately for catch-all addresses. If catch-all bounces are 15% while verified-valid bounces are 1%, your catch-all policy is too loose. Track them separately to know