# Recipes

> Working solutions: clean a CRM export, gate a signup form, block disposable addresses, verify a mailing list in CI, keep a suppression list in sync.

Source: https://emailverifypro.com/docs/recipes

Complete, working solutions to the things people actually build. Every snippet runs
as-is once you substitute your key.

## Clean a CRM export before a campaign

You have a CSV of leads and want only the addresses that will land.

```bash
emailverify login
emailverify bulk --file leads.csv --out verified.json --timeout 300
```

`--file` accepts one address per line, a JSON array, or a CSV — for a CSV it picks the
column containing an `@`. Then keep only the sendable ones:

```bash
jq -r '.results[] | select(.status == "valid" or .status == "accept_all") | .email' \
  verified.json > safe-to-send.txt
```

Or in Python, using the deliverability score rather than the raw status:

```python
import csv, json
from emailverify import Client

client = Client.from_env()

with open("leads.csv") as fh:
    emails = [row["email"] for row in csv.DictReader(fh) if row.get("email")]

safe, risky = [], []
for i in range(0, len(emails), 100):                 # bulk caps at 100 per call
    for r in client.bulk(emails[i:i + 100])["results"]:
        score = r.get("deliverability_score") or {}
        target = safe if score.get("can_send") else risky
        target.append(r["email"])

print(f"{len(safe)} safe, {len(risky)} risky")
open("safe-to-send.txt", "w").write("\n".join(safe))
```

## Gate a signup form

Validate syntax instantly on the client, then verify properly on the server before you
create the account.

```javascript
// server side — never trust the browser for this
const res = await fetch(
  `https://emailverifypro.com/validate?email=${encodeURIComponent(email)}`,
  { headers: { "X-API-Key": process.env.EMAILVERIFY_API_KEY } }
);
const data = await res.json();

if (data.is_disposable) {
  return reject("Please use a permanent email address.");
}
if (data.did_you_mean) {
  return suggest(`Did you mean ${data.did_you_mean}?`);
}
if (!data.deliverability_score?.can_send) {
  return reject("We couldn't verify that address.");
}
```

> Keep the full check off the request path where you can — it can take seconds. Either
> run it asynchronously after signup, or use `GET /validate/quick` for a fast DNS-only
> answer and follow up properly in a background job.

## Catch typos at the point of entry

```bash
curl "https://emailverifypro.com/verify/typo?email=someone@gmial.com"
```

```json
{"email": "someone@gmial.com", "did_you_mean": "someone@gmail.com", "confidence": 0.97}
```

## Block disposable addresses

```python
r = client.verify("test@mailinator.com")
if r["is_disposable"]:
    raise ValueError("Disposable addresses are not accepted")
```

## Find someone's address

```python
r = client.call("find-email", domain="example.com",
                first_name="Jane", last_name="Doe")
print(r["email"], r["confidence"])
```

It learns the domain's convention from addresses it has already seen, builds the likely
candidates, and verifies them.

## Keep a suppression list in step with your ESP

Point your ESP's webhook at the matching receiver and bounces feed back automatically:

| ESP | Webhook URL |
|---|---|
| SendGrid | `https://emailverifypro.com/webhooks/sendgrid` |
| Amazon SES | `https://emailverifypro.com/webhooks/ses` |
| Postmark | `https://emailverifypro.com/webhooks/postmark` |
| Mailchimp | `https://emailverifypro.com/webhooks/mailchimp` |

Or record outcomes yourself:

```python
client.call("bounce", email="hard-bounced@example.com", bounce_type="hard")
client.call("delivered", email="landed@example.com")
```

Every report improves the model for your future verifications.

## Verify in CI before a send

```yaml
# .github/workflows/verify-list.yml
name: Verify mailing list
on:
  pull_request:
    paths: ["lists/**.csv"]

jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pipx install emailverify
      - name: Verify and fail on undeliverable addresses
        env:
          EMAILVERIFY_API_KEY: ${{ secrets.EMAILVERIFY_API_KEY }}
        run: |
          emailverify bulk --file lists/campaign.csv --out out.json --json
          bad=$(jq '[.results[] | select(.status == "invalid")] | length' out.json)
          echo "$bad undeliverable addresses"
          test "$bad" -eq 0
```

## Re-verify a warm list on a schedule

Addresses decay — people leave companies and domains lapse. Re-check anything older than
90 days:

```bash
emailverify raw POST /admin/reverify --body '{"older_than_days": 90}'
```

## Deduplicate before you spend credits

Gmail dots, plus-addressing and case differences all hide duplicates:

```bash
emailverify dedup --file leads.txt
```

```json
{"original_count": 5000, "unique_count": 4380, "duplicates_removed": 620}
```

## Ask an AI assistant to do it

With the [MCP server](https://emailverifypro.com/docs/mcp) connected, these all work as plain requests:

- *“Verify every address in this CSV and tell me which will bounce.”*
- *“Is acme.com a catch-all domain?”*
- *“Find the email format for stripe.com, then work out Jane Doe's address.”*
- *“How much of my monthly quota is left?”*
- *“Add these five addresses to my suppression list.”*
