# Email Validator: Syntax, MX, Disposable & Role Check (`ailu_studio/email-validator`) Actor

Bulk email validation without SMTP probing: syntax, MX records via DNS-over-HTTPS, disposable domains, role accounts, free providers, typo suggestions and Gmail normalisation. One row per email.

- **URL**: https://apify.com/ailu_studio/email-validator.md
- **Developed by:** [Ailu Studio](https://apify.com/ailu_studio) (community)
- **Categories:** Lead generation, Marketing, 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 $0.50 / 1,000 email checkeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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: Bulk Email Syntax, MX Record, Disposable, Role Account and Typo Check (No SMTP)

Clean an email list in bulk before you import it into a CRM or send a campaign. For every address the Actor checks the syntax, looks up the domain's MX records over DNS-over-HTTPS (Cloudflare, with Google Public DNS as fallback), flags disposable domains (about 120,000 known throwaway domains), role accounts (info@, admin@, noreply@ ...), free consumer providers (Gmail, Outlook, Yahoo, iCloud, Proton, GMX, Yandex, Mail.ru, QQ, Naver ...) and likely typos such as `hotmial.com` with a corrected `didYouMean` address. It also returns a Gmail-normalised address for de-duplication. The Actor never connects to mail servers and never sends anything, so it is fast, cheap and cannot hurt your sender reputation; rows whose DNS lookup failed are free.

### Use cases

- Remove invalid, disposable and mistyped addresses from sign-up forms and lead lists.
- Separate personal addresses from generic role mailboxes before outreach.
- Split B2B leads from free-mailbox (consumer) leads.
- De-duplicate Gmail addresses written with dots or +tags (`J.Doe+promo@gmail.com` = `jdoe@gmail.com`).
- Pre-filter a list cheaply before a paid SMTP verification service.

### Input

| Field | Default | Meaning |
|---|---|---|
| `emails` | 5 sample addresses (role, Gmail with dots and +tag, disposable, typo, invalid) | Addresses to check, one per line. Case-insensitive duplicates are checked once. |
| `maxConcurrency` | `10` | Addresses checked in parallel. |
| `maxItems` | `1000` | Maximum number of addresses checked in this run. |

Example: `{ "emails": ["sales@acme.com", "john@gmial.com", "x@mailinator.com"], "maxItems": 5000 }`

### Output

| Field | Meaning |
|---|---|
| `id` | First 16 hex characters of SHA-1 of the lower-cased address |
| `email`, `normalizedEmail` | Input and normalised form (lower case; Gmail dots and +tag removed) |
| `status` | `valid`, `risky`, `invalid` or `unknown` |
| `reason` | Why, e.g. `role account`, `domain does not exist`, `syntax: consecutive dots in local part` |
| `syntaxValid`, `domain` | Syntax verdict and lower-case domain |
| `hasMx`, `mxRecords`, `hasImplicitMx` | MX records (up to 5, preferred first); A/AAAA fallback |
| `isDisposable`, `isRoleAccount`, `isFreeProvider` | Flags |
| `didYouMean` | Suggested correction for provider typos |
| `catchAll` | Always `null` (needs SMTP, see below) |
| `fetchedAt` | Time of the check |

Status rules: **invalid** = bad syntax, non-existent domain (NXDOMAIN), null MX or neither MX nor A record. **risky** = deliverable domain but disposable, role account or likely typo. **valid** = deliverable domain and none of these. **unknown** = DNS failed on both resolvers (free row).

Sample row:

```json
{
  "id": "4428a0907a9e828e", "email": "J.Doe+newsletter@Gmail.com", "normalizedEmail": "jdoe@gmail.com",
  "status": "valid", "reason": "MX records found", "syntaxValid": true, "domain": "gmail.com", "hasMx": true,
  "mxRecords": ["gmail-smtp-in.l.google.com", "alt1.gmail-smtp-in.l.google.com", "alt2.gmail-smtp-in.l.google.com", "alt3.gmail-smtp-in.l.google.com", "alt4.gmail-smtp-in.l.google.com"],
  "hasImplicitMx": false, "isDisposable": false, "isRoleAccount": false, "isFreeProvider": true, "didYouMean": null, "catchAll": null,
  "fetchedAt": "2026-11-04T11:37:20.485Z"
}
```

### How it works and limits

- No SMTP: port 25 is blocked on Apify and probing mailboxes is abusive, so the Actor cannot tell whether a specific mailbox exists or whether a domain is catch-all (`catchAll` is always `null`). A `valid` address can still bounce if the mailbox was deleted.
- DNS: `MX` query via `cloudflare-dns.com`, fallback `dns.google` on HTTP errors or SERVFAIL; when there is no MX, `A`/`AAAA` is checked (implicit MX). A single `0 .` record (RFC 7505 null MX) means the domain accepts no mail. Answers are cached per domain within a run; at most 10 queries per second per resolver.
- Syntax covers the common unquoted form (letters, digits and `!#$%&'*+/=?^_{|}~-`, Unicode letters allowed). Quoted local parts and IP-literal domains are reported as invalid.
- Disposable detection uses the open-source `disposable-email-domains` npm list (MIT); new throwaway domains appear every day, so coverage is never complete.
- Typo suggestions only target well-known consumer providers.

### Pricing

Pay per result: **$0.50 per 1,000 rows** (`result` event). Platform usage is billed separately by Apify. Removed/free rows are not charged.

Rows with status `unknown` (DNS failure) are free.

### Legal

Not affiliated with Cloudflare, Google or any email provider. Uses the public Cloudflare DNS-over-HTTPS and Google Public DNS JSON APIs only. Data licence/terms: https://developers.cloudflare.com/1.1.1.1/privacy/public-dns-resolver/ and https://developers.google.com/speed/public-dns/terms. Only the domain part of each address is sent to the DNS resolvers. You are responsible for having a lawful basis to process the addresses you check (GDPR, CAN-SPAM).

### FAQ

**Is "valid" a guarantee of delivery?** No. It means the address is well-formed and its domain accepts mail. Only an SMTP check (not done here) or a real send can confirm a mailbox.

**Why is support@company.com "risky"?** Role mailboxes are shared and often filtered; many outreach tools treat them as risky. Use `isRoleAccount` to decide yourself.

**Why do I get both `didYouMean` and status `risky` for a domain with MX records?** Typo domains such as `hotmial.com` often have MX records operated by third parties; mail sent there does not reach the intended person.

**How fast is it?** Hundreds of addresses per minute; lists with many addresses on the same domains are faster thanks to the DNS cache.

# Actor input Schema

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

Addresses to validate, one per line. Leading and trailing spaces are trimmed; case-insensitive duplicates are checked once.

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

How many addresses are checked at the same time. DNS answers are cached per domain, so lists with many addresses on the same domain are fast.

## `maxItems` (type: `integer`):

Upper limit on addresses checked in this run (the first N unique addresses of the list). Rows whose DNS lookup failed (status unknown) are free.

## Actor input object example

```json
{
  "emails": [
    "support@apify.com",
    "J.Doe+newsletter@Gmail.com",
    "temp.user@mailinator.com",
    "jane.smith@hotmial.com",
    "not-an-email@"
  ],
  "maxConcurrency": 10,
  "maxItems": 1000
}
```

# Actor output Schema

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

All rows in the default dataset.

# 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",
        "J.Doe+newsletter@Gmail.com",
        "temp.user@mailinator.com",
        "jane.smith@hotmial.com",
        "not-an-email@"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ailu_studio/email-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": [
        "support@apify.com",
        "J.Doe+newsletter@Gmail.com",
        "temp.user@mailinator.com",
        "jane.smith@hotmial.com",
        "not-an-email@",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ailu_studio/email-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": [
    "support@apify.com",
    "J.Doe+newsletter@Gmail.com",
    "temp.user@mailinator.com",
    "jane.smith@hotmial.com",
    "not-an-email@"
  ]
}' |
apify call ailu_studio/email-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ailu_studio/email-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/OXqYVdFYW2HlAmFqY/builds/DaH5iuQvhoTf85RXH/openapi.json
