# Email & Phone Verifier (`lergassy/email-phone-verifier`) Actor

Check e-mail addresses and phone numbers in bulk. E-mails: syntax, live MX lookup, throwaway and role-account flags, mail host, typo suggestions. Phones: numbering-plan validation, type, US state and time zone. Feed it a list or the dataset of another Actor.

- **URL**: https://apify.com/lergassy/email-phone-verifier.md
- **Developed by:** [Matvey](https://apify.com/lergassy) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 contact checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 & Phone Verifier** checks a list of e-mail addresses and phone numbers and tells you which ones you can actually use. Every address is checked for syntax and then looked up against its domain's mail servers, so you learn whether that domain accepts mail at all. Every number is validated against the North American numbering plan and tagged with its type, the US state of its area code and the time zone to call it in. Each row comes back with a verdict, the reason behind it, and a 0–100 contact score you can sort an outreach list by.

It can also work the other way round: give it a **company name** and it finds the company's website and any address published on it, so a lead with no contact stops being a dead row.

It takes a plain list, a set of contact objects, or the **dataset of another Actor** — so a lead list you scraped five minutes ago can be cleaned without moving data by hand. No login, no API key, no third-party service: the checks are DNS lookups plus open reference data, which is why they cost almost nothing and leak nothing.

### What is Email & Phone Verifier?

It is a **bulk email verification** and **phone number validation** tool for people who send cold e-mail, run dialers, or buy lead lists and suspect half of it is junk. Instead of a single yes/no it returns the evidence: whether the domain exists, whether it publishes mail servers, who runs that mail, whether the address is a role account like `info@`, whether the mailbox is a free consumer one, whether the domain is a known throwaway service, and whether a number is even dialable.

### What it checks

| Check | What you get | Why it matters |
|---|---|---|
| **Syntax** | Bad addresses rejected before anything else | Typos and pasted junk never reach your sending tool |
| **Domain exists** | `domain_not_found` when the domain does not resolve | The single most common cause of hard bounces |
| **Mail servers (MX)** | `deliverable` when the domain accepts mail, `domain_accepts_no_mail` when it refuses | A domain that publishes no mail server can never receive your message |
| **Throwaway domains** | `emailIsDisposable`, from a list of 8,700 services | Sign-up spam and burner addresses out of your CRM |
| **Role accounts** | `emailIsRole` for `info@`, `sales@`, `billing@` … | Deliverable, but nobody's personal inbox — different campaign, different expectations |
| **Free mailboxes** | `emailIsFree` for Gmail, Yahoo, Outlook … | A business contact on a free mailbox is usually a sole trader |
| **Mail host** | `emailProvider`: google, microsoft, zoho, proofpoint … | Tells you what filtering your message will face |
| **Typos** | `emailSuggestion` when the domain is one or two letters off a big provider | `gmial.com` becomes a recovered contact instead of a bounce |
| **Numbering plan** | `valid` / `invalid` with the rule that failed | Impossible numbers never reach a dialer |
| **Number type** | geographic, toll-free, premium, non-geographic, international | A toll-free number is a switchboard, not a person |
| **Location** | `phoneState`, `phoneTimezone` from the area code | Call inside business hours instead of at 6 a.m. |
| **Formats** | `phoneE164` and a readable form | Ready for a CRM field or a dialer, whatever it expects |
| **Website discovery** | `website`, `websiteStatus` from the company name | A lead with no contact becomes a lead with a site |
| **Address discovery** | `foundEmails`, `foundEmailSource` | Reads the address the company publishes itself |

### What it does not do, and why

This is the part most tools in this category are quiet about, so here it is plainly.

**We do not knock on the mailbox.** Confirming that one specific mailbox exists needs an SMTP conversation on port 25, and cloud platforms — Apify included — block outbound port 25 entirely. We measured it rather than assumed it: a lookup of the mail servers answers in about seven milliseconds, and a connection to those same servers on port 25 times out. Any serverless tool promising per-mailbox certainty is either routing through a private relay it does not mention, or guessing.

**And even where port 25 is open, the answer lies.** Gmail and Microsoft 365 accept any recipient at that stage and bounce later, so a "verified" verdict from that method is worth little on exactly the domains most of your list sits on.

So `deliverable` here means *the domain accepts mail and the address is well formed* — the check that removes the large majority of hard bounces. It does not mean a person is behind it. That distinction is why the reason and the flags are on every row: you decide what to send where.

**Line type is missing on purpose too.** Whether a US number is a mobile or a landline cannot be determined from free data — the authoritative databases are licensed commercially, and number portability makes the underlying block data wrong anyway. We would rather leave the field out than invent it.

### Turning a company name into a contact

Registry and directory data is full of companies with no e-mail at all. Switch on
**🔎 Find the company website and e-mail** and every row that carries a company name
gets a second pass:

1. Candidate domains are built from the name, with the legal suffixes stripped:
   `Acme Holdings LLC` becomes `acme.com`, `acme-holdings.com`, `acme.io` and so on.
2. Each candidate is resolved, and the home page is fetched.
3. **The name has to be on the page.** This is the step that separates a real find
   from a plausible-looking wrong company, and most tools skip it.
4. The home, contact and about pages are read for a published address, preferring one
   on the company's own domain.

`websiteStatus` tells you how much to trust the answer. **verified** means two or more
distinctive words of the company name appear on the page. **probable** means the name
is a single word — a real match, but a namesake is possible, so check before you send.
Single-purpose entities that never have a site, such as property vehicles and film
companies, come back as **skipped\_no\_website\_expected** without a single request being
made, which keeps the run cheap.

On real registry names this finds a site for roughly four companies in ten, and an
address on about four of every ten sites found. Billing follows the result: a search
that finds nothing costs nothing.

### How much does it cost to verify a list?

Pricing is **pay per event**: you are charged per contact checked, and nothing else. There is no browser and no proxy in this Actor — a DNS lookup and some in-memory lists — so platform usage is negligible and a list of ten thousand contacts finishes in well under a minute. See the **Pricing** tab for the current rate.

### How to verify a list

1. Paste your addresses into **📧 E-mail addresses**, your numbers into **📱 Phone numbers**, or both.
2. If one person has both, use **👥 Contacts** and pass objects — any extra fields you include, such as your own record id, are copied to the output row.
3. To clean a list you just scraped, paste that run's dataset id into **🔗 Check a dataset from another Actor** and name the fields to read.
4. Pick **🎯 Keep only** if you want the run to return just the usable rows.
5. Click **Start**, then export as CSV, JSON or Excel, or read the dataset through the API.

### ⬇️ Input

![Email and phone verifier input: paste a list of e-mail addresses and phone numbers](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/email-phone-verifier/email-phone-verifier-input-form.png)

```json
{
  "emails": ["owner@acme.com", "info@gmail.com", "hello@mailinator.com"],
  "phones": ["+1 206 235 0243", "(212) 211-5678"],
  "keep": "all"
}
```

#### Contacts with both channels

```json
{
  "contacts": [
    { "email": "jane@acme.com", "phone": "+1 415 555 2671", "crmId": "A-1042" },
    { "email": "carlos@ejemplo.mx", "phone": "+52 55 1234 5678" }
  ]
}
```

`crmId` is not a field this Actor knows — it is copied through untouched, so the output lines up with your own records.

#### Cleaning another Actor's output

```json
{
  "inputDatasetId": "aBcD1234efGh5678",
  "emailField": "email, contactEmail",
  "phoneField": "phone, contactPhone",
  "keep": "usable"
}
```

Point it at the dataset of a lead scraper — for example our [US New Business Leads](https://apify.com/lergassy/us-business-filings) — and it reads the contacts out of that run directly.

### ⬆️ Output

One row per contact.

![Bulk email verification output: deliverable, risky and undeliverable verdicts with the reason, mail host and role flag](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/email-phone-verifier/email-phone-verifier-output-table.png)

```json
{
  "type": "contact",
  "email": "info@gmail.com",
  "emailStatus": "deliverable",
  "emailDomain": "gmail.com",
  "emailProvider": "google",
  "emailIsRole": true,
  "emailIsFree": true,
  "emailIsDisposable": false,
  "emailSuggestion": null,
  "phone": "+1 206 235 0243",
  "phoneStatus": "valid",
  "phoneE164": "+12062350243",
  "phoneFormatted": "(206) 235-0243",
  "phoneType": "geographic",
  "phoneCountry": "US",
  "phoneAreaCode": "206",
  "phoneState": "WA",
  "phoneTimezone": "America/Los_Angeles",
  "contactScore": 75,
  "checkedAt": "2026-09-03T11:59:00.000Z"
}
```

![Phone number validation output: verdict, number type, US state and time zone](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/email-phone-verifier/email-phone-verifier-phone-table.png)

#### A row that failed

```json
{
  "email": "sales@acmeholdings-llc.com",
  "emailStatus": "undeliverable",
  "emailReason": "domain_not_found",
  "phone": "(212) 211-5678",
  "phoneStatus": "invalid",
  "phoneReason": "reserved_exchange",
  "contactScore": 0
}
```

`reserved_exchange` means the exchange code can never be assigned to a subscriber — 211 is a service code, like 911. Fourteen such codes exist, and they are checked for you.

### Use cases

#### Cold outreach without burning the domain

Bounces above a few percent damage sender reputation for everything you send afterwards. Dropping `undeliverable` rows and treating `risky` ones carefully is the cheapest protection there is.

#### Cleaning bought or scraped lists

Lists sold by the thousand are full of dead domains and throwaway sign-ups. Run the list first, and the `emailReason` column tells you exactly what you paid for.

#### Dialers and call centres

`phoneStatus` keeps impossible numbers out of the queue, `phoneType` separates switchboards from direct lines, and `phoneTimezone` stops calls landing before breakfast.

#### Sorting a lead list

`contactScore` ranks contacts by how reachable they are: a deliverable company address beats a Gmail one, which beats a role account, which beats a throwaway.

#### Sign-up and form validation

Reject disposable domains and obvious typos at the point of entry, and suggest the correction with `emailSuggestion`.

### Integrations

Run it from the [Apify API](https://docs.apify.com/api/v2), the JavaScript and Python clients, a schedule, or a webhook when another run finishes. It plugs into **n8n**, **Make** and **Zapier** through the Apify connectors, and results can be pushed straight to Google Sheets, Slack or a CRM.

The most useful pattern is chaining: a lead scraper writes its dataset, and this Actor is called with that `inputDatasetId` to clean it before anything reaches your outreach tool.

### 🤖 For AI Agents & LLM Apps

Compact reference for agents calling this Actor through the [Apify MCP server](https://mcp.apify.com) or the Apify API (`lergassy/email-phone-verifier`).

**Purpose:** decides whether contacts are worth using. Given e-mails or phone numbers, it returns a verdict per contact with the evidence behind it. Use it to answer "is this address worth sending to", "which of these numbers can be dialled" and "clean the list I just scraped".

**Minimal input:**

```json
{ "emails": ["owner@acme.com"], "phones": ["+1 206 235 0243"] }
```

**Output:** one flat row per contact — `type`, `email`, `emailStatus`, `emailReason`, `emailDomain`, `emailProvider`, `emailIsRole`, `emailIsFree`, `emailIsDisposable`, `emailSuggestion`, `phone`, `phoneStatus`, `phoneReason`, `phoneE164`, `phoneFormatted`, `phoneType`, `phoneCountry`, `phoneAreaCode`, `phoneState`, `phoneTimezone`, `phoneTimezoneOptions`, `contactScore`, `checkedAt`. Fields you pass in on a contact object are copied through unchanged.

**Behaviors an agent should know:**

- Set `findWebsite: true` when a row has a company name but no address — it returns
  `website`, `websiteStatus`, `foundEmails` and `foundEmailSource`. Trust `verified`,
  double-check `probable` (single-word names can hit a namesake), and read
  `skipped_no_website_expected` as "this kind of entity never has a site".
- Four inputs do the same job: `emails`, `phones`, `contacts` (objects, when one person has both) and `inputDatasetId` (read another run's dataset). They can be combined, and duplicates are removed across all of them.
- `emailStatus` is about the **domain**, not the mailbox: cloud platforms block outbound port 25, so no serverless tool can prove a mailbox exists. Treat `deliverable` as "worth sending", `risky` as "check first", `undeliverable` as "do not send", `unknown` as "retry later" — never as bad.
- `emailIsRole`, `emailIsFree` and `emailIsDisposable` are independent of the verdict. A deliverable address can be all three at once.
- Mobile versus landline is deliberately absent for US numbers; it cannot be derived from free data. Do not infer it.
- `phoneTimezone` is empty for 21 US area codes that straddle a time-zone boundary; both candidates are in `phoneTimezoneOptions`.
- To return only usable contacts, set `keep` rather than filtering rows afterwards — the run is billed per contact checked either way, but the answer is smaller and cleaner.
- Numbers written with a country code other than `+1` get a structure check only, reported as `phoneReason: "structure_only"`.
- No proxies, no browser, no third-party API: a run of a few thousand contacts finishes in seconds.

### ❓ FAQ

#### Does a "deliverable" verdict guarantee the mailbox exists?

No, and no serverless tool can guarantee that. It means the address is well formed and its domain accepts mail, which is what removes most hard bounces. Mailbox-level confirmation needs an SMTP conversation on a port cloud platforms block, and the big providers answer misleadingly on that port anyway.

#### Can it tell me if a phone number is a mobile?

No. For US numbers that requires commercially licensed carrier data, and number portability makes the free approximations wrong. We leave the field out rather than invent it.

#### How fresh are the throwaway-domain and area-code lists?

The disposable-domain list is a community blocklist updated daily; the area-code table comes from the North American numbering administrator's own public database. Both are baked into the Actor and refreshed when it is rebuilt.

#### Is it legal to verify e-mail addresses?

Checking the technical validity of an address you already hold is routine practice and involves no message being sent. How you obtained the list and what you send to it are governed by your local rules — GDPR, CAN-SPAM and their equivalents. Ask a lawyer about your campaign, not about this check.

#### Does it send any e-mail?

No. Nothing is sent, and no address leaves the run except as a DNS question about its domain.

#### Can I use it with the Apify API or an MCP server?

Yes. It runs from the API and the official clients, and it is available to AI agents through the Apify MCP server without any extra setup.

#### Can it clean the output of another Actor?

Yes — that is what `inputDatasetId` is for. Give it the dataset id of a previous run and the field names to read.

#### How does it find a company website?

It builds candidate domains from the name, resolves them, and then requires the
company's own name to appear on the page before reporting a match. A guess that
cannot prove itself is discarded rather than returned.

#### What happens to my own fields?

Anything you attach to a contact object is copied to the output row untouched, so the results line up with your records.

### Your feedback

Found a case it gets wrong, or need a check that is missing? Open an issue on the **Issues** tab — every one gets answered, and rules that turn out to be wrong get fixed.

### You might also like

| Actor | What it does |
|---|---|
| [Airbnb Scraper](https://apify.com/lergassy/airbnb-scraper) | Airbnb listings for any place and dates: nightly price, rating, beds, amenities, host |
| [US New Business Leads](https://apify.com/lergassy/us-business-filings) | Newly registered US businesses with phone and e-mail, already checked |
| [Google Flights Scraper](https://apify.com/lergassy/google-flights-scraper) | Live flight prices, layovers and booking options |
| [Agoda Reviews Scraper](https://apify.com/lergassy/agoda-reviews-scraper) | Hotel reviews and ratings, including the Booking.com reviews shown on Agoda |
| [Mercado Libre Scraper](https://apify.com/lergassy/mercadolibre-scraper) | Latin American marketplace listings, prices and reviews |

# Actor input Schema

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

Addresses to check, one per line. Each one is checked for syntax, then the domain is looked up to see whether it can receive mail at all.

## `phones` (type: `array`):

Numbers to check, one per line. Any format works: <code>+1 206 235 0243</code>, <code>(206) 235-0243</code>, <code>2062350243</code>.

## `contacts` (type: `array`):

Use this when one person has both. Each entry is an object such as <code>{"email": "jane@acme.com", "phone": "+1 206 235 0243", "name": "Jane"}</code>. Any extra fields you add are copied to the output row, so your own ids survive the run.

## `inputDatasetId` (type: `string`):

Dataset of a previous run — the lead list you just scraped, for example. The Actor reads the contacts out of it, so you never move data by hand. Pick it from the list, or pass its id through the API.

## `emailField` (type: `string`):

Which field holds the address. Several names can be listed, separated by commas — the first one present wins.

## `phoneField` (type: `string`):

Which field holds the number. Several names can be listed, separated by commas.

## `keep` (type: `string`):

Filter the output by what the checks found, instead of filtering rows yourself afterwards.

## `findWebsite` (type: `boolean`):

For every contact that carries a company name, guess the company's domain, verify it by finding the company name on the page, then read any address published on the home, contact or about page. Costs one or two extra requests per company and is billed only when something is found.

## `companyField` (type: `string`):

Which field holds the company name when reading another Actor's dataset. Several names can be listed, separated by commas.

## `defaultCountry` (type: `string`):

Bare numbers are read as North American by default. Numbers written with a country code (<code>+44…</code>) are always treated as international.

## `dnsCheck` (type: `boolean`):

Ask the domain's name servers whether it accepts mail. Turning this off leaves syntax and list checks only, and makes the run instant.

## `maxContacts` (type: `integer`):

Safety cap on how many contacts one run will check.

## `concurrency` (type: `integer`):

How many domains to look up at once. Twenty is fast and gentle; raise it for very large lists.

## `dnsTimeoutMs` (type: `integer`):

How long to wait for a name server before the address is returned as unknown rather than bad. A timeout is retried once with double the budget.

## `httpTimeoutMs` (type: `integer`):

How long to wait for a company page before giving up on it.

## Actor input object example

```json
{
  "emails": [
    "owner@example.com",
    "info@gmail.com",
    "hello@mailinator.com"
  ],
  "phones": [
    "+1 206 235 0243",
    "(212) 211-5678"
  ],
  "emailField": "email, contactEmail, emailAddress",
  "phoneField": "phone, contactPhone, phoneNumber",
  "keep": "all",
  "findWebsite": false,
  "companyField": "company, companyName, name",
  "defaultCountry": "US",
  "dnsCheck": true,
  "maxContacts": 10000,
  "concurrency": 20,
  "dnsTimeoutMs": 5000,
  "httpTimeoutMs": 8000
}
```

# Actor output Schema

## `contacts` (type: `string`):

One row per contact: e-mail verdict (deliverable, risky, undeliverable, unknown) with the reason and role/free/disposable flags, phone verdict with number type, US state and time zone, and a 0-100 contact score.

# 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": [
        "owner@example.com",
        "info@gmail.com",
        "hello@mailinator.com"
    ],
    "phones": [
        "+1 206 235 0243",
        "(212) 211-5678"
    ],
    "emailField": "email, contactEmail, emailAddress",
    "phoneField": "phone, contactPhone, phoneNumber",
    "companyField": "company, companyName, name"
};

// Run the Actor and wait for it to finish
const run = await client.actor("lergassy/email-phone-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": [
        "owner@example.com",
        "info@gmail.com",
        "hello@mailinator.com",
    ],
    "phones": [
        "+1 206 235 0243",
        "(212) 211-5678",
    ],
    "emailField": "email, contactEmail, emailAddress",
    "phoneField": "phone, contactPhone, phoneNumber",
    "companyField": "company, companyName, name",
}

# Run the Actor and wait for it to finish
run = client.actor("lergassy/email-phone-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": [
    "owner@example.com",
    "info@gmail.com",
    "hello@mailinator.com"
  ],
  "phones": [
    "+1 206 235 0243",
    "(212) 211-5678"
  ],
  "emailField": "email, contactEmail, emailAddress",
  "phoneField": "phone, contactPhone, phoneNumber",
  "companyField": "company, companyName, name"
}' |
apify call lergassy/email-phone-verifier --silent --output-dataset

```

## MCP server setup

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