# Bulk Email Checker - Syntax, MX, Disposable & Typo Check (`skillmerc/bulk-email-checker`) Actor

Clean email lists fast: syntax, domain and MX checks, disposable and role account detection, free provider flag and typo fixes like gmial.com to gmail.com. $0.30 per 1,000. No mailbox pinging.

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

## Pricing

from $0.21 / 1,000 checked emails

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?

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

## Bulk Email Checker: Syntax, MX, Disposable and Typo Check

Clean an email list before you send to it. For every address this actor checks the format, whether the domain exists and can receive mail (MX records), whether it is a disposable or temporary address, whether it is a role account such as info@ or sales@, and whether it is a free provider such as Gmail. It also catches common typos and suggests the fix, for example `jane@gmial.com` to `jane@gmail.com`.

Each email gets one clear status, a recommended action and the reasons behind it.

**Price: $0.30 per 1,000 checked emails**, plus $0.0005 per run. Duplicates are removed first and are not charged.

### What it checks

| Check | How |
|---|---|
| Format | Practical address rules: one @, allowed characters, no leading, trailing or double dots, length limits, valid domain labels and TLD. International domains are supported. |
| Domain exists | DNS lookup. Domains that do not exist are marked invalid. |
| Can receive mail | MX records. A domain with no MX but an A record is flagged as risky (mail may still arrive through the A record). A domain that publishes a "null MX" (it explicitly refuses mail, like example.com) is invalid. |
| Disposable | Matched against the community maintained [disposable-email-domains](https://github.com/disposable-email-domains/disposable-email-domains) list (about 9,200 domains, public domain CC0), including subdomains of listed domains. |
| Role account | info@, sales@, support@, admin@, noreply@, hr@ and about 80 more shared inbox names. |
| Free provider | Gmail, Yahoo, Outlook, Hotmail, iCloud, AOL, Proton, GMX, Yandex, Mail.ru and others. Shown as a flag only. |
| Typo | Close misspellings of popular providers (gmial.com, yahooo.com, hotmial.com) and wrong endings (gmail.con). Returns `suggested_email`. |

Each domain is looked up once per run, so lists with many addresses on the same domains run very fast.

### What it does not do

**It does not ping mailboxes.** There is no SMTP check, so it cannot tell you whether a specific mailbox like `john@company.com` exists, and it does not detect catch-all domains. A `valid_format_and_domain` result means the address is well formed and its domain is set up to receive mail. If you need mailbox-level verification, run this first to remove the obvious bad addresses cheaply, then send only the survivors to a mailbox verifier.

### Statuses

| status | recommendation | Meaning |
|---|---|---|
| `valid_format_and_domain` | `send` | Well formed, domain exists and has mail servers, not disposable, not a role account, no typo. |
| `risky` | `review` or `remove` | Deliverable domain but something to look at: role account, possible typo, no MX record (A record only), or a DNS lookup that failed twice. Disposable addresses are `risky` with `remove`. |
| `invalid` | `remove` | Bad format, domain does not exist, domain refuses mail, or no mail server at all. |

### Input

Use any mix of these:

```json
{
  "emails": ["john.doe@gmail.com", "jane@gmial.com", "Sales Team <sales@acme.com>"],
  "fileUrl": "https://example.com/leads.csv",
  "datasetId": "aBcD1234",
  "datasetField": "email",
  "maxEmails": 0
}
```

- `emails`: one per line, or several separated by commas or semicolons.
- `fileUrl`: a CSV or text file. A column named `email` is used if present, otherwise every email-like string in the file is checked. A Google Sheets "publish to web as CSV" link works.
- `datasetId`: the output of another Apify actor, such as a lead or contact scraper. `datasetField` names the field that holds the email (arrays of emails are fine).

### Output

One row per unique email:

```json
{
  "email": "jane@gmial.com",
  "input": "jane@gmial.com",
  "status": "risky",
  "recommendation": "remove",
  "reasons": ["disposable_domain", "possible_typo", "no_mx_record_uses_a_record"],
  "is_valid_syntax": true,
  "domain": "gmial.com",
  "domain_exists": true,
  "has_mx": false,
  "mail_server_fallback": true,
  "is_disposable": true,
  "is_role_account": false,
  "is_free_provider": false,
  "suggested_email": "jane@gmail.com",
  "mx_records": [],
  "mailbox_checked": false
}
```

Possible `reasons`: `missing_at_sign`, `multiple_at_signs`, `missing_local_part`, `missing_domain`, `contains_whitespace`, `too_long`, `local_part_too_long`, `bad_dots_in_local_part`, `invalid_characters_in_local_part`, `invalid_domain`, `domain_has_no_tld`, `invalid_tld`, `ip_literal_domain_not_supported`, `domain_not_found`, `domain_does_not_accept_mail`, `no_mail_server`, `no_mx_record_uses_a_record`, `disposable_domain`, `role_account`, `possible_typo`, `dns_lookup_failed_<code>`.

A run summary (counts per status, duplicates removed, domains looked up, duration) is saved as `OUTPUT` in the key-value store.

### Common uses

- Clean a lead list before a cold email campaign to cut bounces and protect your sender reputation.
- Check sign-up emails from a form or CRM export and block disposable addresses.
- Fix typos in customer emails before sending receipts or onboarding mail.
- Cheap first pass before a paid mailbox verifier, so you only pay that verifier for addresses worth checking.
- Validate the output of contact or lead scrapers straight from their Apify dataset.

### Pricing

Pay per event, platform usage included:

- $0.0003 per checked email ($0.30 per 1,000)
- $0.0005 per run at the default 512 MB memory (Apify charges the start event once per GB, minimum one)

Duplicates are removed before checking and are not charged. You can cap spend with the run's maximum total charge setting; the actor stops cleanly when it is reached.

### API and integrations

Call it from the Apify API, the JavaScript or Python client, Make, Zapier, n8n or any MCP client like other Apify actors, and chain it after a scraper with an Apify integration so new leads are checked automatically.

# Actor input Schema

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

One email per line. Lines may also hold several emails separated by commas or semicolons. Formats like "Name <name@example.com>" and mailto: links are accepted.

## `fileUrl` (type: `string`):

Public URL of a CSV or plain text file (for example a Google Sheets CSV export link). If the CSV has a column named email, e-mail, email address or mail, that column is used. Otherwise every email-like string in the file is checked.

## `datasetId` (type: `string`):

Check emails stored in an Apify dataset, for example the output of a lead or contact scraper.

## `datasetField` (type: `string`):

Name of the field that holds the email in each dataset item. Arrays of emails are supported.

## `maxEmails` (type: `integer`):

Stop after this many unique emails. 0 means no limit.

## `includeMxRecords` (type: `boolean`):

Add up to five mail server hostnames for each domain to the output.

## `dnsConcurrency` (type: `integer`):

How many domains to look up at the same time. Each domain is looked up once per run.

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

Optional resolver IPs, for example 1.1.1.1 and 8.8.8.8. Leave empty to use the default resolver.

## Actor input object example

```json
{
  "emails": [
    "john.doe@gmail.com",
    "jane@gmial.com",
    "test@mailinator.com",
    "info@stripe.com",
    "nobody@thisdomaindoesnotexist-zz9.com",
    "plainaddress"
  ],
  "datasetField": "email",
  "maxEmails": 0,
  "includeMxRecords": true,
  "dnsConcurrency": 50,
  "dnsServers": []
}
```

# 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 = {
    "emails": [
        "john.doe@gmail.com",
        "jane@gmial.com",
        "test@mailinator.com",
        "info@stripe.com",
        "nobody@thisdomaindoesnotexist-zz9.com",
        "plainaddress"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("skillmerc/bulk-email-checker").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": [
        "john.doe@gmail.com",
        "jane@gmial.com",
        "test@mailinator.com",
        "info@stripe.com",
        "nobody@thisdomaindoesnotexist-zz9.com",
        "plainaddress",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("skillmerc/bulk-email-checker").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": [
    "john.doe@gmail.com",
    "jane@gmial.com",
    "test@mailinator.com",
    "info@stripe.com",
    "nobody@thisdomaindoesnotexist-zz9.com",
    "plainaddress"
  ]
}' |
apify call skillmerc/bulk-email-checker --silent --output-dataset

```

## MCP server setup

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

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/2UgiapwJlfT2P2dVj/builds/dMKbYnDRrQSbz8jud/openapi.json
