# Bulk Email Verifier, Finder and Deliverability Checker (`neverbounce/email-finder-and-verifier`) Actor

Verify, find, and grade emails without the guesswork. Unlike cheaper checkers that quietly mark a blocked connection as 'invalid,' this one tells you the truth: verified, invalid, or honestly unknown, every time.

- **URL**: https://apify.com/neverbounce/email-finder-and-verifier.md
- **Developed by:** [Never Bounce](https://apify.com/neverbounce) (community)
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.85 / 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?

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 and Finder

Find corporate email addresses, check whether an address is likely real, and grade a domain's email deliverability setup (SPF, DKIM, DMARC), with zero third-party API dependency. Everything runs as this Actor's own code: DNS lookups, pattern generation, and optional public-data enrichment (company website, GitHub, Gravatar). This Actor does not do live SMTP mailbox probing (see "Why this Actor doesn't do live SMTP checks" below for why).

### What it does

Give it any mix of three input types in one run:

- **emails**, a list of specific addresses to check (format, MX/provider, disposable/free/role detection, and a confidence score built from the domain's DNS and email-authentication setup).
- **contacts**, a list of `{firstName, lastName, domain}`. Generates the common corporate email patterns (`first.last@`, `flast@`, `first@`, etc.), ranks them, and optionally cross-checks the company website, GitHub commit history, and Gravatar for a real observed match.
- **domains**, a list of domains. Tests common role addresses (`info@`, `contact@`, `sales@`, `support@`, `hello@`, `admin@`) and reports which ones the domain could plausibly deliver to.

Every result also gets an optional deliverability grade (A+ to F) for the domain's SPF, DKIM, and DMARC setup.

### First time seeing your results? Read this first.

**If most of your rows show `resultStatus: "unknown"` or `"likely_valid"` rather than a flat `"valid"`, that is the normal, expected result, not a sign this Actor is broken or that your email is bad.** Every single row also includes a `statusMeaning` field with a plain-English sentence explaining exactly what that specific result does and doesn't tell you. Start there before worrying about the other fields.

The short version: no email tool, on Apify or anywhere else, can safely and reliably confirm one specific mailbox exists without a live SMTP conversation with the target mail server, and this Actor deliberately doesn't attempt that (see below). So `resultStatus` is built entirely from real, checkable signals instead:

| `resultStatus` | What it means |
|---|---|
| `invalid_format` | The address isn't even validly formatted. |
| `invalid` | The domain has a confirmed absence of any MX record, it cannot receive mail at all. |
| `likely_valid` | Strong signal: the domain has a full email-authentication setup (MX + SPF + DKIM + DMARC all found). Not a live confirmation, this Actor's strongest tier below outright evidence. |
| `valid` | Contact mode only: this exact address was directly observed in public data (the company website or a GitHub commit), not guessed from a pattern. |
| `unknown` | Looks plausible, but there isn't enough evidence to call it more than that. |
| `low_confidence` | A weak pattern guess with little supporting evidence, treat as a long shot. |

Use `confidenceScore` (0 to 100) as your real signal, higher means more likely to be a real, deliverable address.

### Why this Actor doesn't do live SMTP checks

Outbound port 25, the port live SMTP mailbox verification needs, is blocked on Apify's infrastructure (and on most cloud platforms), so a live "does this mailbox actually exist" probe almost never gets a real answer there anyway. Worse, sending a speculative RCPT TO to a mail server you don't own, to test whether some address exists, is exactly the kind of sending behavior that gets IPs rate-limited or blacklisted by receiving providers. We don't think that tradeoff, unreliable results in exchange for a real deliverability risk, is worth it, so this Actor doesn't do it at all.

Some competing actors do attempt it, and quietly treat a failed or blocked connection as proof the address is invalid. We tested one directly: it reported `postmaster@github.com`, an address guaranteed to exist by RFC, as invalid, with the exact same output as a genuinely nonexistent address. That's not a verification result, it's a coin flip dressed up as one, and it will get real, deliverable leads thrown out of your list.

Instead, every result here is built from signals that are actually real and checkable:

1. **MX records** — can the domain receive mail at all? A confirmed absence is the only thing that produces `"invalid"`.
2. **SPF, DKIM, and DMARC** — does the domain have a genuine, complete email-authentication setup? All three present is a meaningfully strong signal for a domain a business actually controls and runs, so it earns `"likely_valid"`.
3. **Direct evidence** (contact mode only) — was this exact address found on the company's own website, or in public GitHub commit history? That's real observed evidence, not a guess, so it earns `"valid"`.

One important nuance: for free webmail domains (Gmail, Outlook, Yahoo, etc.), MX/SPF/DKIM/DMARC are essentially always fully present, that's true of every address at that domain, real or made up, so those records say nothing about whether one specific username exists there. This Actor accounts for that: domain-authentication signals only earn their full confidence bonus for a domain the target actually controls (a custom company domain), not a shared free provider. That's why `test@gmail.com` correctly stays `"unknown"` even though gmail.com itself has perfect SPF/DKIM/DMARC.

### Input

See the input schema for full details. Key fields:

| Field | Description |
|---|---|
| `emails` / `contacts` / `domains` | The three input modes, use any combination |
| `emailsFile` | Optional CSV upload as an alternative to `emails`. Single column, no header row |
| `contactsFile` | Optional CSV upload as an alternative to `contacts`. Header row required, with columns `firstName`, `lastName`, `domain`, and an optional `linkedinUrl` column |
| `verificationLevel` | `format` (syntax only) or `mx` (default; also checks MX, provider, and SPF/DKIM/DMARC) |
| `maxPatternsToTest` | How many email patterns to try per contact (default 8) |
| `checkDeliverability` | SPF, DKIM, and DMARC grade for the domain |
| `checkWebsite` / `checkGitHub` / `checkGravatar` | Extra enrichment for contact mode: company website, GitHub, and Gravatar lookups. All three are free (no extra cost) and on by default |
| `githubToken` | Optional token for higher GitHub API rate limits |

For contacts, `firstName`, `lastName`, and `domain` (the contact's company website domain, e.g. `example.com`) are all mandatory, whether you enter them as JSON or upload them as a CSV. `linkedinUrl` (optional) can be added too, it's simply passed through to the result for your own reference. For emails, you can either use the Bulk edit box or upload a single-column, no-header CSV in `emailsFile` instead.

### Output

One dataset row per result, including: `email`, `resultStatus` (`valid`, `likely_valid`, `unknown`, `invalid`, `invalid_format`, or `low_confidence`, see the table above), **`statusMeaning`** (a plain-English sentence explaining that row's result, read this first if a status is confusing), `confidenceScore`, `isVerified`, `hasMxRecords`, `provider`, `isDisposable`, `isFreeProvider`, `isRoleAccount`, `verificationMethod`, and (when enabled) `deliverabilityGrade`, `deliverabilityScore`, and `deliverabilityIssues`.

### Pricing

$4.00 per 1,000 results, pay per event. No hidden per-call costs from third-party services, this Actor doesn't call any.

### FAQ

**Does this Actor need any API keys?**

No. Everything (DNS lookups, pattern generation, deliverability grading) runs as the Actor's own code. The only optional key is a GitHub token, used solely to raise the GitHub search rate limit for contact-mode enrichment.

**Why does my result say "unknown" or "likely\_valid" instead of "valid"?**

That's expected for most real, deliverable addresses, see "First time seeing your results? Read this first" above and the `statusMeaning` field on the row itself. This Actor doesn't do live SMTP mailbox probing (see "Why this Actor doesn't do live SMTP checks"), so `"valid"` is reserved for cases with real observed evidence in contact mode. Everything else is scored honestly from MX and email-authentication signals instead of guessed. Check `confidenceScore` for a 0-100 likelihood.

**Will it ever falsely mark a real email as invalid?**

No. Only a confirmed-absent MX record or a failed format check produces `"invalid"`. There's no SMTP step that could misread a blocked connection as a rejection.

**Can I upload a list of names and domains and get back verified emails?**

Yes, that's the contacts input mode: give it `firstName`, `lastName`, and `domain` for each row, and it returns the best-candidate email with a confidence score.

**Can I check whether a domain's email setup will land in spam?**

Yes, turn on `checkDeliverability`. It grades the domain's SPF, DKIM, and DMARC records (A+ to F) and lists specific issues found.

# Actor input Schema

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

List of specific email addresses to check (format, MX, disposable/free/role detection, and a confidence score from the domain's DNS and email-authentication setup). Use the Bulk edit button to paste many at once, or skip this and upload a CSV in the field below instead.

## `emailsFile` (type: `string`):

Optional alternative to the list above. CSV file with a single column of email addresses and no header row. Rows here are combined with any addresses entered above.

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

List of {firstName, lastName, domain, linkedinUrl}. firstName, lastName, and domain (the company's website domain, e.g. example.com) are all mandatory per contact. LinkedIn URL (optional) is passed straight through to the result for your own reference. Generates common corporate email patterns and scores the best candidate. Enable website/GitHub/Gravatar checks below for stronger evidence than pattern guessing alone, all at no extra cost. You can also skip this and upload a CSV in the field below instead.

## `contactsFile` (type: `string`):

Optional alternative to the list above. CSV file with a header row and the columns firstName, lastName, domain (all three required per row; domain is the contact's company website domain, e.g. example.com), plus an optional linkedinUrl column. Rows here are combined with any contacts entered above.

## `domains` (type: `array`):

List of domains. Tests common role addresses (info@, contact@, sales@, support@, hello@, admin@).

## `verificationLevel` (type: `string`):

'format' only checks syntax. 'mx' (default) also checks DNS MX records, identifies the provider, and factors the domain's SPF/DKIM/DMARC authentication setup into the confidence score. There is no live SMTP option - see README for why.

## `maxPatternsToTest` (type: `integer`):

How many common corporate email formats to generate and rank per contact.

## `checkDeliverability` (type: `boolean`):

DNS-based check of the domain's email authentication setup. Produces a letter grade (A+ to F) and a 0-100 score plus a list of issues found.

## `checkWebsite` (type: `boolean`):

Contact mode only. Scrapes the domain's homepage / contact / about / team pages for an email that plausibly belongs to this person. A direct match here overrides pattern guessing and is reported as resultStatus 'valid'. Free, no extra cost, on by default.

## `checkGitHub` (type: `boolean`):

Contact mode only. Searches public GitHub commit history for an author-email match on the target domain. Useful for developer contacts. Unauthenticated requests are rate-limited to about 10/minute - add a token below for higher limits. Free, no extra cost, on by default.

## `checkGravatar` (type: `boolean`):

Contact mode only. Checks whether the best-candidate email has a Gravatar profile, adding a small confidence boost if so. Free, no extra cost, on by default.

## `githubToken` (type: `string`):

Personal access token for higher-rate-limit GitHub commit search. Only used when checkGitHub is on.

## Actor input object example

```json
{
  "emails": [
    "test@gmail.com"
  ],
  "contacts": [
    {
      "firstName": "John",
      "lastName": "Doe",
      "domain": "example.com",
      "linkedinUrl": "https://www.linkedin.com/in/johndoe"
    }
  ],
  "verificationLevel": "mx",
  "maxPatternsToTest": 8,
  "checkDeliverability": true,
  "checkWebsite": true,
  "checkGitHub": true,
  "checkGravatar": true
}
```

# 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": [
        "test@gmail.com"
    ],
    "contacts": [
        {
            "firstName": "John",
            "lastName": "Doe",
            "domain": "example.com",
            "linkedinUrl": "https://www.linkedin.com/in/johndoe"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverbounce/email-finder-and-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": ["test@gmail.com"],
    "contacts": [{
            "firstName": "John",
            "lastName": "Doe",
            "domain": "example.com",
            "linkedinUrl": "https://www.linkedin.com/in/johndoe",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("neverbounce/email-finder-and-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": [
    "test@gmail.com"
  ],
  "contacts": [
    {
      "firstName": "John",
      "lastName": "Doe",
      "domain": "example.com",
      "linkedinUrl": "https://www.linkedin.com/in/johndoe"
    }
  ]
}' |
apify call neverbounce/email-finder-and-verifier --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverbounce/email-finder-and-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/AxBfo2FB1hbOaa8Wu/builds/pk0fQ4dgscsGh3Nfn/openapi.json
