# Email Verifier & Finder (no SMTP): Syntax, MX, Disposable, Role (`everyotherfriday/email-verifier`) Actor

Clean a list of up to 50,000 addresses in one run with DNS-level checks and clear verdicts, plus a finder that turns first name, last name and domain into ranked candidate emails. Honest about limits: no mailbox pings, so catch-all domains are flagged as risky rather than guessed.

- **URL**: https://apify.com/everyotherfriday/email-verifier.md
- **Developed by:** [Paul Vasquez](https://apify.com/everyotherfriday) (community)
- **Categories:** Lead generation, Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 email checkeds

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 and Pattern Finder — No SMTP

Check email syntax, DNS mail routing, provider identity, disposable domains, role accounts, and optional Gravatar presence. Generate ranked address candidates from a person's name and a domain. This actor makes no SMTP connections, sends no email, and never confirms mailbox existence. A `deliverable` verdict means only that the supported syntax is valid and DNS advertises mail routing, without the risk flags described below. It is not a guarantee that a message will arrive.

### Quick start

Use Python 3.12. Create a virtual environment, install the pinned dependencies, and run the actor locally:

```powershell
python -m venv .venv
.venv/Scripts/python.exe -m pip install -r requirements.txt
apify validate-schema .actor/input_schema.json
.venv/Scripts/python.exe -m unittest discover -s tests -v
./validation/run_live.ps1
```

The validation script copies `INPUT.json` into fresh actor-local storage and runs the actual Apify SDK entry point. It requires no keys, forces local execution in the child process, and exports counts, timings, and rows under `validation/`. Pass `-Python path/to/python.exe` to use an existing compatible interpreter. On Linux, use `.venv/bin/python validation/live.py`. For ordinary execution, supply input through Apify's default key-value store and run `python -m src` or `apify run`.

The supplied daily sample contains 30 mixed addresses and three finder entries. It includes public role addresses, synthetic test identifiers, obvious syntax failures, disposable services, typo domains, and one blank. Names and known examples in finder samples are illustrative; they are not evidence of actual people or contacts at those organizations.

### Input

`emails` accepts up to 50,000 strings. Whitespace around an address is trimmed; domain names are lowercased and converted to IDNA ASCII. Local-part case is preserved. The practical RFC-style subset supports dot-atoms, plus tags, and apostrophes. Quoted local parts, comments, domain literals, and internationalized local parts requiring SMTPUTF8 are unsupported and marked invalid. Invalid syntax is still a completed, billable address check.

`finder` accepts up to 5,000 objects containing `firstName`, `lastName`, and `domain`. Each may include up to 1,000 `knownExamples`: email strings or objects with `email`, `firstName`, and `lastName`. Named examples provide exact pattern evidence. Bare addresses provide weak separator evidence; ambiguous unseparated strings do not identify a pattern. Other-domain and invalid examples are ignored.

`checkGravatar` defaults to true. Set it false to avoid sending address hashes to Gravatar. `concurrency` defaults to 20 and separately caps DNS lookups, HTTP requests, and input batch size. `timeoutSecs` defaults to five seconds per network attempt. HTTP 429/5xx responses and transport failures receive two retries, with one- and two-second backoff. Transient DNS failures receive the same bounded retry schedule. The timeout is not a whole-run deadline; large lists naturally take longer.

### Checks and interpretation

Address rows include `email`, `normalized`, `valid_syntax`, `suggestion`, `domain`, `domainExists`, `hasMx`, `mxHosts`, `mxProvider`, `disposable`, `role`, `freeProvider`, `catchAllLikely`, `gravatar`, `verdict`, `reasons`, and `source`. Additional `rowType` identifies verify, finder, error, or summary rows.

`domainExists` specifically reports A/AAAA presence; an MX-only domain can legitimately have false here and still accept mail. DNS uncertainty produces null rather than false. MX provider identification uses hostname suffixes and may identify a filtering gateway rather than the ultimate mailbox host. Null MX explicitly rejects mail. No MX and no address records is undeliverable; address-only fallback is risky.

Disposable, role, suspected typo, and catch-all signals make otherwise plausible addresses risky. Free-provider membership alone does not. Suggestions are never silently applied. Catch-all likelihood reflects configurable wildcard/disposable providers or address-only fallback; it does not establish that arbitrary recipients are accepted. Unknown DNS results remain risky, not proof of rejection. Gravatar returns true for HTTP 200, false for 404, and null when disabled, skipped, or unavailable. Avatar presence never upgrades a verdict.

### Finder results and pricing

Finder rows contain names, domain, candidates, best, source, and an explicit qualification. Ten patterns include first.last, first, flast, first\_last, f.last, firstl, last.first, firstlast, last, and lastf. Names are transliterated to ASCII letters; unusable names produce free errors. Duplicate candidates are removed. Each candidate contains its email, pattern, verdict, ranking score, reasons, and full checks. Scores are ordering weights, not probabilities. `best` is null when every candidate is undeliverable. These are guesses: “syntactically plausible + domain accepts mail” always carries the DNS-only qualification.

Pricing is $0.0005 per processed address and $0.004 per completed finder entry, including all candidate checks. Blank/malformed input errors and empty-input summaries are uncharged. One `Actor.charge` call precedes each billable row; exhausted budgets stop output. Charging and persistence are not atomic. Configure the two custom events and disable synthetic events before publication; local runs do not bill.

The bundled [disposable-email-domains list](https://github.com/disposable-email-domains/disposable-email-domains) includes its CC0 license, pinned source commit, and checksums in `vendor/`. Subdomains inherit list matches. Lists and provider mappings can become stale; review updates periodically. See `VALIDATION.md` for observed results and deployment limits.

### Example output

One recorded dataset row, trimmed by omitting fields only. Values are the saved snapshot, not current measurements. Source: [validation/rows.json](validation/rows.json), first row.

```json
{
  "rowType": "verify",
  "normalized": "info@python.org",
  "valid_syntax": true,
  "hasMx": true,
  "role": true,
  "verdict": "risky",
  "reasons": [
    "Syntactically plausible + domain accepts mail according to DNS only; mailbox unconfirmed",
    "Shared or automated role account",
    "Mailbox existence unconfirmed; no SMTP used"
  ]
}
```

### Use cases

- A CRM administrator can flag syntax errors and suspected domain typos before importing contacts, keeping suggested corrections for human review.
- A newsletter operations team can separate disposable and role addresses for its own list-quality policy without treating DNS as mailbox confirmation.
- A recruiting researcher can generate candidate patterns from a supplied name and domain, using known examples to rank guesses for independent verification.
- A customer support team can inspect mail-routing and provider signals when investigating address problems, preserving unknown DNS outcomes instead of assuming rejection.

### Pricing example

Hypothetical batch, calculated from [`.actor/pay_per_event.json`](.actor/pay_per_event.json):

| Event | Count | USD per event | Subtotal |
| --- | ---: | ---: | ---: |
| `email-verified` | 10,000 | $0.0005 | $5.0000 |
| `finder-query` | 100 | $0.004 | $0.4000 |

Total declared event charges: **$5.40**. These counts are a budgeting example, not a promised yield or an actual bill. Any applicable platform or proxy costs are outside this calculation.

Processed invalid-syntax addresses count as checks. Finder candidates are included in the finder event, with no separate per-candidate charge.

### Limitations

No SMTP or mailbox-existence check is performed. Finder scores rank guesses rather than estimate delivery probability. The saved public role address is classified risky; it is not a verified contact.

# Actor input Schema

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

Up to 50,000 addresses. Invalid syntax is processed and charged; blanks produce free errors.

## `finder` (type: `array`):

Objects with firstName, lastName, domain, optional knownExamples (emails or named objects with email/firstName/lastName). No mailbox confirmation.

## `checkGravatar` (type: `boolean`):

Send the MD5 of each normalized email to Gravatar; an avatar never proves mailbox existence.

## `concurrency` (type: `integer`):

Maximum DNS lookups in flight, HTTP requests in flight and input batch size.

## `timeoutSecs` (type: `integer`):

Seconds per DNS or HTTP attempt; transient failures have two retries with 1/2 second backoff.

## Actor input object example

```json
{
  "emails": [],
  "finder": [],
  "checkGravatar": true,
  "concurrency": 20,
  "timeoutSecs": 5
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("everyotherfriday/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 = {}

# Run the Actor and wait for it to finish
run = client.actor("everyotherfriday/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 '{}' |
apify call everyotherfriday/email-verifier --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,everyotherfriday/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/LXhrWPtflencWzaO6/builds/ZxIXINhJoW3a5Byvu/openapi.json
