# Bulk Email Validator & Verifier (`bite-sized-insights/validator-email`) Actor

Validate email lists in bulk. Checks RFC syntax, MX records, disposable domains and role accounts, flags free providers, suggests typo fixes, and returns a deliverable-likely verdict per address with a reason, so you can clean lists before sending.

- **URL**: https://apify.com/bite-sized-insights/validator-email.md
- **Developed by:** [bite-sized-insights](https://apify.com/bite-sized-insights) (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.70 / 1,000 apify-default-dataset-items

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

***Validate and verify email addresses in bulk - RFC syntax, MX/domain existence, disposable-email detection, and role-account flagging, with one composite "deliverable likely" verdict per address.***

Simply provide a list of email addresses and get back a structured verdict for each one: is it syntactically valid, does its domain have a mail server, is it a throwaway/disposable address, and is it a role account (info@, admin@, support@, etc.)?

No login required. No cookies needed. Pay-per-event pricing.

### Why Choose This Actor?

**"Clean your list before you send - in seconds, not hours"**

Our Bulk Email Validator & Verifier provides:

- **Four Independent Checks Per Address**: RFC syntax + normalization, MX-record domain existence, disposable/temporary-domain detection, and role-account flagging - not just a single pass/fail
- **Very Fast Responses**: Concurrent DNS lookups (up to 20 in parallel) for large batches
- **Cost-Effective**: Pay only for the emails you validate: `$0.70 / 1k emails`
- **No Third-Party API Dependency**: Runs entirely on open, direct checks (an RFC validator, a DNS MX lookup, and a maintained disposable-domain list) - no upstream service to fail or rate-limit you

### Quick Start

```json
{
  "emails": ["user@example.com", "invalid-email", "test@mailinator.com"]
}
```

### Usage Examples

#### Validate a Single Address

```json
{
  "emails": ["jane.doe@gmail.com"]
}
```

#### Validate a Batch

```json
{
  "emails": [
    "jane.doe@gmail.com",
    "not-an-email",
    "temp123@mailinator.com",
    "support@acme.com",
    "user@this-domain-does-not-exist-anywhere.zzz"
  ]
}
```

### Output Example

```json
{
  "email": "jane.doe@gmail.com",
  "normalized_email": "jane.doe@gmail.com",
  "syntax_valid": true,
  "domain_has_mx": true,
  "is_disposable": false,
  "is_role_account": false,
  "is_free_provider": true,
  "did_you_mean": null,
  "deliverable_likely": true,
  "reason": null
}
```

```json
{
  "email": "temp123@mailinator.com",
  "normalized_email": "temp123@mailinator.com",
  "syntax_valid": true,
  "domain_has_mx": true,
  "is_disposable": true,
  "is_role_account": false,
  "is_free_provider": false,
  "did_you_mean": null,
  "deliverable_likely": false,
  "reason": "disposable/temporary email domain"
}
```

```json
{
  "email": "not-an-email",
  "normalized_email": null,
  "syntax_valid": false,
  "domain_has_mx": false,
  "is_disposable": false,
  "is_role_account": false,
  "is_free_provider": false,
  "did_you_mean": null,
  "deliverable_likely": false,
  "reason": "An email address must have an @-sign."
}
```

### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `emails` | array | Yes | List of email addresses to validate/verify (1 to 100,000 addresses, up to 254 characters each; surrounding whitespace is trimmed) |

### Output Fields

| Field | Type | Description |
|-------|------|--------------|
| `email` | string | The email address exactly as provided in the input |
| `normalized_email` | null/string | RFC-normalized form (lowercased domain), or null if syntax is invalid |
| `syntax_valid` | boolean | Whether the address is RFC-compliant |
| `domain_has_mx` | boolean | Whether the domain has a resolvable MX record (i.e. can receive mail) |
| `is_disposable` | boolean | **Disposable-email flag**: whether the domain is a known disposable/temporary-email provider (feeds `deliverable_likely`) |
| `is_role_account` | boolean | Whether the local part looks like a role/functional account (info@, admin@, support@, etc.) |
| `is_free_provider` | boolean | Whether the domain is a free mailbox provider (Gmail, Yahoo, Outlook, iCloud, GMX, etc.); bonus signal, not part of deliverable_likely |
| `did_you_mean` | null/string | Suggested corrected address if the domain looks like a typo of a popular provider (e.g. john@gmial.com -> john@gmail.com), otherwise null |
| `deliverable_likely` | boolean | Composite verdict: syntax valid **and** domain has MX **and** not disposable |
| `reason` | null/string | Short explanation when `deliverable_likely` is false, otherwise null |

`is_role_account` is a bonus signal only, not part of `deliverable_likely` - role accounts (e.g. `support@company.com`) are frequently perfectly deliverable, they're just worth flagging separately if your use case treats personal and role addresses differently (e.g. cold outreach vs. transactional mail).

### Pricing

| Cost | Description |
|------|-------------|
| $0.0001 / start | Charged per actor run |
| $0.70 / 1,000 emails | Charged per email address validated |

### API Integration

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/bite-sized-insights/validator-email/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "emails": ["jane.doe@gmail.com", "temp123@mailinator.com"]
  }'
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("bite-sized-insights/validator-email").call(
    run_input={
        "emails": ["jane.doe@gmail.com", "temp123@mailinator.com"]
    }
)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"{item['email']}: deliverable_likely={item['deliverable_likely']} ({item['reason']})")
```

#### JavaScript

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

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });

const run = await client.actor('bite-sized-insights/validator-email').call({
    emails: ['jane.doe@gmail.com', 'temp123@mailinator.com'],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach(item => {
    console.log(`${item.email}: deliverable_likely=${item.deliverable_likely} (${item.reason})`);
});
```

### Use Cases

- **List Hygiene Before a Campaign**: Strip invalid, disposable, and undeliverable addresses from a mailing list before you pay to send to it
- **Signup-Form Validation**: Batch-check addresses collected through a form, trial signup, or lead-gen funnel
- **CRM/Lead Data Enrichment**: Add a deliverability verdict to imported or purchased contact lists
- **Fraud/Abuse Signals**: Flag disposable-domain signups as a low-cost first-pass fraud signal
- **Role-Account Segmentation**: Separate personal addresses from role accounts (info@, support@, etc.) for outreach that should only target a named individual

### FAQ

#### Does this actually send a test email or do an SMTP handshake?

No. This actor checks syntax, domain existence (does the domain have a mail server, via its MX record), and known disposable-domain membership - it does not perform an SMTP `RCPT TO` handshake against the destination mail server. Many mail providers (Gmail, Outlook, etc.) silently accept the SMTP conversation regardless of whether the mailbox exists and only bounce later, which makes real-time SMTP verification unreliable and easy to get your sending IP flagged for - the checks here are the same non-intrusive signals most bulk list-hygiene tools lead with, without that risk.

#### What counts as a "disposable" domain?

Domains from well-known temporary/throwaway email services (Mailinator, Guerrilla Mail, 10 Minute Mail, and thousands of similar providers), sourced from the actively-maintained, MIT-licensed [`disposable-email-domains`](https://github.com/disposable-email-domains/disposable-email-domains) list. It is refreshed periodically, not in real time - a brand-new disposable domain may not be caught immediately.

#### Why is `is_role_account` not part of `deliverable_likely`?

Role accounts like `support@company.com` or `sales@company.com` are usually monitored mailboxes that receive mail perfectly well - flagging them as "undeliverable" would be wrong. It's surfaced as a separate signal so you can decide for yourself whether your use case (e.g. person-to-person outreach) should treat them differently.

#### What do `is_free_provider` and `did_you_mean` add?

`is_free_provider` flags addresses at free mailbox providers (Gmail, Yahoo, Outlook/Hotmail, iCloud, GMX and similar), handy for telling private from business addresses. `did_you_mean` suggests a corrected full address when the domain looks like a typo of a popular provider (`john@gmial.com` -> `john@gmail.com`, also `.con` / `.cm` slips), otherwise it is null. Both are bonus signals and never change `deliverable_likely`; the `is_disposable` flag is the one that does.

#### What if a domain has no MX record but the address still works?

A small number of domains rely on an implicit mail-exchanger fallback (an A/AAAA record with no explicit MX record) rather than a standards-recommended MX record. This actor checks MX records specifically, matching the check most competitor bulk-validator actors lead with; such domains would show `domain_has_mx: false` even though mail may still be deliverable in practice.

#### Is my email list stored or shared anywhere?

No. Input is processed for the duration of the run only and results are written to your own private run dataset, same as any other Apify actor.

### Comparison

Bulk email verification is a proven, high-volume Apify Store category - the current category leader (per public Store stats) sits at **1,638 users and 304,300 runs**, with several independent competitors each still drawing 90-106 users. This actor covers the same core checks (syntax, MX/domain existence, disposable-domain detection) those competitors are built around, at a comparable or lower per-result price, with no external API dependency to introduce latency, rate limits, or an extra point of failure.

### Disclaimer

This actor checks syntax, domain existence, and known disposable-domain membership - it does not perform an SMTP mailbox-existence check and cannot guarantee an address will accept mail (many providers accept and then silently drop, or only reveal a bad mailbox after final delivery). Treat `deliverable_likely` as a strong pre-send filter, not a delivery guarantee.

***

**Need help?** Open an issue on the actor's repository or contact support through Apify Console.

# Actor input Schema

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

List of email addresses to validate/verify.

## Actor input object example

```json
{
  "emails": [
    "user@example.com",
    "invalid-email",
    "test@mailinator.com"
  ]
}
```

# Actor output Schema

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

Per-email validation verdicts: syntax, MX/domain existence, disposable-domain, role-account and free-provider flags, a typo suggestion, and a composite deliverable_likely verdict

# 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": [
        "user@example.com",
        "invalid-email",
        "test@mailinator.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("bite-sized-insights/validator-email").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": [
        "user@example.com",
        "invalid-email",
        "test@mailinator.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("bite-sized-insights/validator-email").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": [
    "user@example.com",
    "invalid-email",
    "test@mailinator.com"
  ]
}' |
apify call bite-sized-insights/validator-email --silent --output-dataset

```

## MCP server setup

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

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/lWhRpftS5fquhljkk/builds/bwC93t2zE8rbBgDrY/openapi.json
