# Email Pattern Finder (`accountable_eel/email-pattern-finder`) Actor

Generate ranked, likely email address candidates for a person from their name and company domain, based on well-known corporate email conventions. Confirms only that the domain accepts mail (MX/A record) — never claims mailbox-level verification, which no HTTP-only tool can honestly provide.

- **URL**: https://apify.com/accountable\_eel/email-pattern-finder.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 successful lookups

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

You give this actor a list of people — a name plus their company's domain. For each one, it generates a ranked set of likely email address candidates using well-known corporate email conventions (`first.last@`, `flast@`, `firstlast@`, and the rest), and confirms the domain can actually receive mail via a live MX/A-record check. You get back the most likely address, the full ranked candidate list, and which pattern won, as one flat row per person.

This is a **pattern generator with a mail-route check, not a mailbox verifier.** Confirming a specific address is a real, live mailbox requires an SMTP `RCPT TO` probe on port 25 — outbound port 25 is blocked on essentially every cloud/serverless runtime, Apify's included, to prevent spam abuse. Nothing here pretends otherwise: `mailboxVerified` is always `false`. What this actor honestly delivers is the same first step every commercial "email finder" tool starts from — a confident, ranked guess plus proof the domain isn't a dead end — without charging for a domain that can't receive mail at all.

### Who it's for

The accountable\_eel catalogue sells company intelligence columns for outbound. Each actor takes a list of domains or company identifiers and returns one flat, stably-named row per input — firmographics, registry IDs, tech stack, email route, hiring activity — the shape a Clay table, an n8n workflow, or an AI agent can consume without post-processing. Pricing is pay-per-event: a few tenths of a cent for a row that actually resolved, and nothing for a miss, so a list that doesn't enrich costs you next to nothing. This actor reuses this portfolio's own `email-deliverability-check` mail-route logic verbatim rather than re-deriving it, so the two stay consistent on what "this domain accepts mail" means.

This one is for outbound prospecting specifically: you have a decision-maker's name and their company's domain (from a job posting, a LinkedIn profile, a company's team page) and need a best-guess email address to reach them at, before running it through a separate deliverability/verification step.

### Why this one

- **Never charged for a domain that can't receive mail.** The MX/A-record check runs before any candidate is billed — a domain with no mail route is a free, honest miss, not a wasted guess.
- **Ranked, not just listed.** Candidates come back ordered by real-world pattern frequency, with a single `mostLikelyEmail` pick, not an unordered dump you have to re-rank yourself.
- **Honest about what it can't confirm.** `mailboxVerified` is always `false` — this actor will never claim a specific mailbox exists, because no HTTP-only tool honestly can.
- **Runs as a batch.** Paste a whole list of names and domains from a lead list or a scraped team page, get back a dataset instead of guessing each address by hand.
- **Handles real-world name mess.** Apostrophes, hyphens, and accented characters (`O'Brien`, `Renée`, `Smith-Jones`) sanitize into the plain local-part real mail systems actually use, not a broken guess.
- **Field names don't move between runs.** The output schema is frozen — `mostLikelyEmail` is always `mostLikelyEmail` — so a Clay table or an agent's tool call built against it today still works against it next month.

### What you get

One row per input person. Every field below is present on a `found: true` row; on a miss, only `query`, `found`, `status`, `message`, and `scrapedAt` are set.

| Field | Type / format | Description |
|---|---|---|
| `query` | string | The input line exactly as you submitted it. |
| `found` | boolean | `true` if the domain has a confirmed mail route and at least one candidate was generated, `false` otherwise. Never charged when `false`. |
| `status` | string | `OK` on a hit; `NOT_FOUND` or `BAD_FORMAT` on a miss. |
| `firstName` | string | The first name as submitted. |
| `lastName` | string | The last name as submitted. |
| `domain` | string | The company domain, lowercased. |
| `hasMailRoute` | boolean | Always `true` on a hit — the domain has an MX or, failing that, an A record. |
| `mostLikelyEmail` | string | The single top-ranked candidate address. |
| `mostLikelyPattern` | string | Which pattern produced it (e.g. `first.last`, `flast`). |
| `candidateEmails` | array of strings | Every ranked candidate, most-likely first. |
| `candidateCount` | number | How many distinct candidates were generated (duplicates from short names, e.g. `first` and `flast` colliding, are removed). |
| `mailboxVerified` | boolean | Always `false` — see the honesty note above. Never becomes `true`; it exists so a buyer's pipeline can filter on it deliberately rather than assume verification happened. |
| `scrapedAt` | string (ISO 8601) | Timestamp of the mail-route check for this row. |
| `message` | string (miss rows only) | Human-readable reason for a miss. |

### Price

$4 per 1,000 people + domains, plus a $0.00005 start fee. Misses (`found:false`) are never charged.

$4 per 1,000 people, plus a $0.00005 start fee — the same rate as this portfolio's other validator-class actors (`email-deliverability-check`, `iban-bic-validator`). A domain with no mail route is never charged.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `people` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~email-pattern-finder/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"people":["Jane Doe, acme.com"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

1) Paste one person per line as `First Last, domain.com` (an `@` also works instead of the comma). A first and last name are both required — a single name (a mononym, or a first-name-only lead) doesn't have enough signal to rank patterns against and comes back `BAD_FORMAT`.
2) Press Start. Each line becomes one dataset row, tagged `found: true` or `found: false`.
3) Use `mostLikelyEmail` as your first guess, or take the whole `candidateEmails` list into a separate deliverability check (this portfolio's own `email-deliverability-check`, or your own SMTP-capable verifier) before sending anything.
4) Export the dataset as CSV/JSON, or pull it via the API — `onlyFound` and `columns` (see the integrations section below) let you trim the response before it lands in your table.

### Input

```json
{
  "people": [
    "Jane Doe, acme.com"
  ]
}
```

One per line: "First Last, domain.com". A ranked set of likely email address candidates is generated for each — confirmed only at the mail-route level (the domain accepts mail), never mailbox-verified. Accepted formats: Jane Doe, acme.com, Jane Doe @ acme.com.

### Sample output

| query | found | status | firstName | lastName | domain | hasMailRoute | mostLikelyEmail | mostLikelyPattern | candidateEmails | candidateCount | mailboxVerified | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Jane Doe, google.com | true | OK | Jane | Doe | google.com | true | jane.doe@google.com | first.last | \["jane.doe@google.com","jane@google.com","janedoe@google.com","jdoe@google.com","jane\_doe@google.com","doe.jane@google.com","doejane@google.com","doe@google.com"] | 8 | false | 2026-08-31T06:30:27.572Z |

A miss looks like: `{"query": "Not A Real Domain, thisdomaindoesnotexist12345zz.com", "found": false, "status": "NOT_FOUND", "message": "This domain has no mail route (no MX or A record) — it can't receive email, so no pattern was generated.", "scrapedAt": "2026-08-24T16:54:25.233Z"}` — no charge, one row.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~email-pattern-finder/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"people":["Jane Doe, acme.com"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~email-pattern-finder/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"people":["Jane Doe, acme.com"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~email-pattern-finder/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"people":["{{person + domain}}"]}`, mapping the row's person + domain into the `people` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Email Pattern Finder | Apify" — the agent will find and run this actor.

### Tips

- Feed `mostLikelyEmail` alone into a downstream deliverability check first — it's right often enough (`first.last@` and a bare first name are the two most common corporate defaults) to be worth checking before spending on the full candidate list.
- Use `columns` to request only `mostLikelyEmail` if you don't need the full ranked list — it trims the response without changing what you're charged.
- A domain with no MX or A record is a strong signal the domain itself is dead or was typed wrong — worth double-checking the source data before assuming the person doesn't exist.
- This actor does not detect which pattern a specific company actually uses from known examples — it ranks by general frequency across companies. If you already have one or two confirmed real addresses at a target company, that's stronger evidence of their actual convention than this actor's ranking.

### vs. alternatives

For a one-off guess, most people just type `first.last@company.com` by hand. This actor exists for the batch case and the honesty guarantee — a whole list in, a dataset out, with the mail-route check and pattern ranking already done, and nothing billed for a dead domain.

| | What it costs | What you get | Trade-off |
|---|---|---|---|
| **This actor** (`email-pattern-finder`) | $0.004 per resolved row (FREE tier), $0.00005 actor start, nothing for a dead domain | Ranked candidate emails plus a confirmed mail-route check, per person | Never claims mailbox-level verification — pair with a deliverability check before sending. |
| **A commercial email-finder platform** | $0.03–$0.10+ per lookup in credits, often with a monthly seat | Pattern-plus-verification in one step, sometimes with a confidence score from a proprietary database of confirmed hits | Pricier per row, usually bundled into a broader enrichment seat rather than callable as one column. |
| **Guessing by hand** | Free, your time | The same `first.last@` default most people reach for anyway | No batch mode, no mail-route check — you find out a domain is dead only when your email bounces. |

Prices for third-party tools are their published list prices as of August 2026 and are not tracked here — check the vendor before relying on the comparison.

### FAQ

**Does this confirm a specific email address is real?**
No — it confirms the domain can receive mail and ranks candidate addresses by how common each pattern is. Confirming one specific mailbox exists requires an SMTP probe, which is blocked on this actor's runtime (and most cloud platforms) to prevent spam abuse. `mailboxVerified` is always `false`.

**What happens when a domain can't receive mail at all?**
It's a clean miss: `found: false`, `status: "NOT_FOUND"`, and you're never charged. No candidates are generated for a domain that can't receive mail — there'd be nothing honest to guess at.

**Why do I need both a first and last name?**
The ranked pattern list (`first.last`, `flast`, `last.first`, and the rest) needs both parts to build most of its candidates. A single name doesn't carry enough signal, so it comes back `BAD_FORMAT` rather than a degraded guess.

**Is this cached, or a live check every run?**
Live. Every row runs a fresh MX/A-record check when the actor executes — there's no stored snapshot from a previous run.

**Can this detect the actual pattern a specific company uses, not just a general guess?**
Not currently — it ranks by general cross-company frequency. If you already have a couple of confirmed real addresses at the target company, that's stronger signal than this actor's ranking and should take priority.

**Does this actor use a proxy?**
No — the only network call is a DNS-over-HTTPS query to Cloudflare's public resolver, not a scrape of any target site, so there's no anti-bot layer to route around.

**Can I schedule this to run automatically, or call it from an AI agent?**
Yes to both. Set up an Apify schedule for recurring lead-list enrichment, or call it via the Apify API from a script or n8n/Make workflow. It's also discoverable through the Apify MCP server, so an AI agent (Claude, Cursor, or any MCP client) can find and run it directly by name — see the integrations section above.

### Related actors

- [Email Deliverability Check](https://apify.com/accountable_eel/email-deliverability-check) — mail route, disposable-domain and role-address flags, and provider fingerprint for a specific email address (run this actor's candidates through it before sending).
- [Domain RDAP Lookup](https://apify.com/accountable_eel/domain-rdap-lookup) — registrar, registration and expiry dates for the same domains.
- [Company Domain Enrichment](https://apify.com/accountable_eel/company-domain-enrichment) — multi-source company intelligence fan-out from a single domain.

# Actor input Schema

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

One per line: "First Last, domain.com". A ranked set of likely email address candidates is generated for each — confirmed only at the mail-route level (the domain accepts mail), never mailbox-verified. Accepted formats: Jane Doe, acme.com, Jane Doe @ acme.com. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

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

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

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

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

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

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "people": [
    "Jane Doe, acme.com"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "columns": [
    "firstName",
    "lastName",
    "domain",
    "hasMailRoute",
    "mostLikelyEmail",
    "mostLikelyPattern",
    "candidateEmails",
    "candidateCount",
    "mailboxVerified"
  ],
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": 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 = {
    "people": [
        "Jane Doe, acme.com"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/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": ["Jane Doe, acme.com"],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/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": [
    "Jane Doe, acme.com"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/email-pattern-finder --silent --output-dataset

```

## MCP server setup

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