# Email Pattern Finder — Name + Domain to Business Email (`haketa/email-pattern-finder`) Actor

Find business emails from a person's name and company domain, in bulk. Generates and ranks every email format (first.last@, flast@, first@…), learns a company's exact pattern from one known address, and validates domains via MX. Keyless email finder for lead gen, sales and outreach.

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

## Pricing

from $2.00 / 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.
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 Pattern Finder — Name + Domain to Email

Turn a person's **name and company domain into the most likely business email address** — in bulk. Give the Actor a list like `Jane Doe, acme.com` and for each person it returns the **best-guess email, the format used, a confidence score, MX validation and a full set of ranked alternatives**.

And the part that makes it actually accurate: feed it **one known address per company** (e.g. `John Smith, jsmith@acme.com`) and it **reverse-engineers that company's exact email format** and applies it to everyone else at the same domain.

**Keyless**, DNS-based and built for scale — the missing link that turns a plain list of names into a reachable, outreach-ready email list.

***

### What this Actor does

You have names and the companies they work at. What you *need* are their email addresses. This Actor bridges that gap.

For every person on your list it:

- **Parses the name** into first / last (handles middle names, accents and unicode — `José Müller` → `jose.muller`).
- **Generates every realistic email format** for that person at the company domain — `first.last@`, `flast@`, `first@`, `f.last@`, `firstlast@` and more.
- **Ranks the candidates** by how common each format is in the real world, so the single most likely address is at the top.
- **Validates the domain via MX** — confirming the company can actually receive mail — and scores dead domains near zero.
- **Learns the exact pattern** when you provide a known sample, lifting accuracy from a statistical guess to a high-confidence match.

The result is a clean table: one row per person, the most likely email, a confidence score you can filter on, and every alternative in case you want to try variations.

***

### Why use it

- **You already have the names.** From a conference attendee list, a scraped org chart, a CRM export, a webinar sign-up sheet, a list of authors, speakers, founders or decision-makers — you have names and companies but no emails. This fills them in.
- **Pattern learning = real accuracy.** Most "email guessers" throw the statistically common format and hope. Give this one a single confirmed address per company and it locks onto that company's real format for everyone else — the same trick the expensive tools use.
- **Confidence you can filter.** Every row carries a 0–100 score and a `high` / `medium` / `low` label, so you can keep only the addresses you trust before you send a single message.
- **MX validation built in.** Domains that can't receive mail are flagged, so you don't waste sends (or hurt your sender reputation) on dead companies.
- **Keyless and cheap.** No API keys, no third-party credits, no per-lookup fees. It runs over DNS and scales from a handful of names to hundreds of thousands.
- **Bulk-first.** Paste thousands of `name, domain` lines and get thousands of ranked emails back, fast.

***

### Who it's for

- **Sales & SDR teams** building outbound lists — you know *who* you want to reach at a target account, you just need the address.
- **Recruiters & sourcers** reaching candidates and hiring managers whose names you already have.
- **Founders & marketers** doing cold outreach, partnership and BD, PR and link-building.
- **Agencies** turning prospect and lead lists into contactable email lists for clients.
- **Researchers & data teams** enriching any dataset that has people + companies but no contact column.

***

### Quick start

#### Run it in the console (no code)

1. In **People (name + company)**, paste one person per line as `Full Name, company.com`:

   ```
   Jane Doe, acme.com
   John Appleseed, example.io
   Maria García, globex.com
   ```

2. *(Optional but recommended)* In **Known emails — learn the pattern**, add one confirmed address per company:

   ```
   Sarah Lee, slee@acme.com
   ```

   Now everyone at `acme.com` is generated using the `flast` format with high confidence.

3. *(Optional)* If everyone is at the same company, skip the domains above and set **Company domain** once.

4. Click **Start**, then export as **JSON, CSV, Excel or HTML**, or push straight to Google Sheets, a webhook or your database.

Leave everything empty to run a **built-in demo** and see exactly what the output looks like before you load your own list.

#### Run it via API (Python)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run_input = {
    "people": [
        "Jane Doe, acme.com",
        "John Appleseed, example.io",
    ],
    "knownEmails": ["Sarah Lee, slee@acme.com"],
    "verifyMx": True,
}

run = client.actor("YOUR_USERNAME/email-pattern-finder").call(run_input=run_input)

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["fullName"], "→", row["email"], f"({row['confidence']}% {row['confidenceLabel']})")
```

#### Run it via API (JavaScript)

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('YOUR_USERNAME/email-pattern-finder').call({
    people: ['Jane Doe, acme.com', 'John Appleseed, example.io'],
    knownEmails: ['Sarah Lee, slee@acme.com'],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((r) => console.log(`${r.fullName} → ${r.email} (${r.confidence}% ${r.confidenceLabel})`));
```

***

### Input

| Field | Type | Description |
|---|---|---|
| **People (name + company)** | array | One person per line, as `Full Name, company.com`. Drop the domain if you set **Company domain** below. Empty = demo. |
| **Company domain** | string | A single domain applied to every person who has no domain on their own line. Handy when the whole list is one company. |
| **Known emails — learn the pattern** | array | One confirmed address per company as `Full Name, their.email@company.com`. The Actor learns that company's exact format and applies it to the rest. The single highest-impact input. |
| **Validate domain via MX** | boolean | Check each domain has mail servers. Addresses on domains that can't receive mail are flagged and scored near zero. Default: on. |
| **Include all candidate emails** | boolean | Also output every alternative format, ranked — not just the top guess. Default: on. |
| **Speed (parallel checks)** | integer | How many people to process at once. Default 50. |
| **Proxy configuration** | object | Apify Proxy. Off by default — the Actor runs over DNS and doesn't need one. |

#### Input formats accepted

The **People** field is flexible. All of these work:

```
Jane Doe, acme.com            ← name + domain on the line
Jane Doe                      ← name only (uses "Company domain")
Jane Doe | acme.com           ← pipe separator also works
```

Via API you can also pass objects: `{ "fullName": "Jane Doe", "domain": "acme.com" }` or `{ "firstName": "Jane", "lastName": "Doe", "company": "acme.com" }`.

***

### Output

One record per person. Example:

```json
{
  "fullName": "Jane Doe",
  "firstName": "Jane",
  "lastName": "Doe",
  "domain": "acme.com",
  "company": "acme",
  "email": "jdoe@acme.com",
  "pattern": "flast",
  "confidence": "92",
  "confidenceLabel": "high",
  "method": "pattern-learned",
  "hasMx": "true",
  "mxHost": "aspmx.l.google.com",
  "isFreeProvider": "false",
  "isRoleBased": "false",
  "allCandidates": "jdoe@acme.com; jane.doe@acme.com; jane@acme.com; janedoe@acme.com; j.doe@acme.com; ...",
  "candidateCount": "12",
  "scrapedAt": "2026-10-08T12:00:00.000Z"
}
```

| Field | Description |
|---|---|
| `fullName` / `firstName` / `lastName` | The person's name as provided and parsed. |
| `domain` / `company` | The company domain and a short company slug. |
| `email` | **The single most likely email address.** |
| `pattern` | The format used — `first.last`, `flast`, `first`, `f.last`, `firstlast`, … |
| `confidence` | Score from 0 to 100. |
| `confidenceLabel` | `high` / `medium` / `low` — easy to filter on. |
| `method` | `pattern-learned` (from your known sample) or `statistical` (from format frequency). |
| `hasMx` / `mxHost` | Whether the domain can receive mail, and its primary mail server. |
| `isFreeProvider` | Whether the domain is a free provider (gmail, outlook, yahoo…). |
| `isRoleBased` | Whether the best guess is a role address (info@, sales@…). |
| `allCandidates` / `candidateCount` | Every alternative email, ranked, and how many there are. |
| `scrapedAt` | ISO timestamp. |

Every field is always present — this is a compute-based **email finder**, so it never depends on scraping a remote page that might hide a value. `mxHost` is empty only when a domain has no mail servers, and `allCandidates` / `candidateCount` are populated only when **Include all candidate emails** is on.

***

### 🔁 Pagination & scale

There's **no pagination to configure** here, and that's by design. Unlike a web scraper that crawls page after page of a remote result set, this **email finder** is a bulk compute step: it returns **exactly one record per person on your input list**. Give it 10 names and you get 10 ranked emails; give it 200,000 `name, domain` lines and you get 200,000 records back.

Scale and speed come from two things:

- **Parallel processing.** The **Speed (parallel checks)** input controls how many people are generated and MX-validated at once (default 50, up to 200). Higher values finish large lists faster.
- **Per-domain MX caching.** DNS MX lookups are cached per domain for the whole run, so a list with thousands of people at the same company validates the domain **once** — lists concentrated at a few companies are especially quick.

There is no hard cap on how many names you can process in a single run — it scales from a handful to hundreds of thousands, limited only by your input size and the run's memory/time. Results stream to the dataset as they're generated, so you can start reading partial output before the run finishes.

***

### How confidence works

The Actor is honest about certainty, so you can decide what to trust:

- **`pattern-learned` (high, ~92).** You gave a confirmed address for that company, so the format is known — only the name varies. This is as good as a guess gets without sending mail.
- **`statistical` (medium, up to ~68).** No sample for that company, so the Actor uses the most common real-world format (`first.last@` leads, followed by `flast@`). Reliable as a first attempt, but verify before high-stakes sends.
- **No MX (low, near 0).** The domain has no mail servers — the address almost certainly can't be reached, whatever the format.

**Tip:** for best results, provide one known email per target company. One confirmed address unlocks the whole company at high confidence.

***

### Email formats (patterns) supported

Ranked by how common they are in the real world:

| Pattern | Example for *Jane Doe @ acme.com* |
|---|---|
| `first.last` | jane.doe@acme.com |
| `flast` | jdoe@acme.com |
| `firstlast` | janedoe@acme.com |
| `f.last` | j.doe@acme.com |
| `first` | jane@acme.com |
| `first_last` | jane_doe@acme.com |
| `firstl` | janed@acme.com |
| `last.first` | doe.jane@acme.com |
| `first-last` | jane-doe@acme.com |
| `last` | doe@acme.com |
| `lastfirst` | doejane@acme.com |
| `fl` | jd@acme.com |

With **Include all candidate emails** on, every one of these is in `allCandidates`, ranked — so if the top guess bounces, your next-best options are right there.

***

### 🔗 Related scrapers

This Actor is the **generation** step of a complete email workflow. Pair it with these companion Actors to go from raw company names all the way to a verified, send-ready list:

- [**Bulk Company Name to Domain Finder**](https://apify.com/haketa/bulk-company-domain) — the step *before* this one. When you only have company names (not domains), resolve each name to its real website domain first, then feed the domains here.
- [**Bulk Email Verifier**](https://apify.com/haketa/bulk-email-verifier) — the step *after* this one. Syntax-, MX- and disposable-check the generated addresses to clean your list before you send.
- [**Email & Contact Extractor**](https://apify.com/haketa/email-extractor) — a complementary source: finds emails, phones and socials that are *already published* on company websites.
- [**Email Finder & Verifier (B2B Email Lookup)**](https://apify.com/haketa/email-finder-verifier) — an all-in-one find-and-verify lookup when you want generation and verification in a single pass.
- [**Podcast Host Contact Scraper**](https://apify.com/haketa/podcast-contact-scraper) — pull host names, emails, websites and socials from podcasts for outreach lists.
- [**Bulk Phone Number Validator**](https://apify.com/haketa/bulk-phone-validator) — enrich the same contact records with E.164-formatted, carrier- and line-type-validated phone numbers.

Together: **company → domain → generated email → verified, send-ready list.**

***

### Tips for the best results

- **Give one known email per company.** It's the single biggest accuracy lever. Even one confirmed address per target domain turns guesses into high-confidence matches for the whole company.
- **Filter by `confidenceLabel`.** Keep `high` for important sends; use `medium` for lighter, higher-volume outreach.
- **Keep `allCandidates`.** If the top pick bounces, the ranked alternatives are already there — no re-run needed.
- **Mind free providers.** When the domain is gmail/outlook/yahoo, there's no company format to infer — those rows are flagged with `isFreeProvider`.
- **De-dupe your input** for cleaner billing and output.

***

### Frequently asked questions

**Does it send email to verify addresses?**
No. It validates the *domain* via MX records (DNS) and scores each guess by format frequency. It does not open SMTP connections or send probing mail — port 25 is blocked on most clouds and mailbox probing is unreliable and abusive anyway. For mailbox-level checks, run the output through a dedicated verifier.

**How accurate is it?**
With a known sample per company (`pattern-learned`), very accurate — the format is known, only the name changes. Without one (`statistical`), the top guess is right a large share of the time because a handful of formats dominate real-world business email, but you should treat these as best-guesses and verify before high-stakes sends.

**Do I need an API key or paid credits?**
No. It's keyless and runs over DNS. No Hunter, Apollo, Clearbit or similar account required.

**What if I only have company names, not domains?**
Resolve names to domains first (a company-to-domain step), then feed the domains here.

**Can it handle non-English names and accents?**
Yes. Names are normalized to ASCII (`José` → `jose`, `Müller` → `muller`) before building addresses.

**What languages / regions does it support?**
Any — it's name + domain based, so it works for companies and people worldwide.

**How fast is it and how does it scale?**
It processes many people in parallel and caches MX lookups per domain, so large lists at one company are especially quick. It scales from a few names to hundreds of thousands.

***

### Legal & responsible use

This Actor generates *likely* email addresses from names and public company domains and checks whether the domain can receive mail. It does not access private data. You are responsible for using the output in compliance with applicable laws and regulations (such as **GDPR**, **CAN-SPAM** and **CASL**) and with the terms of any service you contact — including honoring opt-outs and only sending messages you're permitted to send. Use it for legitimate business outreach, not spam.

***

### Export & integrate

Results are available as **JSON, CSV, Excel, HTML** and via the **Apify API**. Push them to **Google Sheets**, a **webhook**, **Make**, **Zapier**, or straight into your **CRM** or database. Schedule runs to keep a lead list continuously enriched.

# Actor input Schema

## `people` (type: `array`):

One person per line. Use the form `Full Name, company.com` (e.g. `Jane Doe, acme.com`). If every person is at the same company, you can drop the domain here and set it once in **Company domain** below. Leave this empty to run a quick demo and see exactly what the output looks like.

## `domain` (type: `string`):

A single company domain applied to every person who has no domain on their own line. Handy when your whole list works at one company — e.g. `acme.com`.

## `knownEmails` (type: `array`):

The most powerful option. Give one real, confirmed address per company as `Full Name, their.email@company.com` (e.g. `John Smith, jsmith@acme.com`). The Actor reverse-engineers that company's exact format and applies it to everyone else at the same domain with high confidence.

## `verifyMx` (type: `boolean`):

Check that each company domain actually has mail servers (MX records). Addresses on domains that cannot receive mail are flagged and scored near zero.

## `includeAllCandidates` (type: `boolean`):

Also output every alternative email format (ranked), not just the single most likely one. Useful when you want to try a few variations.

## `maxConcurrency` (type: `integer`):

How many people to process at the same time. Higher is faster. The default works well for most lists.

## `proxyConfiguration` (type: `object`):

Apify Proxy. Off by default — this Actor runs over DNS and does not need a proxy.

## Actor input object example

```json
{
  "people": [
    "Tim Cook, apple.com",
    "Satya Nadella, microsoft.com",
    "Sundar Pichai, google.com",
    "Marc Benioff, salesforce.com"
  ],
  "knownEmails": [],
  "verifyMx": true,
  "includeAllCandidates": true,
  "maxConcurrency": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `fullName` (type: `string`):

Person's name as provided

## `firstName` (type: `string`):

Parsed first name

## `lastName` (type: `string`):

Parsed last name

## `domain` (type: `string`):

Company domain

## `company` (type: `string`):

Company slug from domain

## `email` (type: `string`):

Most likely email address

## `pattern` (type: `string`):

Format used (first.last, flast…)

## `confidence` (type: `string`):

Confidence score 0-100

## `confidenceLabel` (type: `string`):

high / medium / low

## `method` (type: `string`):

pattern-learned / statistical

## `hasMx` (type: `string`):

Domain can receive mail

## `mxHost` (type: `string`):

Primary mail server

## `isFreeProvider` (type: `string`):

gmail/yahoo/outlook…

## `isRoleBased` (type: `string`):

Role address (info@…)

## `allCandidates` (type: `string`):

Ranked alternative emails

## `candidateCount` (type: `string`):

Number of candidates

## `scrapedAt` (type: `string`):

Timestamp

# 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 = {
    "people": [
        "Tim Cook, apple.com",
        "Satya Nadella, microsoft.com",
        "Sundar Pichai, google.com",
        "Marc Benioff, salesforce.com"
    ],
    "knownEmails": [],
    "maxConcurrency": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/email-pattern-finder").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 = {
    "people": [
        "Tim Cook, apple.com",
        "Satya Nadella, microsoft.com",
        "Sundar Pichai, google.com",
        "Marc Benioff, salesforce.com",
    ],
    "knownEmails": [],
    "maxConcurrency": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("haketa/email-pattern-finder").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 '{
  "people": [
    "Tim Cook, apple.com",
    "Satya Nadella, microsoft.com",
    "Sundar Pichai, google.com",
    "Marc Benioff, salesforce.com"
  ],
  "knownEmails": [],
  "maxConcurrency": 50
}' |
apify call haketa/email-pattern-finder --silent --output-dataset

```

## MCP server setup

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

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/ldoCfBuQqNLFrjrbr/builds/aZhgJLbSUAp2308s3/openapi.json
