# Bulk Email Verifier Pro (`ayeeyee/bulk-email-verifier-pro`) Actor

Bulk-verify email deliverability: syntax + DNS MX/A, disposable/role/free-provider flags, and typo suggestions. Per-email verdict. No SMTP probing, no proxy. Pay only for emails we actually verify - malformed input and DNS failures are free.

- **URL**: https://apify.com/ayeeyee/bulk-email-verifier-pro.md
- **Developed by:** [Virtual Footprint LLC](https://apify.com/ayeeyee) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.25 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Bulk Email Verifier Pro

Bulk-verify email deliverability with **DNS MX/A checks**, **disposable / role / free-provider flags**, and **typo suggestions** — a clear per-email verdict at **$0.005 per verified email** (20× below the $0.10-per-result incumbent). No SMTP probing, no proxy, no residential IPs. **You pay only for emails we actually verify** — malformed input and DNS failures are free.

> **Pricing promise:** `apify-actor-start` $0.0001 (one-time) + `email_verified` **$0.005** charged **once per email we actually verify against DNS** (`deliverable`, `undeliverable-no-mail-server`, or `risky`-disposable). Malformed input, placeholder/example domains, and DNS failures are **never charged** — no-result = no charge. Each result row carries a `dnsChecked` flag so you can see exactly what was billed.

### What it checks (in order, cheapest first)

1. **Syntax** — RFC-pragmatic validation (local part bounded, ≥2-char alpha TLD). Rules ported from our published Website Contact Extractor, so the verifier is never fooled by demo/placeholder emails (`you@company.com`, `info@example.com`) or scraped junk.
2. **Domain MX/A via DNS** — the domain must be configured to receive mail. MX is preferred, with an A-record fallback (per RFC). **Lookups are cached per domain**, so a list of 5,000 addresses at one company costs **one** DNS query, not 5,000.
3. **Disposable / throwaway detection** — a curated static list of common disposable domains (mailinator, guerrillamail, 10minutemail, yopmail, …). No external API call.
4. **Role-account detection** — flags shared/role mailboxes (`info@`, `sales@`, `support@`, `admin@`, `noreply@`, …). B2B buyers usually filter these; most incumbents don't flag them.
5. **Free-provider detection** — `gmail.com`, `yahoo.com`, `outlook.com`, … → `freeProvider: true` (a B2B list usually wants company domains).
6. **Typo suggestion** — obvious typos against well-known domains, e.g. `gmial.com → gmail.com`, returned as `didYouMean`.

### What we deliberately DON'T check (and why)

- **No SMTP mailbox probing.** Connecting to mail servers to confirm a specific mailbox exists gets your IPs blocklisted, is slow at scale, and is unreliable under greylisting and catch-all servers. We'd rather be honest than overclaim.
- **Therefore `deliverable` means the domain is configured to receive mail (MX or A record) — it does NOT guarantee the specific mailbox exists.** `risky` flags disposable domains (they accept mail but are low-value/junk). `unknown` means the DNS check couldn't complete (transient error) and you are not charged.

If you need per-mailbox SMTP confirmation, pair this actor with an SMTP-probing service downstream — but most deliverability screening and list-hygiene jobs only need what we provide, at 20× the value.

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `emails` | array<string> | yes | — | Emails to verify. Bare addresses or `Display Name <a@b.com>` envelopes. Messy input (whitespace, mixed case, blanks, duplicates) is handled. |
| `skipDisposable` | boolean | no | `false` | If `true`, disposable addresses are dropped from the output. Default `false` keeps them with a `risky` verdict + `disposable: true` so you can see what was filtered. |
| `concurrency` | integer | no | `10` | Parallel emails (1–25). DNS is cached per domain. |

```json
{
  "emails": [
    "team@stripe.com",
    "notrealperson@thisdomaindoesnotexistxyz.com",
    "joe@mailinator.com",
    "info@example.com",
    "Jane <jane.doe@gmail.com>"
  ],
  "skipDisposable": false,
  "concurrency": 10
}
```

### Output

One row per input email (or per malformed entry). Duplicates are deduped case-insensitively.

| Field | Type | Description |
|---|---|---|
| `email` | string | The normalized address (lowercased). For malformed input, the original raw string. |
| `valid` | bool|null | `true` = domain accepts mail; `false` = undeliverable (bad syntax or no mail server); `null` = unknown (DNS check failed). |
| `status` | string | `deliverable` | `undeliverable` | `risky` | `unknown`. |
| `reason` | string | Human-readable, always populated. |
| `domain` | string|null | The email domain. |
| `hasMx` | bool|null | `true` if an MX record exists. `null` if the DNS check failed. |
| `mxHost` | string|null | Highest-priority MX exchange host, or `null` if no MX record (even when an A-record fallback makes the domain deliverable). |
| `disposable` | bool | `true` if the domain is a known throwaway provider. |
| `role` | bool | `true` if the local part is a shared/role mailbox. |
| `freeProvider` | bool | `true` if the domain is a free consumer provider (gmail/yahoo/outlook/…). |
| `didYouMean` | string|null | Suggested domain for an obvious typo (e.g. `gmail.com`), or `null`. |
| `dnsChecked` | bool | `true` if the verdict came from a live DNS check (this is what you're billed for). `false` for malformed/placeholder rows that were rejected before DNS — never charged. |
| `checkedAt` | string (ISO) | UTC timestamp of the check. |

### Live-run sample

Real output from a run on Apify (build `latest`). `checkedAt` omitted for brevity; every other field is verbatim from the dataset.

```json
[
  {"email": "team@stripe.com",          "valid": true,  "status": "deliverable",   "reason": "domain is configured to receive mail (MX or A record)", "domain": "stripe.com",   "hasMx": true,  "mxHost": "alt2.aspmx.l.google.com", "disposable": false, "role": true,  "freeProvider": false, "didYouMean": null,       "dnsChecked": true},
  {"email": "jane.doe@gmail.com",        "valid": true,  "status": "deliverable",   "reason": "domain is configured to receive mail (MX or A record)", "domain": "gmail.com",    "hasMx": true,  "mxHost": "alt2.gmail-smtp-in.l.google.com", "disposable": false, "role": false, "freeProvider": true,  "didYouMean": null,       "dnsChecked": true},
  {"email": "notrealperson@thisdomaindoesnotexistxyz.com", "valid": false, "status": "undeliverable", "reason": "domain has no mail server (no MX/A record)", "domain": "thisdomaindoesnotexistxyz.com", "hasMx": false, "mxHost": null, "disposable": false, "role": false, "freeProvider": false, "didYouMean": null, "dnsChecked": true},
  {"email": "joe@mailinator.com",       "valid": true,  "status": "risky",         "reason": "disposable/throwaway domain (accepts mail but junk)",   "domain": "mailinator.com", "hasMx": true, "mxHost": "mail.mailinator.com",  "disposable": true,  "role": false, "freeProvider": false, "didYouMean": null,       "dnsChecked": true},
  {"email": "info@anthropic.com",       "valid": true,  "status": "deliverable",   "reason": "domain is configured to receive mail (MX or A record)", "domain": "anthropic.com","hasMx": true,  "mxHost": "alt3.aspmx.l.google.com", "disposable": false, "role": true,  "freeProvider": false, "didYouMean": null,       "dnsChecked": true},
  {"email": "team@gmial.com",           "valid": true,  "status": "deliverable",   "reason": "domain is configured to receive mail (MX or A record)", "domain": "gmial.com",    "hasMx": true,  "mxHost": "mail.gmial.com",        "disposable": false, "role": true,  "freeProvider": false, "didYouMean": "gmail.com", "dnsChecked": true},
  {"email": "plainjunk",                "valid": false, "status": "undeliverable", "reason": "malformed",                                       "domain": null,           "hasMx": false, "mxHost": null,                    "disposable": false, "role": false, "freeProvider": false, "didYouMean": null,       "dnsChecked": false},
  {"email": "info@example.com",         "valid": false, "status": "undeliverable", "reason": "placeholder/example domain",                      "domain": "example.com",  "hasMx": false, "mxHost": null,                    "disposable": false, "role": false, "freeProvider": false, "didYouMean": null,       "dnsChecked": false}
]
```

**Charge audit for this run:** 6 `email_verified` charges (the six `dnsChecked: true` rows) + 1 `apify-actor-start`. The two `dnsChecked: false` rows (malformed `plainjunk`, placeholder `info@example.com`) are **not charged** — exactly as promised. (`mxHost` values reflect Google's rotating MX hosts at run time; `gmial.com` is a real registered domain that accepts mail, so it's correctly `deliverable` *and* flagged `didYouMean: gmail.com`.)

### Pricing vs. the market

| | Per-result price | Charges on errors? | Role flag | Disposable flag | Typo suggestion |
|---|---|---|---|---|---|
| **Bulk Email Verifier Pro** | **$0.005** | **No** (malformed/DNS-fail free) | ✅ | ✅ | ✅ |
| `michael.g/email-verifier-validator` (incumbent, 1,250+ users) | **$0.10** | varies | — | — | — |

We charge ~20× less per verified email, **and** we don't charge you for input we couldn't verify. That price transparency is the differentiator — the same deliverability screening for list-hygiene and B2B lead cleanup, at a fraction of the cost.

### Troubleshooting

- **A real company address shows `unknown`** — a transient DNS failure (resolver timeout). Re-run; DNS is generally stable and the cached-per-domain design keeps retries cheap.
- **`deliverable` but the address still bounces** — we verify the *domain* accepts mail, not the specific mailbox (we don't SMTP-probe by design). Use `role` / `freeProvider` / `disposable` to triage quality before sending.
- **An address you expected is missing from the output** — you set `skipDisposable: true` and it was a throwaway domain, or it was an exact duplicate of an earlier address (deduped). Set `skipDisposable: false` to see everything.
- **`didYouMean` is `null` for an obvious typo** — we only suggest against well-known domains to avoid proposing junk. For niche domain typos, no suggestion is returned.
- **Large lists at one company are slow / fast?** — DNS is cached per domain, so 5,000 addresses at one company is one lookup; raise `concurrency` toward 25 for big, diverse lists.

### Notes

- No API keys, no proxy, no residential IPs. The only network hop is standard DNS (UDP). Margin-positive from the first email.
- Disposable-domain list is a curated static set (not exhaustive) — we update it periodically; it intentionally errs toward the common throwaway providers.

# Actor input Schema

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

List of email addresses to verify. Accepts bare addresses ('a@b.com') or 'Display Name <a@b.com>' envelopes. One verdict row per valid address; malformed entries get an 'undeliverable' row (not charged).

## `skipDisposable` (type: `boolean`):

If true, disposable/throwaway addresses are dropped from the output entirely (no row). Default false — keep them with a 'risky' verdict and disposable=true so you can see what was filtered.

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

How many emails to process in parallel (1–25). DNS lookups are cached per domain, so a list of addresses at the same company costs one DNS query.

## Actor input object example

```json
{
  "emails": [
    "team@stripe.com",
    "notrealperson@realdomainthatdoesnotexist.com",
    "joe@mailinator.com"
  ],
  "skipDisposable": false,
  "concurrency": 10
}
```

# Actor output Schema

## `results` (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 = {
    "emails": [
        "team@stripe.com",
        "notrealperson@realdomainthatdoesnotexist.com",
        "joe@mailinator.com",
        "info@example.com",
        "support@anthropic.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ayeeyee/bulk-email-verifier-pro").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": [
        "team@stripe.com",
        "notrealperson@realdomainthatdoesnotexist.com",
        "joe@mailinator.com",
        "info@example.com",
        "support@anthropic.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ayeeyee/bulk-email-verifier-pro").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": [
    "team@stripe.com",
    "notrealperson@realdomainthatdoesnotexist.com",
    "joe@mailinator.com",
    "info@example.com",
    "support@anthropic.com"
  ]
}' |
apify call ayeeyee/bulk-email-verifier-pro --silent --output-dataset

```

## MCP server setup

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

```

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/ojz4aGhmwhGINtfMp/builds/zKM1qQTm4If2vNbHp/openapi.json
