# Email Validator & Domain Checker — MX, Disposable, Typos, RDAP (`reverend_sal/email-domain-validator`) Actor

Bulk-validate emails without SMTP probing: syntax, MX/DNS, disposable, role, free-provider and typo checks (gmial.com → gmail.com). Domain mode adds SPF, DMARC and RDAP registrar/creation/expiry dates. 10k emails in seconds; unknowns free.

- **URL**: https://apify.com/reverend_sal/email-domain-validator.md
- **Developed by:** [North Eng](https://apify.com/reverend_sal) (community)
- **Categories:** Lead generation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 email or domain checks

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 Validator & Domain Checker — MX, Disposable, Typos, RDAP

Clean a list of email addresses or check a list of domains in one fast run. For every address you get a clear
**status** (`valid`, `risky`, `invalid`, `disposable`, `unknown`), the **reason**, and a **"did you mean"** fix for
typos like `john@gmial.com` → `john@gmail.com`. For domains you also get SPF, DMARC, nameservers and registration
facts (registrar, creation and expiry dates) from the official RDAP registries.

**What it checks, in short:** syntax, the domain's MX / DNS mail setup, disposable (temporary) inbox services, role
accounts and common typos. **What it does not do:** it never connects to mail servers (no SMTP check), so it cannot
tell whether a specific mailbox exists. No personal data lookups — only public DNS, public registry data and
open-licensed lists.

### What it checks

| Check | Emails | Domains |
|---|---|---|
| Syntax (RFC 5321/5322 rules people actually hit, IDN / Unicode domains) | ✅ | ✅ |
| Domain exists, MX records, null MX (RFC 7505), mail servers that do not resolve | ✅ | ✅ |
| Disposable / temporary email services (≈10,000 domains, also matched on subdomains and on mail servers) | ✅ | ✅ |
| Role accounts (`info@`, `sales@`, `no-reply@` …) | ✅ | — |
| Free consumer providers (Gmail, Outlook, Yahoo, GMX, Proton …) and the mail host (Google Workspace, Microsoft 365, Proofpoint …) | ✅ | ✅ |
| Typo suggestions (`gmial.com`, `hotmail.co`, `yahoo.con`, `outlok.com` …) | ✅ | — |
| SPF record, DMARC policy, nameservers, IPv4 / IPv6 | optional | ✅ |
| Registration via RDAP: registrar, created / updated / expires, age in days, status, DNSSEC | — | ✅ (can be turned off) |

### Input

- **Emails** — one per line, or paste a CSV column. `Name <ann@example.com>` and `mailto:` links are fine.
- **Domains** — bare domains, URLs (`https://www.example.com/page`) or even email addresses; the domain is taken from each.
- **Registration data (RDAP)** — on by default for domains.
- **DNS details for emails** — adds SPF / DMARC / nameservers to each email row.

Up to 100,000 unique records per run. Duplicates (case-insensitive) and blank lines are removed before checking and are not charged.

### Output

One row per unique input. The **Overview** table view shows the key columns; the full row also has `mxRecords`,
`mailProvider`, `isValidSyntax`, `checkedAt` and, for domains, `spf`, `dmarc`, `dmarcPolicy`, `nameservers`, `ipv4`,
`ipv6` and `registration`.

| status | Meaning | Typical reasons |
|---|---|---|
| `valid` | Syntax is fine and the domain accepts mail | `accepts_mail` |
| `risky` | Deliverable domain, but think before mailing | `role_account`, `possible_typo`, `no_mx_record` (mail only via the A record), `quoted_local_part` |
| `invalid` | Mail to this address cannot be delivered | `domain_not_found`, `null_mx`, `no_mail_server`, `mx_unresolvable`, syntax reasons such as `invalid_dots`, `unknown_tld` |
| `disposable` | Temporary inbox service | `disposable_domain`, `disposable_mx` |
| `unknown` | DNS did not answer in time — **not charged**, re-run later | `dns_timeout`, `dns_error` |

Domain rows use `active`, `inactive`, `not_registered`, `invalid` and `unknown`.

A summary (counts by status, charged / not charged, speed) is saved as `OUTPUT` in the run's key-value store.

### Accuracy and speed

We tested the Actor on a small labeled set of 169 addresses that we built ourselves (good, broken, disposable, role
and typo cases); it classified 166 of them as expected. This is an internal sanity check, not an independent
benchmark, and it measures classification only — not whether mail actually gets delivered. The three misses were
forwarding services (33mail, Burner Mail, Spamgourmet) that the open disposable list does not include. On Apify with
the default 512 MB it checked 8,000 mixed addresses in 5.4 seconds (about 1,500 per second).

### Limits — read before you buy

- **No SMTP / mailbox check.** We never connect to mail servers to ask whether a mailbox exists. That practice is
  unreliable (catch-all servers, greylisting), blocked on cloud networks, and gets IP addresses blocklisted. So
  `valid` means "the domain is set up to receive mail", not "this exact mailbox exists", and catch-all domains are not
  detected.
- **RDAP fair use.** Registration lookups go to the official registries, at most 4 parallel requests per registry
  with spacing, cached per domain and slowed down when a registry asks. Registries' terms forbid using their data to
  support unsolicited bulk email; use it for list hygiene, fraud checks and research only. Only registrar facts are
  returned, never registrant contact data.
- Some registries (many country-code TLDs) have no RDAP service yet; those rows show `registration.errorCode: "NO_RDAP_SERVER"`.

### Pricing

Pay per record checked. Only final verdicts are charged:

| Event | Price |
|---|---|
| Email or domain check (`valid`, `risky`, `invalid`, `disposable`, or a domain verdict) | **$0.001** ($1 per 1,000) |
| Actor start | Apify default ($0.00005) |

**Free:** `unknown` rows (DNS did not answer), values that are not an email address at all, duplicates and blank lines.

Examples: 1,000 addresses cost **$1**, 10,000 cost **$10**. If you set a maximum cost per run, the Actor stops
cleanly when it is reached and keeps every result it already delivered. If a run is restarted or migrated, records
that were already charged are not charged again.

### FAQ

**Why is a Gmail address `valid` when the mailbox might not exist?** Without SMTP probing nobody can know that
reliably; we report what DNS proves. Combine with a double opt-in for signups.

**Why `risky` for `info@`?** Role addresses reach a team or nobody, and are a frequent spam-trap source. Keep them if
you know the company.

**Where do the lists come from?** The disposable list is the CC0 disposable-email-domains project; provider and
role lists are our own.

**Can I call it from code?** Yes — any Apify API client or the REST API; the dataset is ready as JSON, CSV or Excel.

# Actor input Schema

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

Email addresses to validate, one per line (up to 100,000 per run together with domains). Pasted lists separated by commas, semicolons or new lines are split; "Name <a@b.com>" and mailto: links are accepted. Duplicates are removed and never charged.

## `domains` (type: `array`):

Domains (or website URLs) to check: DNS (A/AAAA, NS, MX), mail provider, SPF, DMARC, and registration data from the registry's RDAP server (registrar, creation, expiry, status, DNSSEC).

## `includeRegistration` (type: `boolean`):

For entries in "Domains": look up registrar, creation/expiry dates, status and DNSSEC from the official registry RDAP server. Only non-personal registration facts are returned. Turn off for a faster DNS-only check.

## `includeDnsDetails` (type: `boolean`):

For entries in "Emails": also return the domain's SPF and DMARC records, DMARC policy, nameservers and IP addresses (looked up once per domain).

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

How many records are checked at the same time. 0 = automatic (200 for emails, 64 when domains are included). DNS answers are cached per domain, so large lists from a few providers are very fast.

## Actor input object example

```json
{
  "emails": [
    "info@apify.com",
    "john.doe@gmial.com",
    "test@mailinator.com",
    "someone@nonexistent-domain-zzqx.com"
  ],
  "includeRegistration": true,
  "includeDnsDetails": false,
  "maxConcurrency": 0
}
```

# Actor output Schema

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

One row per email or domain: status (valid, risky, invalid, disposable, unknown for emails; active, inactive, not_registered, invalid, unknown for domains), reason, flags and DNS/RDAP data.

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

Counts per status, charged and free rows, duplicates removed and speed.

# 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",
        "john.doe@gmial.com",
        "test@mailinator.com",
        "someone@nonexistent-domain-zzqx.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("reverend_sal/email-domain-validator").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",
        "john.doe@gmial.com",
        "test@mailinator.com",
        "someone@nonexistent-domain-zzqx.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("reverend_sal/email-domain-validator").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",
    "john.doe@gmial.com",
    "test@mailinator.com",
    "someone@nonexistent-domain-zzqx.com"
  ]
}' |
apify call reverend_sal/email-domain-validator --silent --output-dataset

```

## MCP server setup

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

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/R1iZUIFoJh20MVO5V/builds/UURQy345aT1I8xciq/openapi.json
