# Email Finder: Find Work Emails by Name & Company Domain (`datadesk-io/email-finder-by-name-domain`) Actor

Email finder for B2B sales and recruiting: enter first name, last name and company domain, get the most likely work email. Ranks patterns from addresses published on the company site, checks MX, syntax, disposable and typos. Works for US and global companies. No SMTP. $3 per 1,000.

- **URL**: https://apify.com/datadesk-io/email-finder-by-name-domain.md
- **Developed by:** [DataDesk](https://apify.com/datadesk-io) (community)
- **Categories:** Lead generation, AI
- **Security:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 contact with email candidates

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?

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 Finder: Find Work Emails by Name & Company Domain

Give it **a first name, a last name and a company domain** and get the **most likely work email addresses, ranked by evidence**, plus **domain checks**: syntax, MX records, disposable domains, free providers and typo suggestions. Export JSON/CSV/Excel or call it from an AI agent.

> **Read this first: there is NO SMTP check.** This Actor never connects to a mail server to ask if a mailbox exists. It does **not** verify that a mailbox exists, and it does **not** detect catch-all domains. It tells you which address patterns are plausible and whether the **domain** can receive mail. Outbound SMTP (port 25) is blocked on the Apify platform, so a mailbox-level check is not possible here. If you need mailbox verification, run the candidates through a dedicated verifier before sending.

🇪🇸 **Español:** encuentra el correo laboral más probable a partir de nombre, apellido y dominio de la empresa. Genera patrones (nombre.apellido, inicial+apellido, etc.), los ordena según evidencia publicada en el sitio de la empresa y valida el **dominio** (sintaxis, MX, dominios desechables, errores tipográficos). **No usa SMTP**: no confirma que el buzón exista ni detecta dominios catch-all.
🇧🇷 **Português:** encontra o e-mail corporativo mais provável a partir de nome, sobrenome e domínio da empresa. Gera padrões (nome.sobrenome, inicial+sobrenome etc.), ordena por evidências publicadas no site da empresa e valida o **domínio** (sintaxe, MX, domínios descartáveis, erros de digitação). **Não usa SMTP**: não confirma se a caixa postal existe nem detecta domínios catch-all.

### What you get

- 🔎 **Ranked email candidates** per person (up to 11 patterns: `first.last`, `flast`, `first`, `firstlast`, `f.last`, `first_last`, `firstl`, `last`, `last.first`, `lastf`, `first-last`).
- 🏷️ **A `confidence` label per candidate**, based on what the company's own website shows (see below). It is a ranking label, **not a probability**: we publish no accuracy percentage.
- ✅ **Domain verification:** valid syntax, MX records (and the mail host), disposable-domain blocklist (about 9,900 domains), free providers (gmail.com...), typo suggestion (`gmial.com` → `gmail.com`, `.con` → `.com`).
- 📨 **Email checker mode:** pass addresses in `emailsToCheck` to get syntax, MX, disposable, free-provider, role-based (info@, sales@) and typo flags.
- 🌎 Accents and compound surnames are handled (`María González` → `maria.gonzalez`); works with any domain, including `.com.br`, `.mx`, `.co`.
- 💸 **$3 per 1,000 contacts**, plus $0.005 per run. Runs with no results cost nothing.

### How the confidence label works

| `confidence` | `evidence` | Meaning |
|---|---|---|
| `high` | `seen_on_website` | This exact address is published on the company website (home, contact, about, team pages). |
| `medium` | `matches_site_convention` | At least 2 person addresses published on the site use the same pattern (for example two `first.last` addresses). |
| `low` | `single_site_example` | One address on the site has this pattern. |
| `low` | `common_pattern` | Nothing on the site; this is only a common pattern, in a fixed conventional order. |

**Most results will be `low`:** many companies do not publish staff addresses. In our tests (Oct 24, 2026) we got `high` for a person listed on a public NASA page and `low` for most commercial sites (HubSpot, Stripe, Microsoft, Shopify and Mercado Libre showed no person addresses on the pages we read). Treat `low` as a guess to verify elsewhere. The `siteConvention` field shows what the site published, so you can judge the evidence yourself.

### Sources and coverage

| Source | Coverage | Data |
|---|---|---|
| **Company website** | Any domain | emails published on the site, used to learn the company's email pattern |
| **DNS (MX records)** | Any domain | whether the domain accepts email, mail provider |
| **Pattern engine** | Any name + domain | ranked candidates (first.last@, flast@...) with confidence label |

### How to use it

1. Click **Try for free** (or **Start**): the prefilled input runs a quick test.
2. Fill in the input fields (see the examples below). Every field is optional.
3. Click **Start** and wait a few seconds to minutes, depending on how many contacts you ask for.
4. Open the **Output** tab and export as **JSON, CSV, Excel, XML or HTML**, or read the results via API.

### Input examples

**Two contacts (default):**

```json
{"contacts": [{"firstName": "Jane", "lastName": "Doe", "domain": "hubspot.com"}]}
```

**Bigger list, top 5 candidates:**

```json
{"contacts": [{"firstName": "John", "lastName": "Smith", "domain": "stripe.com"}, {"firstName": "Ana", "lastName": "García", "domain": "mercadolibre.com"}], "candidatesPerContact": 5}
```

**Only check email domains:**

```json
{"emailsToCheck": ["sales@acme.com", "jane@gmial.com"]}
```

**Fast mode (no website scan):**

```json
{"contacts": [{"firstName": "Jane", "lastName": "Doe", "domain": "notion.so"}], "scanWebsite": false}
```

#### Input fields

| Field | Type | Description |
|---|---|---|
| `contacts` | array | List of people to find. Each item is {"firstName","lastName","domain"} (or {"fullName","domain"}), or a string like "Jane Doe, hubspot.com". Leave empty and fill only … |
| `emailsToCheck` | array | Optional addresses to validate at domain level: syntax, MX, disposable, free provider, role-based, typo suggestion. It does NOT check that the mailbox exists. |
| `candidatesPerContact` | integer | How many ranked email candidates to return per person (the best goes in 'email', the rest in 'alternatives'). |
| `scanWebsite` | boolean | Reads a few pages of the company site (home, contact, about, team) to find published addresses. They raise the confidence of a matching pattern. Turn off for faster, p… |
| `maxPagesPerDomain` | integer | Pages to read per company domain when scanning the website. |
| `includeFailedRows` | boolean | Also output contacts whose domain is invalid, has no MX or is disposable, with the reason. Those rows are never charged. |
| `maxResults` | integer | Hard cap on charged rows for the whole run. Never exceeded. |

### Output example

One row per item. Values below are **example values** from a real test run (long text shortened):

```json
{
  "mode": "find",
  "firstName": "jane",
  "lastName": "doe",
  "domain": "hubspot.com",
  "input": {"firstName": "Jane", "lastName": "Doe", "domain": "hubspot.com"},
  "email": "jane.doe@hubspot.com",
  "emailPattern": "first.last",
  "confidence": "low",
  "evidence": "common_pattern",
  "alternatives": ["{…}", "{…}"],
  "smtpVerified": false,
  "verificationNote": "Domain-level checks only (syntax, MX, disposable). Mailbox existence and catch-all were NOT checked.",
  "domainStatus": "deliverable_domain",
  "domainHasMx": true,
  "mxHost": "smtp.google.com",
  "isDisposableDomain": false,
  "isFreeProvider": false,
  "websitePagesScanned": 5
  "…": "more fields below"
}
```

Fields a source does not publish are `null`.

#### Output fields

| Field | Description |
|---|---|
| `mode` | find (name + domain) or check (email validation) |
| `firstName` | First name used (ASCII, lowercase) |
| `lastName` | Last name used (ASCII, lowercase, spaces removed) |
| `domain` | Company domain (normalized, no www) |
| `input` | Your original input item |
| `email` | Best candidate email (rank 1), or the address checked in 'check' mode |
| `emailPattern` | Pattern of the best candidate (first.last, flast, first...) |
| `confidence` | high = this exact address is published on the company website; medium = at least 2 person addresses published on the site use this pattern; low = single example or only a common pattern, no real evidence. A ranking label, NOT a probability |
| `evidence` | seen_on_website (this exact address is published on the site), matches_site_convention (2+ person addresses on the site share this pattern), single_site_example (1 address on the site has this pattern) or common_pattern (no evidence) |
| `alternatives` | Other ranked candidates: email, pattern, evidence, confidence |
| `smtpVerified` | Always false: this Actor never uses SMTP and does not verify mailboxes or catch-all |
| `verificationNote` | Plain-language limit of the verification |
| `domainStatus` | deliverable_domain (has MX), no_mx_a_record_only, domain_not_found, invalid_domain_syntax or invalid_syntax |
| `domainHasMx` | True when the domain publishes MX records |
| `mxHost` | Primary mail server host |
| `isDisposableDomain` | True when the domain is in a public disposable-email blocklist |
| `isFreeProvider` | True for free providers such as gmail.com |
| `domainTypoSuggestion` | Likely correct domain when the input looks like a typo (gmial.com, .con) |
| `websitePagesScanned` | Pages of the company website read |
| `websiteError` | Why the site could not be read, if so |
| `siteConvention` | Person-address patterns published on the site: counts, dominant pattern and sample local parts |
| `scrapedAt` | ISO timestamp |

The Output tab has a **Contacts** view.

### Pricing

Pay-per-event: you pay only for results delivered, no monthly fee.

- **Contact with email candidates** (`contact`): **$3.00 per 1,000** ($0.003 each)
- **Email domain check** (`email-check`): **$1.00 per 1,000** ($0.001 each)
- **Actor start:** $0.005 per run

| Run | Contacts | Cost |
|---|---|---|
| Quick test | 20 | $0.065 |
| Small run | 100 | $0.305 |
| Regular run | 1,000 | $3.005 |
| Large run | 10,000 | $30.005 |

Rows that are not delivered are never charged. Apify's free plan includes monthly credit, and you can set a maximum cost per run in the run options.

### Use cases

Outbound sales prospecting · recruiting outreach · PR and link-building lists · cleaning lead lists (typos, disposable and invalid domains) · feeding AI agents that need a work-email candidate for a contact.

### Integrations and API

- Export results as JSON, CSV, Excel, XML or HTML from the Output tab.
- **Schedule** runs (daily, hourly) and send results to Google Sheets, Slack, Zapier, Make, n8n, Airbyte or webhooks.
- Call it from code with the [Apify API](https://docs.apify.com/api/v2):

```bash
curl -X POST "https://api.apify.com/v2/acts/datadesk-io~email-finder-by-name-domain/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"contacts": [{"firstName": "Jane", "lastName": "Doe", "domain": "hubspot.com"}]}'
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("datadesk-io/email-finder-by-name-domain").call(run_input={'contacts': [{'firstName': 'Jane', 'lastName': 'Doe', 'domain': 'hubspot.com'}]})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item.get('domain'), item.get('email'))
```

### FAQ

**Why is nearly everything `low`?** Because most company sites do not publish staff emails. The label tells you honestly when there is no evidence.
**Can I send to the candidates directly?** Verify them first with a mailbox verifier; wrong guesses can bounce and hurt your sender reputation.
**Issues?** Report them on the Issues tab. We usually reply within 48 h.

**Do I need a login, API key or proxies?** No. It uses publicly available data only, so no account on the source site is needed.

**How fresh is the data?** Live: every run reads the source at that moment. Schedule it to keep a dataset up to date.

**Why did I get fewer contacts than the maximum?** The source had fewer matching contacts, or duplicates were removed. You only pay for contacts delivered.

### Limitations

- **No SMTP, no mailbox verification, no catch-all detection.** `deliverable_domain` means the domain publishes MX records, not that the person's mailbox exists.
- Candidates are **guesses based on patterns**. A `high` label means the address is published on the company's site, which is strong evidence but not a delivery guarantee.
- The website scan reads a few public pages with a plain HTTP client. Sites that block bots or render everything with JavaScript may give no evidence (`websiteError` tells you).
- The tool does not search LinkedIn, social networks or leaked databases, and it never uses personal data you did not provide.
- Email addresses are personal data in many jurisdictions. Use the results lawfully (GDPR, CAN-SPAM, LGPD) and respect opt-outs.

### Support

Found a bug, or need a new field, source or filter? Open an issue in the **Issues** tab; we usually answer within 24 hours. Custom data work: datadesk.io@proton.me.

### Use with AI agents (MCP)

This Actor works as a tool for AI agents through the [Apify MCP server](https://mcp.apify.com). Add it to Claude, ChatGPT, Cursor, VS Code or any MCP client:

`https://mcp.apify.com/?tools=datadesk-io/email-finder-by-name-domain`

Sign in with OAuth or `Authorization: Bearer <APIFY_TOKEN>`. Local stdio config:

```json
{"mcpServers": {"email-finder": {"command": "npx", "args": ["-y", "@apify/actors-mcp-server", "--tools", "datadesk-io/email-finder-by-name-domain"], "env": {"APIFY_TOKEN": "<token>"}}}}
```

**What goes in → what comes out:** Input: a list of people (first name, last name, company domain) and/or email addresses to check. Output: one JSON row per person with the best email candidate, ranked alternatives, a confidence label with its evidence, and domain checks (MX, disposable, typo). Every input is optional and every output field is documented. The agent should treat results as candidates, not verified mailboxes (`smtpVerified` is always `false`).

**Example prompt:** "Find the likely work email for Maria Garcia at shopify.com and tell me how confident the match is." Use `maxResults` to cap the cost of one call.

# Actor input Schema

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

List of people to find. Each item is {"firstName","lastName","domain"} (or {"fullName","domain"}), or a string like "Jane Doe, hubspot.com". Leave empty and fill only 'Emails to check' to just validate addresses.

## `emailsToCheck` (type: `array`):

Optional addresses to validate at domain level: syntax, MX, disposable, free provider, role-based, typo suggestion. It does NOT check that the mailbox exists.

## `candidatesPerContact` (type: `integer`):

How many ranked email candidates to return per person (the best goes in 'email', the rest in 'alternatives').

## `scanWebsite` (type: `boolean`):

Reads a few pages of the company site (home, contact, about, team) to find published addresses. They raise the confidence of a matching pattern. Turn off for faster, pattern-only results.

## `maxPagesPerDomain` (type: `integer`):

Pages to read per company domain when scanning the website.

## `includeFailedRows` (type: `boolean`):

Also output contacts whose domain is invalid, has no MX or is disposable, with the reason. Those rows are never charged.

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

Hard cap on charged rows for the whole run. Never exceeded.

## Actor input object example

```json
{
  "contacts": [
    {
      "firstName": "Jane",
      "lastName": "Doe",
      "domain": "hubspot.com"
    },
    {
      "firstName": "John",
      "lastName": "Smith",
      "domain": "basecamp.com"
    },
    {
      "firstName": "Maria",
      "lastName": "Garcia",
      "domain": "shopify.com"
    }
  ],
  "candidatesPerContact": 3,
  "scanWebsite": true,
  "maxPagesPerDomain": 4,
  "includeFailedRows": false
}
```

# Actor output Schema

## `overview` (type: `string`):

One row per contact (or checked email) with the best email candidate, alternatives and domain checks.

# 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 = {
    "contacts": [
        {
            "firstName": "Jane",
            "lastName": "Doe",
            "domain": "hubspot.com"
        },
        {
            "firstName": "John",
            "lastName": "Smith",
            "domain": "basecamp.com"
        },
        {
            "firstName": "Maria",
            "lastName": "Garcia",
            "domain": "shopify.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datadesk-io/email-finder-by-name-domain").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 = { "contacts": [
        {
            "firstName": "Jane",
            "lastName": "Doe",
            "domain": "hubspot.com",
        },
        {
            "firstName": "John",
            "lastName": "Smith",
            "domain": "basecamp.com",
        },
        {
            "firstName": "Maria",
            "lastName": "Garcia",
            "domain": "shopify.com",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("datadesk-io/email-finder-by-name-domain").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 '{
  "contacts": [
    {
      "firstName": "Jane",
      "lastName": "Doe",
      "domain": "hubspot.com"
    },
    {
      "firstName": "John",
      "lastName": "Smith",
      "domain": "basecamp.com"
    },
    {
      "firstName": "Maria",
      "lastName": "Garcia",
      "domain": "shopify.com"
    }
  ]
}' |
apify call datadesk-io/email-finder-by-name-domain --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datadesk-io/email-finder-by-name-domain"
        }
    }
}
```

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/Ah3XxnfIqUDvMuVDr/builds/j6rIeDO9bYJzn2pZN/openapi.json
