# Email Verifier: Syntax, MX, Disposable & Role Check (`private_image_hg9/email-verifier-mx-check`) Actor

Check email lists without sending mail: syntax, domain and MX records (with A-record fallback and null MX), disposable domains, free providers, role accounts and typos like gmial.com, with one plain verdict per address. DNS only, from $0.30 per 1,000.

- **URL**: https://apify.com/private_image_hg9/email-verifier-mx-check.md
- **Developed by:** [Michael Shanker](https://apify.com/private_image_hg9) (community)
- **Categories:** Lead generation, Developer tools, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.30 / 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: Syntax, MX, Disposable & Role Check

Give it a list of email addresses and get back **one row per address** with a plain **verdict** and a short **reason**: is the address well formed, does the domain exist, does it have a **mail server (MX records)**, is it a **disposable** (throwaway) inbox, a **free provider** like Gmail or Outlook, a **role account** like info@ or noreply@, or a **likely typo** like `gmial.com` (with the fix suggested).

It works from **DNS only**. No email is sent and no mail server is ever contacted, which is why it's fast, cheap (**$0.30 per 1,000**), and never touches the recipient's mail server or puts your IP address on a blocklist.

### Who it's for

- **Sign-up forms and CRMs:** catch typos (`hotmial.com`), throwaway addresses and dead domains before they get into your list.
- **Sales and marketing ops:** clean a lead list before an email campaign, and split role inboxes (info@, sales@) from people's own addresses.
- **Developers:** a cheap first-pass filter before a paid mailbox (SMTP) check, run from the API, Zapier, Make or n8n.

### What it checks

| Check | How |
|---|---|
| **Syntax** | Practical RFC 5321/5322 rules: one @, an unquoted local part (letters, digits, `.!#$%&'*+/=?^_\`{|}~-`, no leading, trailing or double dots, 64 characters at most), a real internet domain name, 254 characters at most. Internationalised domains (`münchen.de`) work. Quoted local parts, comments and IP-address domains are reported as invalid because almost no real system accepts them. Reserved names (`.local`, `.internal`, `.test`, `localhost`) are refused. |
| **Domain exists** | A DNS lookup. "Does not exist" (NXDOMAIN) gives `domain_not_found`. |
| **MX records** | Sorted by priority. With no MX record, the domain's own A/AAAA address is used as an implicit mail server, as RFC 5321 allows (`mxSource: a_record_fallback`). A **null MX** (`0 .`, RFC 7505: "this domain takes no mail") and MX records pointing to `localhost`or private addresses give`no_mail_server`. |
| **Disposable** | The domain, or a parent of it, is on the bundled [disposable-email-domains](https://github.com/disposable-email-domains/disposable-email-domains) list (10,143 domains such as mailinator.com, yopmail.com, guerrillamail.com). |
| **Free provider** | About 160 consumer mailbox domains: Gmail, Outlook/Hotmail/Live, Yahoo, iCloud, AOL, Proton, GMX, Yandex, Mail.ru, QQ, 163 and big national webmail and ISP domains. |
| **Role account** | The part before the @ (ignoring a +tag) is a shared inbox: info, sales, support, admin, contact, hello, billing, hr, jobs, press, noreply/no-reply, postmaster, webmaster and about 90 more, including forms like `customer.service`and`support-team`. |
| **Likely typo** | One keystroke from a big provider (`gmial.com`, `yaho.com`, `hotmial.com`, `outlok.com`), a wrong ending (`gmail.con`, `hotmail.co`) or a missing dot (`gmailcom\`). Real look-alike providers (mail.com, email.com, ymail.com, gmx.com) are never "corrected". |

### Verdicts

| `verdict` | Meaning | Charged |
|---|---|---|
| `valid_domain` | The domain exists and has a mail server. The mailbox itself is not checked. | Yes |
| `likely_typo` | The domain answers in DNS but looks like a misspelt provider; see `typoSuggestion` and `suggestedEmail`. | Yes |
| `disposable` | A throwaway inbox service. | Yes |
| `no_mail_server` | The domain exists but takes no mail (null MX, no MX and no address, or an MX pointing nowhere). | Yes |
| `domain_not_found` | The domain doesn't exist. A typo hint is added to the reason when there is one. | Yes |
| `invalid_syntax` | The address is malformed; `reason` says how. No DNS lookup is made. | **No** |
| `unknown` | DNS didn't answer, even after a retry. Run the address again later. | **No** |

When more than one applies, the first in this order wins: `domain_not_found`, `no_mail_server`, `likely_typo`, `disposable`, `valid_domain`. The flags (`disposable`, `freeProvider`, `roleBased`, `typoSuggestion`) are always filled in on their own, so you can filter on them whatever the verdict.

### How it compares

| | This tool | A typical SMTP mailbox verifier |
|---|---|---|
| Syntax, domain and MX checks | Yes | Yes |
| Disposable, free-provider, role and typo flags | Yes, every row | Varies |
| Confirms the mailbox exists, detects catch-all domains | **No** | Yes, when the receiving server cooperates |
| Contacts the recipient's mail server | Never | Yes, on port 25 |
| Speed | Thousands of addresses a minute; each domain is looked up once per run | Slower: one server conversation per address, and some servers delay or block it |
| Price | $0.30 per 1,000, malformed and unknown rows free | Store prices we saw run from about $0.89 to $5 per 1,000 |

Use this tool to remove the obviously bad addresses cheaply; use a mailbox verifier afterwards if you need to know that a specific person's inbox exists.

### Input

| Field | What it does |
|---|---|
| **Email addresses** (`emails`) | One per line. `Jane Doe <jane@acme.com>`, `mailto:info@acme.com` and wrapping quotes are cleaned up. |
| **Or paste a list** (`emailsText`) | Addresses separated by new lines, commas or semicolons, e.g. a spreadsheet column. Added to the list above. |
| **DNS lookups at once** (`maxConcurrency`) | Domains looked up in parallel (default 20, max 100). |

Up to **100,000 different addresses** per run. Repeats are checked once: the domain part is compared without regard to capitals, the part before the @ as typed.

```json
{
  "emails": ["info@acme.com", "jane.doe@gmial.com", "test@mailinator.com"],
  "emailsText": "sales@example.org\nsupport@acme.com"
}
```

### Output

One row per address (values illustrative):

```json
{
  "input": "Jane Doe <jane.doe@gmail.com>",
  "email": "jane.doe@gmail.com",
  "localPart": "jane.doe",
  "domain": "gmail.com",
  "verdict": "valid_domain",
  "reason": "gmail.com accepts email: 5 MX records, first gmail-smtp-in.l.google.com. The mailbox itself is not checked.",
  "syntaxValid": true,
  "domainExists": true,
  "mailServerFound": true,
  "mxRecords": [
    {"priority": 5, "host": "gmail-smtp-in.l.google.com"},
    {"priority": 10, "host": "alt1.gmail-smtp-in.l.google.com"}
  ],
  "mxSource": "mx",
  "disposable": false,
  "freeProvider": true,
  "roleBased": false,
  "typoSuggestion": null,
  "suggestedEmail": null,
  "checkedAt": "2026-11-04T10:12:01+00:00"
}
```

`mxSource` is `mx` (real MX records), `a_record_fallback` (implicit MX from the A/AAAA record), `null_mx`, `mx_points_nowhere` or `none`, and empty when the domain wasn't found or DNS failed. `domainExists` and `mailServerFound` are empty (`null`) when they couldn't be checked.

Rows keep the order of your input. Download as JSON, CSV or Excel, or send results to Google Sheets, Zapier, Make or n8n. The run also saves a `SUMMARY` record with counts per verdict, the flags, how many addresses were charged, the DNS lookups made and whether the spending cap stopped the run.

### Pricing

Pay per event: **$0.0003 per address with a DNS-based verdict ($0.30 per 1,000)**, that is every `valid_domain`, `likely_typo`, `disposable`, `no_mail_server` and `domain_not_found` row. `invalid_syntax` and `unknown` rows are free. No start fee. Set a maximum charge per run in the run options and the Actor stops cleanly before going over it (no address is looked up that the cap couldn't pay for).

### Good to know

- **No mailbox check.** Without talking to the receiving mail server there's no way to know whether `jane@acme.com` exists, or whether `acme.com` accepts mail for any name (catch-all). A `valid_domain` verdict means mail to that domain has somewhere to go, not that this person's inbox exists. It's also why the tool is cheap, quick and can't harm your sending reputation.
- **DNS sources.** Lookups go to public resolvers (Cloudflare 1.1.1.1, Google 8.8.8.8, Quad9 9.9.9.9) with short timeouts; if plain DNS is blocked where the Actor runs, it switches once per run to the system resolver, then to DNS over HTTPS (Cloudflare, then Google). A timeout or server failure is retried once. Each domain is looked up once per run however many addresses share it.
- **MX hosts aren't looked up.** The tool checks that the domain names a mail server, not that the named server answers.
- **Disposable list.** Bundled from the [disposable-email-domains](https://github.com/disposable-email-domains/disposable-email-domains) project (file `disposable_email_blocklist.conf`, main branch, downloaded 2026-11-04, 10,143 domains), released under **CC0 1.0** (public domain). New throwaway services appear all the time, so a few will be missing; the list is not downloaded at run time. Some typo domains (like `gmial.com`) are on it too; those get `likely_typo` with the disposable flag set.
- **Typos** are only suggested towards about 40 big mailbox providers, never for company domains.
- **Role and free-provider lists** are heuristics for filtering, not judgments: a small business may well use a Gmail address.

### Responsible use

Use it on addresses you have a reason to hold, such as your own sign-ups, customers or leads, and follow the email and privacy laws that apply to you (for example GDPR, CAN-SPAM and CASL). The tool reads only public DNS records and makes no other network request.

This Actor isn't affiliated with or endorsed by any email provider, DNS provider or the disposable-email-domains project.

### More tools from the same developer

- [Website Contact Details Scraper: Emails, Phones & Socials](https://apify.com/private_image_hg9/website-contact-details-scraper)
- [Phone Number Validator: Format, Type, Country & Carrier](https://apify.com/private_image_hg9/phone-number-validator)
- [Company Job Boards Scraper: Greenhouse, Lever & Ashby](https://apify.com/private_image_hg9/company-job-boards-scraper)
- [Domain WHOIS, DNS & SSL Checker: Expiry, SPF & DMARC](https://apify.com/private_image_hg9/domain-whois-dns-ssl-checker)
- [Federal Contracts Search: SAM.gov Opportunities & Award History](https://apify.com/private_image_hg9/federal-contracts-search)

# Actor input Schema

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

One address per line. Pasted forms like Jane Doe <jane@acme.com> and mailto:info@acme.com are cleaned up. Each different address gets one row; repeats are checked once (the domain part is compared without regard to capitals). Up to 100,000 addresses per run.

## `emailsText` (type: `string`):

Addresses separated by new lines, commas or semicolons, e.g. a column copied from a spreadsheet. Added to the list above.

## `maxConcurrency` (type: `integer`):

How many domains are looked up in parallel. Each domain is looked up once per run however many addresses use it.

## Actor input object example

```json
{
  "emails": [
    "info@apify.com",
    "nonexistent.test.user@gmail.com",
    "someone@gmial.com",
    "test@mailinator.com",
    "user@example.com"
  ],
  "maxConcurrency": 20
}
```

# Actor output Schema

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

One row per address: verdict, reason, MX records and the disposable, free-provider, role and typo checks.

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

Counts per verdict, how many were charged, DNS lookups made, and whether the spending cap was reached.

# 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": [
        "info@apify.com",
        "nonexistent.test.user@gmail.com",
        "someone@gmial.com",
        "test@mailinator.com",
        "user@example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("private_image_hg9/email-verifier-mx-check").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": [
        "info@apify.com",
        "nonexistent.test.user@gmail.com",
        "someone@gmial.com",
        "test@mailinator.com",
        "user@example.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("private_image_hg9/email-verifier-mx-check").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": [
    "info@apify.com",
    "nonexistent.test.user@gmail.com",
    "someone@gmial.com",
    "test@mailinator.com",
    "user@example.com"
  ]
}' |
apify call private_image_hg9/email-verifier-mx-check --silent --output-dataset

```

## MCP server setup

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

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/O0KEfOjj11gHhU1Df/builds/Bo2j553JCyzm2F4sv/openapi.json
