# Email Verifier with Owner Profile (`b2bdata/email-verifier`) Actor

Email verifier: give addresses, get one row per definitive answer (valid or invalid) with the mail server and, when known, the owner's name, title and profile URL. Free plan: 100 addresses per run. Pay per verified row, unknown results free. No login or cookies. Does not find emails.

- **URL**: https://apify.com/b2bdata/email-verifier.md
- **Developed by:** [Open Enrich](https://apify.com/b2bdata) (community)
- **Categories:** Lead generation, Marketing
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 email verifieds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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?

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 Verifier with Owner Profile

Email verifier: give a list of email addresses, get one row per address that has a definitive answer (valid, invalid or catch-all), with the mail server and, when the data knows who the address belongs to, the owner's name, title, company and profile URL. An address that cannot be confirmed is reported as unknown and costs nothing. No login, no cookies, no start fee.

**No login | Addresses in, status out | Valid, invalid or catch-all per row | Owner profile when known | Unknown answers are free | Repeats checked once | JSON/CSV export**

> **Read the limits before you trust a verdict.** `invalid` means the address cannot receive mail. In our own checks it came back for domains with no mail server. A made-up mailbox at a working mail provider (Google Workspace, Microsoft 365, Gmail, Yahoo, a hosted domain) came back `unknown`, because those servers do not reveal whether a mailbox exists. `valid` is therefore a strong signal and `unknown` is not a rejection. See "How to read the rows".

[See all actors by b2bdata](https://apify.com/b2bdata)

***

### 💳 Pricing and plans

One event, `verification`, charged once per address that has a definitive answer (`valid`, `catch_all` or `invalid`). Prices per 1,000 verified addresses (set on the Pricing tab):

| Event | When it is charged | Free plan | Gold plan |
|---|---|---|---|
| `verification` | A row with status valid, catch_all or invalid is delivered to your dataset | $1.096 | $0.796 |

Bronze and Silver prices are on the Pricing tab. Higher Apify plans only lower the price. Empty runs, unknown answers, addresses with nothing on file, failed lookups, unreadable entries and notices are free, and there is no start fee.

**Plan-credit math.** The free plan's $5 monthly credit covers about 4,560 verified addresses at the Free price.

#### Success-only billing

You are charged only for a row you receive. An address that cannot be confirmed gets one free notice row (`reason: "unknown"`). An address with no answer on file gets a free `not_found` notice. An entry that is not an address, a repeat, or a lookup that fails is listed in `MISSES` with its position and a reason and costs nothing. The same address written twice is checked once and charged once.

#### The free plan

Free Apify accounts get up to 100 addresses checked per run, and as many runs as their Apify credit covers. Nothing is stored on our side. When a list is longer, the first 100 valid addresses are checked and one free notice row (`reason: "free_plan_limit"`) says how many were not checked; they are not charged. Run again with the rest, or use a paid Apify plan for no per-run limit.

#### Keep a run small while you test

`limit` (default 100, maximum 1,000) caps the whole run and what is charged. Try one address with `limit: 1`, read the row, then scale up.

***

### ⚡ Why this email verifier?

- **One fixed status.** Every row says `valid`, `catch_all` or `invalid`, whichever mail server answered. No score to interpret and no vendor-specific codes.
- **Who owns the address.** When the data knows the person behind an address, the row carries their name, title, company and profile URL, so a list check doubles as a quick sanity check on who you are about to mail.
- **No charge for no answer.** Unknown results are free notices, not billed rows.
- **Repeats checked once.** Addresses are lower-cased and de-duplicated before any lookup, so a pasted list with repeats is not billed twice.
- **Typos fail early.** An unsupported input field or a limit above the maximum stops the run before any charge.

***

### 🎯 Use cases: check a list before you send

- **Pre-send list check.** Run a campaign list and keep the `valid` rows, review `catch_all`, drop `invalid`.
- **CRM hygiene.** Spot addresses that cannot receive mail.
- **Lead qualification.** Confirm an address and see the title and profile it belongs to.
- **Agent workflows.** An AI agent holds an address and needs a verdict in a single call.

***

### 🚀 Quick start: verify addresses

#### 1. One address

```json
{ "items": ["patrick@stripe.com"], "limit": 1 }
```

#### 2. A list, with the "Name <address>" form accepted

```json
{ "items": ["jane@microsoft.com", "Jane Doe <jane@shopify.com>", "info@openai.com"], "limit": 100 }
```

Each entry becomes its bare lower-case address before the lookup.

***

### ⚙️ Inputs

| Key | Type | Default | What it does |
|---|---|---|---|
| `items` | array of strings | required | Email addresses. `Name <address>` and `mailto:` forms are reduced to the address. An entry that is not an address is listed in `MISSES` as `invalid_email`. Repeats are checked once. Maximum 1,000 per run. Free plan: 100 per run. |
| `limit` | integer | 100 | Most result rows delivered in the whole run, which also caps what is charged. Maximum 1,000. Addresses not reached are listed in `MISSES`. |
| `includeAudit` | boolean | false | Adds an `audit` object to each row. |

Any other field stops the run before a lookup with `remove unsupported fields: ...`. Nothing is charged.

***

### 📤 Output

One result row per address with a definitive answer, in input order. Empty values are left out.

| Key | Type | Meaning, and when it is missing |
|---|---|---|
| `email` | string | The address, lower-cased |
| `emailStatus` | string | `valid`, `catch_all` or `invalid`. `unknown` is never a row, see notices |
| `domain` | string | The part after the @ |
| `mailServer` | string | The mail server that receives mail for the domain. Missing when the domain has none |
| `isCatchAllDomain` | boolean | Whether the domain accepts every address. Missing when that was not determined |
| `fullName`, `firstName`, `lastName` | string | The owner, when the data links a person to the address |
| `jobTitle` | string | The owner's current title |
| `headline` | string | The owner's profile headline |
| `company` | string | The employer of the owner's open position. Missing when there is none |
| `location` | string | Place on the owner's profile |
| `country` | string | Two-letter country code |
| `profileUrl` | string | The owner's public profile URL |
| `slug` | string | Profile slug |
| `updatedAt` | string | When the owner's profile was last refreshed in the data |
| `audit` | object | Only with `includeAudit`: `requestId`, `unitsCharged`, `latencyMs`, `inputRaw` |

Example output (the shape is real, the values are illustrative):

```json
{
  "email": "ann@stripe.com",
  "emailStatus": "valid",
  "domain": "stripe.com",
  "mailServer": "aspmx.l.google.com",
  "fullName": "Ann Example",
  "firstName": "Ann",
  "lastName": "Example",
  "jobTitle": "Staff Engineer",
  "headline": "Engineering leader",
  "company": "Stripe",
  "location": "Durham, North Carolina, United States",
  "country": "US",
  "profileUrl": "https://www.linkedin.com/in/ann-example",
  "slug": "ann-example",
  "updatedAt": "2026-11-09T07:29:01.865Z"
}
```

#### How to read the rows

- **`valid`.** The mail server accepted the address. Treat it as deliverable.
- **`invalid`.** The address cannot receive mail. In our checks every `invalid` was a domain with no mail server (a mistyped or dead domain). A mailbox that does not exist at a working provider was reported `unknown`, not `invalid`, so a list can hold bad addresses that this actor does not flag.
- **`catch_all`.** A valid answer on a domain that accepts every address, so the answer says little about the mailbox. This status appears only when the data flags the domain as catch-all. We did not see one in our checks, so treat the status as available but unproven on real lists.
- **`unknown` (free notice).** No definitive answer. Big providers often give none. It is not a rejection and it is not charged. Decide with your own rule whether to send.
- **The owner is a data link, not proof.** The person shown is the one the data associates with the address. It is present for part of the addresses, mostly work addresses at companies, and personal addresses are covered less well than work addresses. A missing owner says nothing about the address.
- **Each check is one live lookup.** Answers can change as mailboxes change. Re-check a list before a send if it is old.

#### Other records in the run

- **`MISSES`** (key-value store): `[{index, input, reason}]` for every entry without a result row. Reasons: `invalid_email`, `duplicate_input` (the same address as an earlier entry; `duplicateOf` names it), `unknown`, `not_found`, `unreadable_answer`, `not_looked_up_limit` (the run `limit` was reached first), or `Upstream HTTP <code>`.
- **`errors-email-verifier`** (key-value store): the lookups that failed for a technical reason, so you can re-run only those addresses.
- **`RUN_SUMMARY`**: counts (requested, valid entries, checked, rows delivered, counts by status).
- **Notice rows** (`type: "notice"`) appear for an unknown answer, an address with nothing on file, a free-plan list longer than 100 (`free_plan_limit`), a reached `limit` (`limit_reached`), a reached spend limit (`charge_limit_reached`) or invalid input (`invalid-input`). Notices are never charged.

#### What is NOT returned

- A deliverability score, a spam-trap or disposable-address flag, or a risk grade
- Email addresses you did not give (use [email-finder](https://apify.com/b2bdata/email-finder))
- Phone numbers, work history or education

***

### 🛡️ Reliable results

**Our data promise.** We work to keep the data current, and we stand behind it. If an answer looks wrong or an owner is out of date, open the Issues tab with your run link. We review every report and sort it out with you, case by case.

- **No answer is free and visible.** Every address without a row is in `MISSES` with its position and a reason and is never charged.
- **Repeats are charged once.** The same address written twice is one lookup and one row.
- **Each row is saved as it is delivered.** A run that stops keeps what it already produced.
- **Bounded pace.** Lookups run one after another at no more than 60 a minute, so a long list takes time. The run timeout is one hour, which is why a run is limited to 1,000 addresses.
- **Typos fail early.** An unsupported input field or a limit above its maximum stops the run before any charge.

***

### 🤖 For AI agents (MCP)

This actor runs through the Apify MCP server. Prompt to copy:

> "Use b2bdata/email-verifier with items=\["patrick@stripe.com"] and limit=1. Return email, emailStatus and fullName."

**When to use it.** You hold an email address and need to know whether mail to it will be accepted.
**When not to.** You need to find an address (use b2bdata/email-finder) or identify a person from an address (use b2bdata/person-by-email).
**Input contract.** `items` array of email addresses; optional `limit` 1-1,000 (default 100).
**Output contract.** One row per address with `email` and `emailStatus` (`valid`, `catch_all`, `invalid`), plus owner keys when known. Output is deterministic and schema-stable. Unknown and missing answers are a free notice row and listed in `MISSES`.
**Smoke test.** `limit: 1` on a well-known address returns one row.
**Error cases.** `No valid email addresses` means no entry was an address. `remove unsupported fields` or a bad number ends the run SUCCEEDED with one free `invalid-input` notice row and nothing charged.

***

### 🔌 Integrations: send results anywhere

1. **API.** Fetch rows from the run's default dataset: `GET /v2/datasets/{datasetId}/items?format=csv`.
2. **Webhook** on run success to push rows to your own endpoint.
3. **n8n, Make, Zapier.** Use the native Apify nodes: run the actor, then read the dataset.
4. **Google Sheets, Airtable, HubSpot, Salesforce, Clay.** Join on `email` and map `emailStatus` to a status field.

***

### 🕐 Schedule examples

- **Weekly list refresh.** A schedule with a saved task that points at your send list.
- **New-lead check.** A webhook from your form or CRM starts a run with the new addresses.

***

### 🔗 Related actors

- `b2bdata/email-finder`: a work email for a person at a domain. [Open](https://apify.com/b2bdata/email-finder)
- `b2bdata/bulk-email-finder`: work emails for a CSV of names and domains. [Open](https://apify.com/b2bdata/bulk-email-finder)
- `b2bdata/person-by-email`: the person and profile behind an email address. [Open](https://apify.com/b2bdata/person-by-email)
- `b2bdata/waterfall-contact-enrichment`: find and verify a contact from any identifier. [Open](https://apify.com/b2bdata/waterfall-contact-enrichment)

[See all actors by b2bdata](https://apify.com/b2bdata)

***

### ❓ FAQ

#### Data and coverage

**How do I verify a list of email addresses?**
Put the addresses in `items` and run. You get one row per address with a definitive answer: `valid`, `catch_all` or `invalid`.

**Why is an address missing from the results?**
It is in `MISSES` with a reason: no definitive answer (`unknown`), nothing on file, a repeat, or an entry that was not an address. None of these are charged.

**Does it check whether a mailbox exists without sending an email?**
It asks the receiving side without sending a message. Some providers do not say whether a mailbox exists, and those addresses come back `unknown` rather than `invalid`.

**Is there a catch-all check?**
The row carries `isCatchAllDomain` and the status `catch_all` when the data flags the domain as accepting every address. We did not see a catch-all domain in our checks, so test it on your own list.

**Do I get the owner of the address?**
Sometimes. When the data links a person to the address the row has their name, title, company and profile URL. Personal addresses are covered less well than work addresses.

**Do I need a LinkedIn account or cookies?**
No. It is a lookup without login, cookies or an account to ban.

#### Pricing

**What do I pay for an unknown answer?**
Nothing. There is no start fee, and unknown answers, bad entries, repeats and notices are free.

***

### 🛟 Support

Use the **Issues** tab on this actor's Apify page for bugs, questions and feature requests. We answer within 48 hours on working days. Please include the run ID and the input you used. Published on Apify by b2bdata. Not affiliated with, endorsed by, or sponsored by any company or person named in the examples above.

# Changelog

This Actor's version history is a separate document: https://apify.com/b2bdata/email-verifier/changelog.md

# Actor input Schema

## `items` (type: `array`):

Email addresses to verify. Example: \["patrick@stripe.com", "Jane Doe <jane@microsoft.com>"]. A "Name <address>" form is reduced to the address. Addresses are lower-cased and repeats are checked once; an entry that is not an address is listed in the MISSES record as invalid_email. An address that cannot be confirmed (unknown) returns one free notice row. Free plan: 100 addresses per run. Maximum 1000 per run.

## `limit` (type: `integer`):

Most result rows delivered in the whole run, which also caps what is charged. Addresses not reached are listed in MISSES and are not charged. Default 100, maximum 1000.

## `includeAudit` (type: `boolean`):

Adds requestId, unitsCharged, latencyMs and inputRaw to each row under audit. Omit for clean rows.

## Actor input object example

```json
{
  "items": [
    "patrick@stripe.com"
  ],
  "limit": 100,
  "includeAudit": false
}
```

# Actor output Schema

## `verified` (type: `string`):

One row per verified address: status, mail server and, when known, the owner's name, title, company and profile URL

## `summary` (type: `string`):

Counts by status and stop reason for this run

# 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 = {
    "items": [
        "patrick@stripe.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2bdata/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 = { "items": ["patrick@stripe.com"] }

# Run the Actor and wait for it to finish
run = client.actor("b2bdata/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 '{
  "items": [
    "patrick@stripe.com"
  ]
}' |
apify call b2bdata/email-verifier --silent --output-dataset

```

## MCP server setup

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