# Bulk Email Address Verifier (`automation-lab/bulk-email-address-verifier`) Actor

Validate email lists with syntax, normalized domains, MX records, disposable-domain and role-address signals, plus bounded diagnostics. No mailbox ownership or guaranteed-delivery claims.

- **URL**: https://apify.com/automation-lab/bulk-email-address-verifier.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (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 $0.38 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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 Address Verifier

Bulk Email Address Verifier performs **bulk email address verification** for CRM hygiene and lead-list qualification. Supply up to 5,000 addresses and receive one typed row per input with normalized address, syntax status, domain, MX status and records, a conservative disposable-domain signal, a role-address signal, and bounded diagnostics.

The Actor does not open a browser, send mail, contact a mailbox, prove mailbox ownership, or guarantee delivery. It reports address structure and public domain mail-routing evidence.

### What does Bulk Email Address Verifier do?

For each supplied value, the Actor:

1. Trims surrounding whitespace.
2. Validates a practical dot-atom email syntax.
3. Converts internationalized domain names to lowercase ASCII.
4. Resolves public MX records for syntactically valid domains.
5. Distinguishes found, missing, null-MX, skipped, and resolver-error outcomes.
6. Checks a bounded list of widely used disposable-email domains.
7. Detects common team inboxes such as support, billing, sales, and postmaster.
8. Emits one result per input, including malformed and duplicate values.

This makes it suitable for repeatable list review before CRM imports or outreach cycles.

### Who is it for?

- **Revenue operations teams** reviewing CRM exports before campaigns.
- **Lead generation teams** adding domain-level evidence to prospect lists.
- **Marketing operations teams** separating shared inboxes and malformed values.
- **Data engineers** normalizing address columns before warehouse ingestion.
- **Agencies** running the same hygiene check for recurring client lists.
- **Developers** integrating deterministic syntax and MX checks into workflows.

Choose this Actor when row-level evidence matters. It intentionally avoids SMTP-level mailbox claims.

### Why use this bulk email address checker?

- Process 1–5,000 inputs per run.
- Preserve duplicates so output can join back to source rows.
- Return invalid inputs instead of silently dropping them.
- Include ordered MX hostnames and priorities.
- Use stable diagnostic codes for filtering.
- Detect common functional inbox names conservatively.
- Avoid login, proxy, and browser dependencies.
- Configure resolver timeout, concurrency, or custom DNS servers.

### What email verification data is returned?

| Field | Meaning |
| --- | --- |
| `input` | Original supplied string |
| `normalizedEmail` | Trimmed address with normalized ASCII domain, or `null` |
| `syntaxStatus` | `valid` or `invalid` |
| `syntaxValid` | Boolean syntax result |
| `domain` | Parsed normalized domain |
| `mxStatus` | `found`, `missing`, `null_mx`, `skipped`, or `error` |
| `hasMx` | Whether usable MX records were found |
| `mxRecords` | Exchanges and priorities sorted deterministically |
| `isDisposable` | Match against the bounded known-disposable list |
| `isRoleAddress` | Match against a common functional local part |
| `roleType` | Role category, such as `support` or `billing` |
| `diagnostics` | At most five stable signal or failure codes |
| `errorCode` | Resolver code when MX resolution fails |
| `checkedAt` | ISO 8601 result timestamp |
| `durationMs` | Per-address processing duration |

Boolean disposable and role signals are `null` when syntax is invalid because no reliable domain/local part was parsed.

### How to run your first email list check

1. Open the Actor in Apify Console.
2. Paste addresses into **Email addresses**.
3. Keep the default DNS settings for a first run.
4. Click **Start**.
5. Open the **Dataset** tab.
6. Filter by `syntaxStatus`, `mxStatus`, `isDisposable`, or `isRoleAddress`.
7. Export JSON, CSV, Excel, XML, or RSS as needed.

Example input:

```json
{
  "emails": [
    "support@apify.com",
    "sales@github.com",
    "visitor@mailinator.com",
    "not-an-email"
  ],
  "concurrency": 25,
  "timeoutMs": 5000
}
```

### Input parameters

#### `emails`

Required array containing 1–5,000 strings. Each entry produces one charged result and dataset row. Duplicate values remain duplicates by design.

The supported syntax accepts common unquoted dot-atom local parts. Quoted local parts, comments, display-name wrappers, and address groups are not accepted. Supply plain addresses rather than strings such as `Person <person@domain.tld>`.

#### `concurrency`

Optional integer from 1 to 100. Default: 25. Lower it when a custom resolver has strict rate limits.

#### `timeoutMs`

Optional DNS attempt timeout from 500 to 30,000 milliseconds. Default: 5,000.

#### `dnsServers`

Optional list of recursive DNS server IPv4 or IPv6 addresses. When provided, the Actor uses those servers and does not switch to public fallback resolvers.

### Example output

A result has this shape:

```json
{
  "input": "support@apify.com",
  "normalizedEmail": "support@apify.com",
  "syntaxStatus": "valid",
  "syntaxValid": true,
  "domain": "apify.com",
  "mxStatus": "found",
  "hasMx": true,
  "mxRecords": [
    { "exchange": "aspmx.l.google.com", "priority": 1 }
  ],
  "isDisposable": false,
  "isRoleAddress": true,
  "roleType": "support",
  "diagnostics": ["role_address"],
  "errorCode": null,
  "checkedAt": "2026-08-26T12:00:00.000Z",
  "durationMs": 24
}
```

MX answers can change, so hostnames and priorities above are illustrative of the current output shape.

### Understanding statuses and diagnostics

`syntaxStatus: invalid` means deterministic parsing failed. In that case, `mxStatus` is `skipped` and no DNS query is made.

`mxStatus: found` means at least one usable MX exchange was returned. `missing` means no MX answer was found. `null_mx` means the domain explicitly states that it accepts no email. `error` means a resolver failure prevented a conclusion.

Possible diagnostics include:

- `empty_address`
- `address_too_long`
- `invalid_at_separator`
- `local_part_too_long`
- `invalid_local_part`
- `invalid_domain`
- `known_disposable_domain`
- `role_address`
- `mx_not_found`
- `domain_does_not_accept_email`
- `mx_lookup_failed`

Diagnostics are bounded to five values per row.

### How much does it cost to verify email addresses?

The Actor uses pay-per-event pricing:

- one `start` event per run;
- one `email` event for every emitted input result.

Current pricing is a **$0.0001 start fee** plus the per-email rate for your Apify plan:

| Apify plan | Price per email result |
| --- | ---: |
| Free | $0.000736 |
| Bronze | $0.00064 |
| Silver | $0.0004992 |
| Gold | $0.000384 |
| Platinum | $0.000256 |
| Diamond | $0.0001792 |

Calculate a Bronze run as the one-time start fee plus `input rows × Bronze email price`. Duplicate and malformed inputs still produce useful diagnostic rows and therefore trigger the `email` event.

### CRM hygiene workflow

Create an Apify Task with the addresses exported from your CRM, then schedule it before monthly or quarterly outreach reviews.

A practical workflow is:

1. Export the email column plus your internal row key.
2. Preserve source order when building the Actor input.
3. Run this Actor.
4. Join output rows back by position or input value.
5. Route invalid syntax and null-MX rows for correction.
6. Review missing-MX and resolver-error rows separately.
7. Treat disposable and role signals as review flags, not automatic rejection rules.
8. Store timestamped datasets for downstream comparison.

Apify schedules and webhooks provide orchestration. The Actor itself does not send alerts or retain cross-run history.

### Export and integrations

Results use the default Apify dataset and work with:

- Google Sheets for manual list review;
- Make and Zapier for conditional routing;
- BigQuery, Snowflake, and PostgreSQL for enrichment;
- webhooks for completed-run notifications;
- JavaScript and Python API clients;
- dataset exports in JSON, JSONL, CSV, Excel, XML, or RSS.

### Run with the Apify API

Keep `APIFY_TOKEN` in an environment variable.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~bulk-email-address-verifier/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"emails":["support@apify.com","not-an-email"]}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/bulk-email-address-verifier').call({
  emails: ['support@apify.com', 'sales@github.com', 'not-an-email'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/bulk-email-address-verifier').call(run_input={
    'emails': ['support@apify.com', 'sales@github.com', 'not-an-email'],
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Use with Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/bulk-email-address-verifier"
```

#### Claude Desktop, Cursor, and VS Code setup

Use this MCP configuration in Claude Desktop, Cursor, or VS Code:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/bulk-email-address-verifier"
    }
  }
}
```

Example prompts:

- “Check these CRM email addresses and summarize invalid syntax and missing MX rows.”
- “Run the bulk email verifier and return only disposable-domain or role-address signals.”
- “Export the verification dataset as CSV for a list-hygiene review.”

### Reliability and DNS behavior

The runtime resolver is used first. On transient resolver failures only, the Actor makes one bounded recovery attempt through public Cloudflare and Google resolvers unless custom servers were supplied.

Authoritative no-record answers are not retried as transient failures. Deterministic malformed inputs never consume a DNS request.

Public DNS changes over time and recursive caches can briefly differ. Use a custom resolver when consistency with your own infrastructure matters.

### Limitations

- Syntax validity does not prove a mailbox exists.
- An MX record does not guarantee that a recipient accepts mail.
- No SMTP handshake, message, or mailbox login occurs.
- Catch-all behavior is not tested.
- Disposable-domain coverage is intentionally bounded and can miss new services.
- Role detection uses exact common local parts and can miss organization-specific roles.
- Valid quoted local parts are outside the supported syntax subset.
- DNS outages can produce explicit `error` rows.
- The Actor does not create, find, or extract email addresses.

Do not label `syntaxValid` or `hasMx` as a guaranteed deliverability verdict.

### Legality and responsible use

Only process addresses you are authorized to handle. Apply applicable privacy, direct-marketing, anti-spam, employment, and data-retention rules. Minimize input, restrict dataset access, configure retention appropriately, and delete exports when no longer needed.

The Actor provides technical signals. It does not provide consent to contact a person and does not determine whether outreach is lawful or appropriate.

### Troubleshooting

#### Why is a syntactically valid address marked `missing`?

The domain returned no MX answer. Recheck the spelling and retry later if DNS was recently changed. The Actor does not assume an A record is equivalent to an advertised MX route.

#### Why is `mxStatus` equal to `error`?

Inspect `errorCode`. Increase `timeoutMs`, lower concurrency, or supply a reliable recursive resolver. Do not treat a transient error as proof that the domain cannot receive email.

#### Why was a valid-looking address rejected?

Provide a plain address without a display name. Quoted local parts and comments are outside the supported parser subset.

#### Why is a disposable service not flagged?

The embedded list is conservative rather than exhaustive. Use the signal as one review input, not a complete disposable-email database.

### Frequently asked questions

#### Does this Actor guarantee delivery?

No. It explicitly excludes guaranteed-delivery and mailbox-ownership claims.

#### Does it send verification emails?

No. It never sends a message or contacts the recipient mailbox.

#### Are duplicates removed?

No. One row is emitted per input so source-list cardinality remains available for downstream joins.

#### Can I check internationalized domains?

Yes. Valid Unicode domains are converted through IDNA to lowercase ASCII before MX lookup.

#### Can I schedule recurring checks?

Yes. Save the input as an Apify Task and add a schedule. Each run creates a new timestamped dataset.

#### Is a role address invalid?

No. A role address can be completely valid. The signal only identifies common functional inbox names for segmentation or review.

### Related Automation Lab Actors

- [Bulk DNS Records Lookup](https://apify.com/automation-lab/bulk-dns-lookup) for broader DNS record inventory.
- [MX Record Checker](https://apify.com/automation-lab/mx-record-checker) when only mail-exchanger discovery is needed.
- [Bulk DMARC Record Checker](https://apify.com/automation-lab/bulk-dmarc-record-checker) for domain email-authentication policy audits.
- [Website Email Extractor](https://apify.com/automation-lab/website-email-extractor) when you need to collect publicly displayed addresses before validation.

These Actors solve adjacent jobs. This Actor focuses on user-supplied address-list hygiene.

# Actor input Schema

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

One email address per entry. Results preserve input order-independent row cardinality, including duplicate inputs.

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

Number of addresses checked in parallel. Lower this when using a rate-limited custom resolver.

## `timeoutMs` (type: `integer`):

Timeout for each MX resolver attempt.

## `dnsServers` (type: `array`):

Optional IPv4 or IPv6 recursive resolver addresses. Leave empty to use the runtime resolver with bounded public-resolver recovery after transient failures.

## Actor input object example

```json
{
  "emails": [
    "support@apify.com",
    "sales@github.com",
    "not-an-email"
  ],
  "concurrency": 25,
  "timeoutMs": 5000
}
```

# Actor output Schema

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

Default dataset items displayed through the email verification overview.

# 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": [
        "support@apify.com",
        "sales@github.com",
        "not-an-email"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/bulk-email-address-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": [
        "support@apify.com",
        "sales@github.com",
        "not-an-email",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/bulk-email-address-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": [
    "support@apify.com",
    "sales@github.com",
    "not-an-email"
  ]
}' |
apify call automation-lab/bulk-email-address-verifier --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/bulk-email-address-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/HOFWpDG9HiExSodaK/builds/lPclSF0TDKrmMaljP/openapi.json
