# Email Verifier — Bulk Validation, Catch-All & Bounce Removal (`logical_vivacity/email-verifier`) Actor

The email verifier that admits what it cannot know. Catches syntax errors, dead domains, disposables, role accounts and catch-alls — and flags Microsoft 365, Proton and gateway domains as unverifiable instead of calling them valid. Returns SPF, DMARC, MX provider and a 0-100 score.

- **URL**: https://apify.com/logical\_vivacity/email-verifier.md
- **Developed by:** [Logical Vivacity](https://apify.com/logical_vivacity) (community)
- **Categories:** Lead generation, Business, Developer tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.45 / 1,000 email verifieds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Email Verifier — Bulk Email Validation, Catch-All & Bounce Removal

**The email verifier that admits what it cannot know.**

Here is the thing the industry doesn't advertise. Microsoft 365, Outlook.com, Proton, Yahoo and the security gateways in front of corporate mail — Proofpoint, Mimecast, Barracuda — accept mail for **every** address at a domain and decide afterwards whether it bounces. There is no way to ask them "does this mailbox exist" and get a true answer. Not with a better algorithm, not with more IPs, not at any price.

Roughly a third of a typical B2B list sits behind exactly those providers. Every other verifier marks them **valid**, because "valid" is what people want to buy. Then you send, and the bounces arrive.

This Actor returns `unknown` or `risky` for those addresses, with a reason code and a sentence explaining who the provider is and why nobody can confirm the mailbox. Then it gives you every other signal that *can* be established — MX provider, catch-all behaviour, SPF, DMARC, DKIM, role and disposable flags, typo corrections, a 0-100 score — so you can make the call yourself instead of trusting a green tick that was a guess.

**You are charged per email checked.** Duplicates are removed before anything is checked or billed.

***

### Try it in 30 seconds

Leave the defaults on, paste a few addresses, hit Start:

```
sarah.chen@shopify.com
info@stripe.com
j.martinez@gmial.com
```

You get one row per address, plus a summary row at the end telling you what percentage of the list you should probably drop.

***

### The five verdicts, and what to do with each

| `verdict` | What it means | What to do |
|---|---|---|
| `valid` | The mail server accepted this specific address, and the domain is not catch-all. As close to confirmed as email gets. | **Send.** |
| `invalid` | Broken syntax, no mail server, dead domain, or the server explicitly rejected the mailbox. | **Remove.** These are your bounces. |
| `catch_all` | The domain accepts every address, proven by probing one that certainly doesn't exist. The mailbox may or may not be real. | **Send carefully** — separate campaign, watch the bounce rate. Small agencies and one-person domains are often catch-all and perfectly good. |
| `risky` | Deliverable-looking, but a poor bet: disposable domain, role inbox, or a personal address on a B2B list. | **Segment.** Don't put these in a cold sequence with the rest. |
| `unknown` | The mailbox genuinely cannot be checked — the provider accepts everything, or mailbox-level checking was unavailable. The domain signals still come back. | **Judge on the score and the signals.** A 75-scoring address at a Microsoft 365 domain with strict SPF and a DMARC policy is a very different bet from a 45 with neither. |

The honest way to read this: `valid` and `invalid` are facts. `catch_all`, `risky` and `unknown` are the situations where a fact is not available, labelled as such rather than rounded up.

***

### Reason codes

Every row carries a machine-readable `reasons[]` array, so you can filter in a sheet or a script without parsing prose.

| Code | Meaning |
|---|---|
| `SYNTAX_INVALID` | Not a usable address. `syntaxError` says what's wrong. |
| `LIKELY_TYPO` | A near-miss of a common domain. `didYouMean` has the fix. |
| `DISPOSABLE` | Burner domain, checked against a list of 8,870 of them. |
| `NO_MX` | The domain publishes no mail server. Nothing can arrive here. |
| `M365_UNVERIFIABLE` | Microsoft 365 or Outlook. Accepts all, bounces later. Not checkable by anyone. |
| `PROVIDER_ACCEPTS_ALL` | Proton, Yahoo, or a security gateway doing the same thing. |
| `CATCH_ALL` | Proven catch-all: a random address at this domain was accepted too. |
| `MAILBOX_ACCEPTED` | The mail server accepted this exact address. |
| `MAILBOX_REJECTED` | The mail server refused this exact address. |
| `GREYLISTED` | Temporarily deferred. Re-run this one later. |
| `ROLE_ACCOUNT` | `info@`, `sales@`, `support@` — reaches a team, not a person. |
| `FREE_PROVIDER` | Gmail, Yahoo, iCloud and similar. Only counts against the score in B2B mode. |
| `SMTP_UNAVAILABLE` | Mailbox-level checking wasn't possible on this run; domain signals only. |
| `SMTP_NO_ANSWER` | The mail server didn't respond to the probe. |

***

### What a row looks like

```jsonc
{
  "email": "sarah.chen@shopify.com",
  "normalizedEmail": "sarah.chen@shopify.com",
  "domain": "shopify.com",

  "verdict": "unknown",            // honest: this provider cannot be checked
  "score": 73,                     // 0-100, built from everything below
  "bounceRisk": "medium",          // low | medium | high
  "isDeliverable": null,           // true / false / null — null means unknowable

  "reasons": ["M365_UNVERIFIABLE"],
  "providerNote": "Microsoft 365 accepts mail for every address at this domain and rejects bad ones later, so no tool can confirm this mailbox exists without sending to it.",

  "syntaxValid": true,
  "isRole": false,
  "isFreeProvider": false,
  "isDisposable": false,
  "isCatchAll": false,
  "didYouMean": null,

  "domainResolves": true,
  "mxFound": true,
  "mxProvider": "Microsoft 365",
  "mxRecords": ["shopify-com.mail.protection.outlook.com"],

  "spfRecord": "v=spf1 include:_spf.google.com include:spf.protection.outlook.com -all",
  "spfPolicy": "strict",           // strict | soft | neutral | any | none
  "dmarcPolicy": "reject",         // none | quarantine | reject
  "dkimFound": true,

  "smtpVerifiable": false,         // could this provider be asked at all?
  "smtpAttempted": false,

  "durationMs": 412,
  "checkedAt": "2026-09-17T19:12:04Z"
}
```

And the last row of every run is the summary you actually want before you press send:

```jsonc
{
  "summary": true,
  "totalChecked": 10000,
  "byVerdict": { "valid": 4120, "invalid": 812, "catch_all": 690,
                 "risky": 431, "unknown": 3947 },
  "estimatedBounceRatePercent": 8.1,   // if you sent the list as-is
  "wouldRemovePercent": 12.4,          // invalid + risky
  "unverifiablePercent": 46.4,         // honestly unknowable, catch-all included
  "mailboxCheckingAvailable": true,
  "domainsCached": 8571,
  "checkedAt": "2026-09-17T19:14:52Z"
}
```

Export as JSON, CSV, Excel, XML or HTML, or pull it from the API.

***

### What makes it different

**It says "I don't know" when it doesn't know.** This is the whole product. Nothing else on the Store will tell you that 40% of your list is behind providers that cannot be verified by anyone — because saying so sounds worse than saying "valid". It isn't worse. It's the number you needed.

**It names the provider.** `mxProvider` tells you whether an address sits at Google Workspace, Microsoft 365, Proton, Zoho, a Proofpoint gateway or a shared host. That one field explains most of your bounce pattern, and nobody else returns it.

**Real catch-all detection, not a guess.** Where the mail server can be trusted, this Actor asks it about an address that definitely does not exist. If that gets accepted, the domain is catch-all and every "valid" you've ever been sold for it was meaningless.

**Domain reputation signals, free with every row.** SPF record and its policy, DMARC policy, DKIM presence. A domain with `-all` SPF and `p=reject` DMARC is a real, managed mail setup. A domain with none of it, on a shared host, is a very different prospect — and that's a lead-quality signal, not just a deliverability one.

**Typos get fixed, not deleted.** `j.martinez@gmial.com` comes back with `didYouMean: "j.martinez@gmail.com"`. That's a recovered lead, not a removed one.

**A summary row that estimates your bounce rate.** Before the send, not after it.

**Explains itself when a check isn't possible.** Cloud networks generally block the outbound port that mailbox-level checking needs. This Actor detects that **once** at the start of a run rather than failing quietly ten thousand times, logs it in plain English, falls back to domain and provider signals, and flags every affected row with `SMTP_UNAVAILABLE` and `mailboxCheckingAvailable: false` in the summary. You are never told a result is stronger than it is.

***

### Who uses this

**Cold-email senders** — clean the list before it touches your sending domain. One bad send costs you more in reputation than a year of verification.

**CRM and database hygiene** — run your contact table quarterly, drop the dead ones, flag the role inboxes, and see which accounts moved mail providers.

**Signup and lead forms** — catch disposable domains and typos at the point of capture, so the lead is reachable instead of theoretical.

**Agencies and lead sellers** — hand clients a list with a defensible quality report attached, including what genuinely couldn't be verified. It's a better conversation than a bounce report a week later.

**Deliverability consultants** — SPF, DMARC, DKIM and MX provider per domain, in one pass, alongside the addresses themselves.

***

### Settings

| Setting | What it does |
|---|---|
| **Email addresses** | One per line. Duplicates removed before checking and before billing |
| **Treat free-provider addresses as lower quality** | B2B mode: personal addresses score lower and are marked risky. Leave off for B2C |
| **Full check, including mailbox-level verification** | On: every signal available, including catch-all detection and mailbox-level checks where meaningful. Off: fast domain-level only |

Concurrency, retries, caching, pacing and network handling are managed for you. There is nothing to tune and no proxy to configure. Per-domain results are cached within a run, so a 10,000-row list from 1,400 companies does the work once per company.

***

### Automate it

```bash
curl -X POST "https://api.apify.com/v2/acts/YOUR_USERNAME~email-verifier/runs?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "emails": [
          "sarah.chen@shopify.com",
          "info@stripe.com",
          "j.martinez@gmial.com"
        ],
        "businessMode": true,
        "deepCheck": true
      }'
```

Works with Make, Zapier, n8n, Google Sheets and the Apify API, and as an MCP tool for AI agents. Schedule it to re-check a list on any cadence — mailboxes die quietly, and a list verified a year ago is not a verified list.

***

### How accurate is this, honestly

We are not going to claim 99%. Nobody can, and the tools that do are counting unverifiable addresses as correct guesses.

What this Actor actually does, on a typical B2B list:

- **Removes 90%+ of the bounces you would otherwise have taken**, mostly by catching dead domains, absent mail servers, broken syntax, disposable domains and mailboxes the server explicitly rejects.
- **Labels the rest accurately.** Where a mailbox can be confirmed, `valid` means confirmed. Where it can't, you get `unknown` or `catch_all` and the evidence, not a coin flip dressed as a fact.
- **Recovers leads** through typo correction that other tools discard as invalid.

The residual bounce rate after cleaning comes almost entirely from the unverifiable population — Microsoft 365 and friends. No tool reduces that further; the difference is that this Actor tells you the size of it in advance so you can plan the send around it. Warm up properly, send that segment separately, and watch the first batch.

Where mailbox-level checking is unavailable on a run (common on cloud networks, which block the port it needs), confirmed `valid` verdicts are fewer and more addresses land in `unknown`. The summary row tells you which kind of run you got.

***

### FAQ

**My other verifier says this address is valid and you say unknown. Who's right?**
Both of us, technically — but they're not telling you the whole story. Their check asked the mail server "will you take mail for this address", and a Microsoft 365 server, a Proton server, or a Proofpoint gateway says yes to that question for **every** address at the domain, including ones made of random characters. They took that yes and printed "valid". We ask the same question, get the same yes, recognise that the yes is meaningless for that provider, and say so. If you want to test it: put a nonsense address at one of your customers' corporate domains through the other tool and see what it returns.

**Then why bother verifying at all, if a third of the list is unknowable?**
Because the other two thirds isn't. Dead domains, missing mail servers, typos, disposable burners and explicitly-rejected mailboxes are the bulk of what actually bounces, and all of it is catchable. The unverifiable slice is the part you manage with sending strategy instead of a filter.

**Do I need an API key or an account anywhere?**
No.

**Am I charged for invalid addresses?**
Yes — the check is the product, and finding out an address is dead is the most valuable result in the run. Duplicates are removed before billing, so you pay once per unique address.

**Why is `isDeliverable` null on so many rows?**
`null` means unknowable, deliberately. `true` and `false` are only used when there's an actual answer behind them.

**Does this send email to anyone?**
No. Verification stops before any message is transmitted, and nothing arrives in anybody's inbox.

**What does `deepCheck` off actually skip?**
DKIM lookups, catch-all probing, and any mailbox-level conversation. You still get syntax, typos, disposable detection, MX and provider identification, SPF and DMARC. It's faster and still catches most bounces; the full check is the default for a reason.

**Can I verify addresses at a catch-all domain properly?**
Not by verifying — that's what catch-all means. Use the score and the domain signals, send to that segment separately, and treat the first campaign as the test.

**How big a list can I run?**
Large ones are fine. Per-domain caching means the cost scales with the number of distinct domains, not rows, so corporate lists run far faster than the row count suggests.

***

### Terms of Use

This Actor performs technical checks against public DNS records and, where possible and permitted, mail servers. It does not deliver mail to anyone.

**Your responsibility.** You are responsible for using the results lawfully, including compliance with GDPR, CAN-SPAM, PECR, CASL and any other marketing, privacy and data-protection law that applies to you and to the people on your list. Verifying an address is not consent to email it, and this Actor does not establish a lawful basis for contact.

**No warranty.** Results are technical signals, not guarantees. A `valid` verdict does not guarantee delivery; an `unknown` verdict does not mean an address is bad. Mailboxes change between the check and your send. The Actor is provided "as is", without warranties of any kind, and to the maximum extent permitted by law the developer is not liable for any loss arising from your use of the results.

**No affiliation.** This Actor is not affiliated with, endorsed by or sponsored by Microsoft, Google, Proton, Yahoo, Proofpoint, Mimecast, Barracuda or any other mailbox or security provider. Those names appear only to identify which provider handles a domain's mail.

***

*Found a provider this Actor mislabels, or need volume pricing?* Open an issue on the Actor's Issues tab — a wrong provider match is the fastest thing to fix and the most useful thing to report.

# Actor input Schema

## `emails` (type: `array`):

One address per line. Duplicates are removed before anything is checked or charged, so pasting a raw export is fine.

## `businessMode` (type: `boolean`):

For B2B outreach. Gmail, Yahoo, Outlook.com, iCloud and similar personal addresses score lower and are marked risky, because a work list full of personal addresses usually means bad data rather than bad mailboxes. Leave off for B2C lists, where a Gmail address is exactly what you want.

## `deepCheck` (type: `boolean`):

On: every signal we can get — DKIM lookups, catch-all detection, and a mailbox-level conversation with the mail server wherever that server's answers can be trusted and the network allows it. Off: fast domain-level checking only (syntax, disposable, MX, SPF, DMARC, typos), which is cheaper to run and still catches most of the bounces.

## Actor input object example

```json
{
  "emails": [
    "sarah.chen@shopify.com",
    "info@stripe.com",
    "j.martinez@gmial.com"
  ],
  "businessMode": false,
  "deepCheck": true
}
```

# Actor output Schema

## `results` (type: `string`):

Every address classified, with the domain and provider signals that decided it.

## `overview` (type: `string`):

The same rows as a spreadsheet-style table, with the most useful columns first.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "emails": [
        "sarah.chen@shopify.com",
        "info@stripe.com",
        "j.martinez@gmial.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("logical_vivacity/email-verifier").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "emails": [
        "sarah.chen@shopify.com",
        "info@stripe.com",
        "j.martinez@gmial.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("logical_vivacity/email-verifier").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "emails": [
    "sarah.chen@shopify.com",
    "info@stripe.com",
    "j.martinez@gmial.com"
  ]
}' |
apify call logical_vivacity/email-verifier --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,logical_vivacity/email-verifier"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/vica8kitTcVvJPRbN/builds/R97Rcadt7Hy2XeKiG/openapi.json
