# Bulk Email Verifier & Validator (No SMTP) (`uhqxx/email-verifier`) Actor

Bulk email verifier and validator for lists of up to 10,000 addresses. Checks format, domain, mail server (MX), disposable domains, role addresses and typos like gmial.com. Each address gets a clear verdict: ok, risky, invalid or unknown. No mail server is contacted, no address is guessed.

- **URL**: https://apify.com/uhqxx/email-verifier.md
- **Developed by:** [UHQxx](https://apify.com/uhqxx) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 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

## Bulk Email Verifier & Validator (No SMTP)

### What is Email Verifier?

**Email Verifier** checks a list of email addresses in seconds and tells you which ones are worth keeping. For each address it checks the **format**, whether the **domain exists**, whether it declares a **mail server (MX record)**, whether it is a **disposable email** domain, whether it is a **function address** such as `info@` and whether it is a likely **typo** of Gmail, Outlook or Orange.

Each address gets a clear verdict (`ok`, `risky`, `invalid` or `unknown`) with a plain-English reason, ready to export as JSON, CSV or Excel. You can verify up to 10,000 addresses per run.

**What makes it different:** it never contacts a mail server, never guesses or rebuilds addresses, and writes no email address in its logs. Only the domain part (after the @) is looked up.

### What can this email validator do?

- Check the format of each address (including international domain names).
- Check that the domain exists and can receive email (MX records, with the standard fallback and the "null MX" rule).
- Flag disposable (throw-away) email domains.
- Flag function addresses (`info@`, `contact@`, `support@`...) and free providers (Gmail, Outlook...).
- Suggest a fix for likely typos (`jane@gmial.com` becomes `jane@gmail.com`).
- Remove duplicates and skip the addresses in your exclusion list.
- Never mark an address `invalid` just because a lookup failed: those get `unknown`, and are not charged.

### Who is it for?

- Marketers and sales teams cleaning a list before an email campaign or a CRM import.
- Developers who want a simple domain-level check inside a sign-up or import workflow, through the Apify API.
- Anyone who prefers a checker that does not probe mail servers.

### 3 ways to use it

1. **Clean a list before sending.** Remove addresses with a wrong format or a domain that cannot receive email, and review the `risky` ones by hand.
2. **Fix typos at import.** An address such as `jane@gmial.com` is flagged with a suggestion (`jane@gmail.com`).
3. **Spot disposable sign-ups.** Flag addresses that use throw-away email domains.

### Example output

Real output from the Actor (fictional addresses, test domains):

```json
[
  {
    "email": "jane.doe@gmail.com",
    "verdict": "ok",
    "reason": "Valid format and the domain accepts email",
    "syntaxValid": true,
    "domain": "gmail.com",
    "domainAcceptsMail": true,
    "isDisposable": false,
    "isRoleBased": false,
    "isFreeProvider": true,
    "typoSuggestion": null
  },
  {
    "email": "jane@gmial.com",
    "verdict": "risky",
    "reason": "Possible typo in the domain. No mail server (MX record) declared, only a web address: delivery is uncertain",
    "syntaxValid": true,
    "domain": "gmial.com",
    "domainAcceptsMail": null,
    "isDisposable": null,
    "isRoleBased": false,
    "isFreeProvider": false,
    "typoSuggestion": "jane@gmail.com"
  },
  {
    "email": "bob@example.com",
    "verdict": "invalid",
    "reason": "Domain declares that it does not accept email",
    "syntaxValid": true,
    "domain": "example.com",
    "domainAcceptsMail": false,
    "isDisposable": null,
    "isRoleBased": false,
    "isFreeProvider": false,
    "typoSuggestion": null
  },
  {
    "email": "not-an-email",
    "verdict": "invalid",
    "reason": "Missing @",
    "syntaxValid": false,
    "domain": null,
    "domainAcceptsMail": null,
    "isDisposable": null,
    "isRoleBased": null,
    "isFreeProvider": null,
    "typoSuggestion": null
  }
]
```

### How to use it

1. Open the Actor on Apify and paste your addresses in **Email addresses** (one per line; commas, semicolons and spaces also work). Duplicates are checked once.
2. Optional: fill the **Exclusion list** with addresses or domains that must not be processed (see the GDPR section).
3. Click **Start**, then open the **Results** table or export it as JSON, CSV or Excel. A short run summary (totals only) is saved in the `SUMMARY` record of the key-value store.

You can also run it from your own tools through the [Apify API](https://docs.apify.com/api/v2).

#### Input

| Field | Meaning | Default |
|---|---|---|
| `emails` | Addresses to check (required). | none |
| `excludedEmails` | Addresses (`jane@example.com`) or domains (`example.com` or `@example.com`) that must NOT be processed. They are skipped, not saved and not counted. | empty |
| `maxEmails` | Safety limit per run, from 1 to 10,000. Addresses beyond it are not checked (a warning is logged). | 1,000 |
| `dnsTimeoutSeconds` | Time to wait for each domain lookup in the first pass, from 1 to 30. Domains that are slow to answer get one more, more patient attempt (up to 30 seconds). | 10 |

#### Output columns

| Column | Meaning |
|---|---|
| `email` | The address, trimmed and in lower case. |
| `verdict` | `ok`, `risky`, `invalid` or `unknown` (see below). |
| `reason` | Why, in plain English. |
| `syntaxValid` | The address has a valid format. |
| `domain` | The part after the @. |
| `domainAcceptsMail` | `true` if the domain declares a mail server, `false` if it does not exist or cannot receive email, `null` if undetermined. |
| `isDisposable` | The domain belongs to a disposable-email service (`null` if not evaluated). |
| `isRoleBased` | Function address such as `info@` or `contact@` (no named person). |
| `isFreeProvider` | Consumer mailbox provider such as Gmail or Outlook. |
| `typoSuggestion` | Corrected address if the domain looks like a typo of a popular one. |

#### What the verdicts mean

- **ok**: valid format, and the domain declares a mail server.
- **risky**: worth a manual look: disposable domain, likely typo, or no mail server declared (only a web address).
- **invalid**: wrong format, the domain does not exist, or the domain says it does not accept email.
- **unknown**: the lookup could not be completed (timeout or DNS problem). It is never reported as `invalid`. Run those addresses again later.

### FAQ

#### Does it check that a mailbox really exists?

No. It does not talk to the recipient's mail server (no SMTP test), so it cannot know whether `jane@company.com` exists. `ok` means the format is valid and the domain can receive email. This is the price of not probing mail servers, which also avoids getting your own IP address flagged.

#### Why not just test the mailbox with SMTP?

Many mail servers accept every address, block or throttle these tests, or flag the sender. A checker that never contacts mail servers is simpler, faster and more predictable. If you need mailbox-level checks, use a tool that does SMTP probing, and keep this one for the fast domain-level clean-up.

#### Is it safe for personal data?

The addresses you submit are personal data when they identify a person. The Actor sends only the domain part to DNS lookups, never the part before the @, calls no third-party API and writes no address in its logs. You remain responsible for your use: see the GDPR section below.

#### What does `unknown` mean, and am I charged for it?

It means the check could not be completed (for example a slow or broken DNS server). It is never reported as `invalid`, and it is never charged. Run those addresses again later.

#### How many addresses can I check in one run?

Up to 10,000 (the `maxEmails` setting, 1,000 by default). A list of 10,000 addresses on about 550 different domains took 43 seconds in our own test on the Apify platform. Speed depends mostly on the number of different domains.

#### Can I use it through the API or in my workflow?

Yes. Like any Apify Actor, it can be started through the [Apify API](https://docs.apify.com/api/v2), on a schedule, or from integrations such as Make, Zapier or n8n.

### Honest limits

- **It cannot tell whether a mailbox exists.** There is no SMTP test (no conversation with the recipient's mail server). `ok` means "format valid and the domain can receive email", not "this person's mailbox exists" or "delivery is guaranteed".
- Addresses with non-ASCII characters before the @ (for example `josé@...`) are rejected as invalid.
- The disposable-domain list comes from the open-source package [mailchecker](https://github.com/FGRibreau/mailchecker) (MIT licence). No list is complete, and it also contains some look-alike domains (typos of big providers).
- The lists of free providers, function addresses and popular domains (for typo suggestions) are short and hand-written. A typo is only suggested when the domain is one character away from a popular domain, so some typos are missed, and a real domain one character away from a popular one can be flagged as `risky`.
- Speed depends on how many different domains your list contains: each domain is looked up once. A few domains answer very slowly, and a list with many unreachable domains takes longer (the extra patient attempts are capped at about 10 minutes per run). Addresses that still cannot be checked get the verdict `unknown` and are never charged.
- **Run time limit.** If the run timeout you set is about to be reached, the Actor stops early instead of being cut off: the addresses already checked are kept, the others are not checked and not charged, and the status message says so (the `SUMMARY` record has the totals). Start a new run for the rest, or raise the run timeout.

### Data and GDPR: please read

**Data processed.** The addresses you provide, which are personal data when they identify a person. The output contains the same address plus fields derived from it. Nothing else is collected: no names, phone numbers, profiles or sensitive data.

**What leaves the Actor.** Only the domain part of an address (after the @) is sent in DNS queries, to the DNS resolvers of the platform. The part before the @ is never sent anywhere. No third-party API is used. Logs contain totals only, never addresses.

**What is kept.** The Actor keeps nothing of its own. Your results and your run input (including any exclusion list) are stored by Apify with your run, according to Apify's own retention rules. You can delete them from the Console.

**You are responsible for your use.** You decide why and how the addresses are used, so you are responsible for compliance. In particular:

- Have a valid legal basis. For individuals, prior consent. For professionals, the CNIL (the French data protection authority) accepts legitimate interest when the message relates to the person's profession, with a simple and free right to object.
- Inform the people concerned and honour objections and other requests, within one month.
- Make sure your list was obtained lawfully.
- Do not use this Actor to guess or rebuild addresses. This is a separate processing that the CNIL strictly regulates.
- Use the **Exclusion list** for people who objected, so they are never processed again.

This is general information, not legal advice. CNIL sources (in French): [email prospecting](https://www.cnil.fr/fr/la-prospection-commerciale-par-courrier-electronique-sms-mms-et-automate-dappel), [rebuilding professional addresses](https://www.cnil.fr/fr/reconstitution-adresses-professionnelles-quel-cadre), [recommendations for reusing data published online](https://www.cnil.fr/fr/recommandations-reutilisateurs-donnees-internet).

### En bref (français)

Vérifiez une liste d'adresses e-mail : format, domaine existant, serveur de messagerie déclaré, domaine jetable, faute de frappe probable. Chaque adresse reçoit un verdict (`ok`, `risky`, `invalid`, `unknown`) avec la raison. L'Actor ne contacte aucun serveur de messagerie, ne devine aucune adresse et n'écrit aucune adresse dans ses logs. **Limite principale : il ne peut pas savoir si la boîte existe réellement.** Vous restez responsable de l'usage de vos listes (base légale, information des personnes, droit d'opposition, réponse sous un mois) : voir la section « Data and GDPR » et les liens CNIL ci-dessus. Une **liste d'exclusion** permet d'écarter les personnes qui se sont opposées.

### Pricing

You pay per address checked. The price is shown in the **Pricing** tab of the Actor.

- **Charged:** each address that was actually checked and got a verdict of `ok`, `risky` or `invalid`.
- **Never charged:** addresses with the verdict `unknown` (the check could not be completed), duplicates, addresses in your exclusion list, and addresses beyond your `maxEmails` limit.
- A small fixed fee per run start may also apply, as shown in the Pricing tab.
- You can cap what a run can spend with the maximum cost setting of the run. The Actor stops cleanly when the cap is reached; results already saved are kept.

# Actor input Schema

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

The addresses to check. One per line. Commas, semicolons and spaces also work as separators. Duplicates are checked once.

## `excludedEmails` (type: `array`):

Addresses or domains that must NOT be processed, for example people who objected to being contacted. Write a full address (jane@example.com) or a domain (example.com or @example.com). Excluded addresses are skipped, not saved, and not counted in the results.

## `maxEmails` (type: `integer`):

Safety limit for one run. Addresses beyond this number are not checked.

## `dnsTimeoutSeconds` (type: `integer`):

How long to wait for each domain lookup in the first pass. Domains that do not answer in time get one more, more patient attempt (up to 30 seconds). If they still do not answer, the verdict is "unknown" (not charged), never "invalid".

## Actor input object example

```json
{
  "emails": [
    "contact@example.com",
    "jane@gmial.com",
    "someone@this-domain-does-not-exist.invalid",
    "not-an-email"
  ],
  "excludedEmails": [],
  "maxEmails": 1000,
  "dnsTimeoutSeconds": 10
}
```

# Actor output Schema

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

No description

# 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": [
        "contact@example.com",
        "jane@gmial.com",
        "someone@this-domain-does-not-exist.invalid",
        "not-an-email"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("uhqxx/email-verifier").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": [
        "contact@example.com",
        "jane@gmial.com",
        "someone@this-domain-does-not-exist.invalid",
        "not-an-email",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("uhqxx/email-verifier").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": [
    "contact@example.com",
    "jane@gmial.com",
    "someone@this-domain-does-not-exist.invalid",
    "not-an-email"
  ]
}' |
apify call uhqxx/email-verifier --silent --output-dataset

```

## MCP server setup

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

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/1lbAur06rlre4DvYX/builds/B26o1iMagMZjMIu6Q/openapi.json
