# Email Validation API - Verify and Append Names (`nabeelbaghoor/email-validation-api`) Actor

Validate email addresses in bulk with syntax, MX and SMTP checks, get the verdict plus the reason code and a 0-100 score, and append the first name, last name, gender and birth year sitting on each address. Flags catch-all, disposable, free, role and no-reply. Pay per result.

- **URL**: https://apify.com/nabeelbaghoor/email-validation-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 71.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$8.00 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Email Validation API - Verify and Append Names

Clean a mailing list and learn something about it at the same time. Every address comes back with a verdict, the reason behind it, a quality score, and the first name, last name, gender and birth year the provider holds on that address. One flat row per address.

### What this actor does

- **Validates and enriches in the same pass.** Most list cleaning tells you only whether to send. This provider returns the identity on the address alongside the verdict, so a hygiene run doubles as an enrichment run at no extra cost: first name, last name, full name, gender and birth year, plus the mailbox part and any plus-tag on the address.
- **Returns the reason, not just the verdict.** Every row carries one of five verdicts and one of eleven reason codes underneath it. That is what separates a receiving server that timed out and will verify perfectly on a retry from a mailbox that genuinely does not exist. Both are offered as filters.
- **Suggests a fix for a typo.** When an address looks like a misspelling of a real domain, the provider returns what it thinks was meant. That is the difference between throwing away a signup and recovering it, since a person who typed one wrong character in a common webmail domain is still a real subscriber.
- **Scores quality from zero to one hundred.** The verdict alone is blunt. The score lets you keep the stronger risky addresses and drop the weak ones rather than losing a whole verdict class.
- **Flags catch-all, disposable, free, role, no-reply and full mailboxes.** Each is a separate column and a separate filter, so a list can be shaped for B2B outreach, for transactional mail or for a newsletter without three different runs.
- **Batches without losing the results.** The provider accepts up to fifty thousand addresses in one batch, but above one thousand it stops returning results in the response and offers a download file instead, which cannot be read without leaving the API. Batches are therefore capped at one thousand, which keeps every result inline. Results are matched back to your input by address rather than by position, so a collapsed duplicate never shifts the rest onto the wrong rows.
- **Waits properly rather than guessing.** A batch still running answers with a non-final status, which the polling loop watches for. A partially finished batch is never read as a finished one.
- **Keeps the key out of every log line.** This provider accepts the key only as a request parameter, never as a header, so the actor makes sure the real request URL is never written to a log.
- **Never charges for a miss.** Only rows carrying a resolved verdict are billed.

### Input

| Field | What it does |
| --- | --- |
| `emails` | Addresses to validate. |
| `states` | Keep only these verdicts. |
| `reasons` | Keep only these reason codes. |
| `excludeFreeEmail` | Drop free consumer mailboxes. |
| `excludeRoleAccounts` | Drop shared inboxes such as info and support. |
| `excludeDisposable` | Drop throwaway addresses. |
| `excludeAcceptAll` | Drop addresses on catch-all domains. |
| `excludeNoReply` | Drop unattended sender addresses. |
| `requireMxRecord` | Keep only domains that can receive mail at all. |
| `requireName` | Keep only addresses with a person's name attached. |
| `minScore` | Keep only addresses at or above this quality score. |
| `smtpCheck` | Probe the receiving mail server. On by default. |
| `acceptAllCheck` | Additionally test whether the domain accepts everything. |
| `retryUnknown` | Let the provider re-check undecided addresses. |
| `forceSingle` | Send every address as its own request. |
| `timeout` | Seconds allowed per address, on the single address route. |
| `batchSize` | Addresses per batch, up to one thousand. |
| `requestsPerMinute` | Pace the run under your plan's rate limit. |
| `skipNotFound` | Leave addresses with no verdict out of the dataset. |
| `maxResults` | Hard cap on rows, and therefore on spend and run time. |
| `apiKey` | Your own API key. Stored as a secret. |

### Example output

```json
{
  "found": true,
  "mode": "validate",
  "query": "jane.doe@example.com",
  "email": "jane.doe@example.com",
  "state": "deliverable",
  "reason": "accepted_email",
  "score": 100,
  "domain": "example.com",
  "user": "jane.doe",
  "tag": null,
  "acceptAll": false,
  "disposable": false,
  "free": false,
  "role": false,
  "noReply": false,
  "mailboxFull": false,
  "mxRecord": true,
  "smtpProvider": "google",
  "didYouMean": null,
  "firstName": "Jane",
  "lastName": "Doe",
  "fullName": "Jane Doe",
  "gender": "female",
  "birthYear": 1987,
  "durationSeconds": 0.42,
  "error": null,
  "raw": { }
}
```

### Frequently asked questions

#### What does an email validation API actually check?

It works in layers. The address is checked for correct syntax, the domain is looked up for mail exchanger records, and then the receiving mail server is contacted over SMTP and asked whether that specific mailbox exists. On top of that the provider applies its own knowledge of disposable providers, role addresses, no-reply senders and catch-all configurations. Each row returns the verdict of that whole process plus the reason code that decided it and a zero to one hundred quality score.

#### What is the difference between the verdict and the reason code?

The verdict is one of deliverable, undeliverable, risky, unknown or duplicate. The reason code is the detail underneath it, and it is where the useful decisions live. Two addresses can both come back as unknown when one hit a mail server that timed out and will verify fine on a retry, while the other could not be connected to at all. Filtering on the reason code lets you retry the first group and drop the second, instead of discarding both.

#### Does it really return the person's name?

Yes, where the provider holds it. Alongside the deliverability answer you get first name, last name, full name, gender and birth year for the address, plus the mailbox part and any plus-tag. That means a list clean is also an enrichment pass, which matters when the alternative is running the same list through two services and paying twice. Use the require a name filter when enrichment rather than hygiene is the point of the run.

#### What is a catch-all domain and why does it matter?

A catch-all domain is configured to accept mail for every possible mailbox, whether or not it exists. An SMTP check against any address there comes back positive and tells you nothing about the individual mailbox. The provider flags these, and the verdict comes back as risky rather than deliverable. Turning on the catch-all test makes the run slower per address and is worth it whenever you need to know whether a deliverable verdict actually means anything.

#### How large can a batch be, and why is it capped at one thousand?

The provider itself accepts up to fifty thousand addresses in one batch. Above one thousand, though, it stops putting the results in the response and instead offers a download file, which an actor cannot read without leaving the API entirely. So batches here are capped at one thousand, which keeps every result inline and costs nothing but one more request per additional thousand addresses.

#### When does it use the single address route instead of batching?

Whenever a per address timeout is set, because only the single address route honours one, and for very short lists where waiting for a batch to start and finish costs more than just asking. You can also force it with the always use the single address route option, which is worth doing when you want rows arriving as the run goes rather than in blocks after each batch completes.

#### Should I turn off the SMTP check?

Only if speed matters more than the answer. With it off the run does syntax and domain level checks and nothing else, which is fast and much less conclusive: it can tell you a domain is dead but not that a mailbox is. It is on by default for that reason. Leave the automatic retry on as well, since a greylisting receiving server very often answers on the second attempt and an unknown that could have been a deliverable is a wasted credit.

#### Do I need my own API key?

Yes. This actor does not include data access. You use your own API key from Emailable, which is the provider whose email validation API this actor calls, created on their dashboard. Your own plan, credit balance and terms apply. Paste the key into the `apiKey` field, where it is stored as an Apify secret. This provider accepts the key only as a request parameter and not as a header, so the actor makes sure the real request URL never reaches a log line. Note that the provider also issues test keys, and a test key on a live route is rejected the same way an invalid key is.

#### What happens when an address returns nothing?

An undeliverable address is a verdict, not a miss: the provider telling you a mailbox does not exist is exactly the question you asked, so that row is written and billed like any other. A miss is an address the provider returned no verdict for at all, and those are written with `found: false` and never billed. Set `skipNotFound` to true to leave them out of the dataset entirely.

#### How much does a run cost?

Pricing is pay per result: you are charged for each address resolved into the dataset, and never for misses, for rows dropped by the filters, or for duplicates. Apify platform usage is included in the per-result price. Your own API provider's credit balance is separate and billed by them.

### Keyword map

email validation API, email verification API, bulk email validator, email list cleaning, SMTP verification, MX record check, catch-all detection, disposable email detection, role account detection, no-reply detection, email typo correction, did you mean suggestion, email quality score, name append from email, gender append, demographic append, bounce rate reduction, sender reputation, list hygiene, signup form validation, B2B list cleaning

# Actor input Schema

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

Email addresses to validate, one per line. An address pasted inside a longer string, such as a mail header or a name and address pair, is extracted before the request, and duplicates are removed so you are never charged twice for the same address.

## `states` (type: `array`):

Only keep addresses with these verdicts. Restricting to deliverable is how you turn a raw list into one that is safe to send to. Leave empty to keep every verdict. Applied after the provider answers, so it reduces dataset noise and Apify spend rather than provider credits.

## `reasons` (type: `array`):

Only keep addresses whose reason code is one of these. The reason code is the detail under the verdict, and it is what separates a receiving server that timed out and will verify on a retry from a mailbox that genuinely does not exist. Leave empty to keep every reason.

## `excludeFreeEmail` (type: `boolean`):

Leave out addresses on free consumer providers such as the large webmail services, keeping only company addresses. Useful when the list is meant for B2B outreach.

## `excludeRoleAccounts` (type: `boolean`):

Leave out shared inboxes such as info, sales and support, keeping only addresses that belong to an individual.

## `excludeDisposable` (type: `boolean`):

Leave out addresses on throwaway providers, which are created to expire and are worth nothing in a list.

## `excludeAcceptAll` (type: `boolean`):

Leave out addresses on domains that accept everything sent to them. A catch-all domain answers yes to every mailbox, so a positive result there says nothing about the individual address.

## `excludeNoReply` (type: `boolean`):

Leave out addresses the provider identifies as unattended senders, which are technically deliverable and never read by anyone.

## `requireMxRecord` (type: `boolean`):

Only keep addresses whose domain publishes a mail exchanger record, which means the domain can receive mail at all. A quick way to drop parked and abandoned domains.

## `requireName` (type: `boolean`):

Only keep addresses the provider was able to attach a person's name to. Useful when the point of the run is enrichment rather than hygiene.

## `minScore` (type: `integer`):

Only keep addresses scoring at least this on the provider's zero to one hundred scale. A useful middle ground when the verdict alone is too blunt: it keeps the stronger risky addresses and drops the weak ones. Leave at zero to keep every score.

## `smtpCheck` (type: `boolean`):

Probe the receiving mail server over SMTP to confirm the mailbox itself exists. On by default, and turning it off makes a run much faster and much less conclusive, since only syntax and domain level checks then run.

## `acceptAllCheck` (type: `boolean`):

Additionally test whether the domain accepts every address sent to it. Off by default because it costs extra time per address, and worth turning on when you need to know whether a deliverable verdict means anything.

## `retryUnknown` (type: `boolean`):

Let the provider automatically re-check addresses it could not decide on the first attempt. On by default, and worth leaving on: a greylisting receiving server very often answers on the second try.

## `forceSingle` (type: `boolean`):

Send every address as its own request instead of batching. Slower and far more requests, but the results arrive as the run goes rather than after each batch finishes. The actor already uses this route automatically for very short lists and whenever a per address timeout is set.

## `timeout` (type: `integer`):

How long the provider may spend on one address before answering unknown, between two and ten seconds. Only the single address route honours this, so setting it turns batching off for the whole run.

## `batchSize` (type: `integer`):

How many addresses to submit in one batch. The ceiling here is one thousand rather than the provider's own fifty thousand, because above one thousand it stops returning results in the response and offers a download file instead, which cannot be read without leaving the API.

## `requestsPerMinute` (type: `integer`):

Pace the run so it stays under whatever rate your plan allows. Lower this if the provider starts returning rate limit responses; raise it to finish a long list sooner.

## `skipNotFound` (type: `boolean`):

Leave addresses the provider returned no verdict for out of the dataset. Note that an undeliverable address is a verdict, not a miss, so this does not drop undeliverable rows. Off by default. Misses are never charged for either way.

## `maxResults` (type: `integer`):

Stop after this many rows. This is the cap on both spend and run time.

## `apiKey` (type: `string`):

Your own API key for the email validation API. This provider accepts the key only as a request parameter, so the actor keeps it out of every log line instead. Required for every lookup. Stored as a secret.

## Actor input object example

```json
{
  "emails": [
    "jane.doe@example.com"
  ],
  "states": [],
  "reasons": [],
  "excludeFreeEmail": false,
  "excludeRoleAccounts": false,
  "excludeDisposable": false,
  "excludeAcceptAll": false,
  "excludeNoReply": false,
  "requireMxRecord": false,
  "requireName": false,
  "minScore": 0,
  "smtpCheck": true,
  "acceptAllCheck": false,
  "retryUnknown": true,
  "forceSingle": false,
  "batchSize": 1000,
  "requestsPerMinute": 300,
  "skipNotFound": false,
  "maxResults": 100
}
```

# Actor output Schema

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

One row per address, with the verdict, the reason code behind it, a quality score, the domain and account flags and the identity the provider holds on the address.

# 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": [
        "jane.doe@example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/email-validation-api").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": ["jane.doe@example.com"] }

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/email-validation-api").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": [
    "jane.doe@example.com"
  ]
}' |
apify call nabeelbaghoor/email-validation-api --silent --output-dataset

```

## MCP server setup

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

```

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/bi8ONEeARLfmZ3k9F/builds/pvnknhbo1SiUd38cl/openapi.json
