# Work Email Finder: Find Email by Name & Domain (No SMTP) (`data-done-right/work-email-finder`) Actor

Work email finder API: find email by name and company domain without sending any email or SMTP probing. Learns the email pattern from addresses the company publishes, checks MX records, returns the most likely work email with confidence and evidence. Email pattern finder mode for a domain.

- **URL**: https://apify.com/data-done-right/work-email-finder.md
- **Developed by:** [Data Done Right](https://apify.com/data-done-right) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 email founds

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

## Work Email Finder: Find Email by Name & Domain (No SMTP)

This **work email finder** takes a person's name and a company domain and returns the **most likely work email**, without sending any email and without SMTP probing. It reads the company's own public pages, learns which email format the company really uses (`first.last`, `flast`, `first`...), checks the domain's MX records, and then writes the person's name in that format. Use it to find work email addresses for lead lists, outreach and CRM enrichment. Every result says how sure it is (`high`, `medium`, `low`) and which published addresses it is based on.

The result is a **pattern-based likely address**. It is never verified: nothing is sent, no mail server is contacted, and `deliverabilityVerified` is always `false`. Verify addresses with your own tools before you send.

### How to find work email by name and company domain

1. Add people to **People**: first name, last name and the company domain (or website). A full name works too, and so does a website URL, which is turned into a domain.
2. Run it. Each distinct company domain is read once (a few public pages), however many people work there.
3. Download one row per person as JSON, CSV or Excel, or read it through the Apify API.

### What the work email finder does

- **Learns the format from public evidence.** It reads the homepage plus a few contact, team, about, careers or imprint pages, extracts the email addresses the company published (mailto links, visible text, simple obfuscations like `name [at] company [dot] com`), and works out the dominant local-part pattern.
- **Names are normalised first.** Accents (José, Müller, Søren, Łukasz), hyphens (Anne-Marie), particles (van, de la), titles and suffixes (Dr., Jr., PhD), `Last, First` order and nicknames in brackets are handled. Spelling variants are listed as alternates.
- **Checks the domain.** DNS MX lookup only (dnspython). A domain without MX records gets no address. A lookup that fails or times out is reported as unknown (`mxOk: null`), not as "no mail".
- **Rejects non-company domains.** gmail.com, outlook.com, yahoo.com and other free-mail or disposable domains return a clear reason instead of an address.
- **Always one row per person.** Even when nothing is found you get a row with a `reason`, so lists stay aligned. Use `inputIndex` or your own `id` to join back.
- **Polite and safe.** robots.txt is respected, requests are spaced out, a few pages per domain are read with short timeouts, and the crawler refuses localhost, private networks, link-local and cloud-metadata addresses, also through DNS tricks and redirects.

### Confidence levels

| Confidence | Meaning | Charged |
|---|---|---|
| `high` | The pattern is used by 2 or more personal addresses the company published on its own domain, or this exact address is published on its site. | yes |
| `medium` | Exactly 1 published personal address supports the pattern. | yes |
| `low` | A common-pattern guess (no usable evidence, role mailboxes only, or conflicting patterns). Marked clearly, with the reason. | no |
| `none` | No address: free-mail or disposable domain, domain without MX records, or unusable input. | no |

Role mailboxes (`info@`, `support@`, `sales@`, `hello@`, `press@`, `careers@`...) are never used as evidence for a personal pattern. Addresses on subdomains such as `lists.company.com` are listed but not used as evidence either, and a hyphen alone (`public-webapps@`) is not treated as a name pattern.

### Input example

```json
{
  "people": [
    {"firstName": "Jane", "lastName": "Doe", "domain": "acme.com", "id": "lead-1"},
    {"fullName": "Dr. Omar Haddad Jr.", "website": "https://www.acme.com/about"},
    "Anne-Marie Dubois | example.org"
  ],
  "maxPagesPerDomain": 6,
  "evidenceMode": "masked"
}
```

Each person accepts `firstName`, `lastName` (or `fullName`), `domain` (or `website` / `companyUrl`) and your own `id`. A string `"Name | domain"` also works.

### Output example

```json
{
  "inputIndex": 0, "id": "lead-1", "firstName": "Jane", "lastName": "Doe", "domain": "acme.com",
  "email": "jane.doe@acme.com", "confidence": "high", "pattern": "first.last", "publishedOnSite": false,
  "mxOk": true, "mxHosts": ["aspmx.l.google.com", "alt1.aspmx.l.google.com"], "mailProvider": "Google Workspace",
  "deliverabilityVerified": false,
  "alternates": [{"email": "jdoe@acme.com", "pattern": "flast"}, {"email": "jane@acme.com", "pattern": "first"}],
  "evidence": {"patternSupport": 3, "supportingAddresses": ["j***.s***@acme.com", "m***.g***@acme.com"],
               "pages": ["https://acme.com/contact", "https://acme.com/team"]},
  "pagesChecked": 4,
  "reason": "Pattern first.last is used by 3 published addresses on acme.com; the address is derived from the name, not confirmed to exist.",
  "charged": true, "error": null
}
```

- `email` is the best guess and `alternates` lists other likely addresses (other patterns, spelling variants, common patterns).
- `evidence.supportingAddresses` are masked by default (`j***.s***@acme.com`) so you can check the source page without copying other people's addresses. Set **Published addresses in the evidence** to `full` to see them as published.
- A **SUMMARY** record in the key-value store counts results by confidence and charged results.

### Email pattern finder: learn the format of a whole domain

Set **Mode** to `email-pattern` and list company domains. For each domain you get the dominant pattern, every pattern seen with its support, the published addresses (role mailboxes in full, personal ones masked unless you ask for full), the pages read, the MX hosts and the mail provider. Use it to prepare your own outreach lists, or to check a pattern before you run the people mode.

```json
{"mode": "email-pattern", "domains": ["acme.com", "https://www.example.org/contact"]}
```

### Work email finder API

Call it from the Apify API, Python, JavaScript, Make, Zapier or n8n. Example with the Apify Python client:

```python
from apify_client import ApifyClient

run = ApifyClient("<APIFY_TOKEN>").actor("data-done-right/work-email-finder").call(run_input={
    "people": [{"firstName": "Jane", "lastName": "Doe", "domain": "acme.com"}]})
for row in ApifyClient("<APIFY_TOKEN>").dataset(run["defaultDatasetId"]).iterate_items():
    print(row["email"], row["confidence"], row["pattern"])
```

### Use it in Claude, ChatGPT and Cursor

Connect Apify's MCP server (mcp.apify.com) and your AI assistant can look up work emails itself. For example: "find the likely work email for Jane Doe at acme.com and tell me how confident it is and why." The main input is `people`; `mode: "email-pattern"` with `domains` returns the learned format of a domain.

### How much does it cost

Pay per event, with **no per-run start fee**:

- **$0.002 per result** (`email-found`), charged only for `high` and `medium` confidence rows. `low` and `none` rows, unusable input and bad domains are free. 1,000 charged results cost about $2.
- **$0.003 per domain** (`domain-pattern`) in email-pattern mode, charged only when a pattern was learned from published addresses.

Paid Apify plans get 10% (Silver) to 20% (Gold and up) off. Other work email finders on Apify charge about $0.003 to $0.005 per found email; this one is priced below that because it verifies nothing and its low-confidence guesses are free. `maxPeople` caps the bill, and if you set a maximum spend per run the Actor stops cleanly when it is reached and keeps the results already delivered.

### Responsible use

- **You decide whether you may email someone.** Finding a likely address is not permission to use it. In the EU and UK, B2B outreach needs a documented legitimate interest (GDPR Art. 6(1)(f)) and a way to object; some countries require prior consent. In the US, CAN-SPAM requires honest headers, a physical address and a working opt-out. CASL (Canada) and other laws are stricter.
- **Respect opt-outs.** Keep a suppression list and honour unsubscribe requests and "do not contact" signals.
- **No harvesting of personal addresses.** The Actor does not build lists of addresses. It learns a company's format and applies it to people you name. Masked evidence is the default so the other people's published addresses are not copied out.
- **Business domains only.** Free-mail and disposable domains are rejected, so you do not guess private mailboxes.
- **Verify before sending.** A pattern guess can be wrong, and a wrong guess can reach a different real person. Run your own verification and keep bounce rates low.
- Only public pages are read, robots.txt is obeyed, and the crawl is slow and small on purpose.

### Honest limits

- **Not verified, ever.** No SMTP check, no mailbox probe, no catch-all detection. Outbound mail ports are unreliable on cloud platforms and probing mail servers is intrusive, so this Actor does neither. `deliverabilityVerified` is always `false`.
- **Evidence needs published addresses.** Many companies publish only role mailboxes (info@, support@). Then you get a `low` common-pattern guess, which is free and marked as a guess.
- **Compact addresses are ambiguous.** `jsmith@` could be `flast` or a nickname. It only counts as evidence when a name is printed next to it on the page.
- **Companies mix formats.** Large companies often use different formats per team or acquired brand, and aliases can exist next to the main address. The pattern describes the most common published format.
- **Names:** non-Latin names (for example Chinese or Arabic script) need a Latin spelling. Very common names may collide with another person at the same company.
- **Websites can block or hide emails.** JavaScript-only pages, forms instead of addresses and bot blocking mean less evidence. Cloudflare-protected addresses are decoded, other obfuscation is not.
- **Domains:** the subdomain you give is used as is (`eu.acme.com` is not shortened to `acme.com`). A domain with only an address record and no MX is treated as unable to receive mail.

### FAQ

#### How do I find a work email by name?

Give the first name, last name and company domain (or website). The Actor learns the company's email format from its public pages and writes the name in that format. Check `confidence` and `reason` to see how well it is supported.

#### Does it send emails or check the mailbox?

No. Nothing is sent, and no mail server is contacted. It does a DNS MX lookup and reads public web pages. That is why the results are labelled as likely addresses and never as verified.

#### Why do I get a `low` confidence result?

The company publishes no personal addresses that show a pattern (or the patterns conflict), so the Actor falls back to the most common format, `first.last`. These rows are free. Treat them as a starting point and verify them.

#### Can it find a personal Gmail or Outlook address?

No. Free-mail and disposable domains are rejected on purpose. It is a work email finder for company domains.

#### What is the email pattern finder mode?

It takes only domains and returns the format the company uses (for example `first.last`) with the published addresses it comes from, so you can see the evidence yourself.

#### What is the difference to email verifiers?

Verifiers contact the mail server to test a mailbox. This Actor does not, so it is cheaper and does not touch anyone's mail server, but it cannot tell you the address exists. A common workflow is to use this Actor to generate the likely address and your own verifier to confirm it.

#### Does it respect robots.txt?

Yes. A `Disallow` for the bot is obeyed, a Crawl-delay slows it down (up to 10 seconds), a 4xx on robots.txt means no restrictions, and a 5xx or an unreachable robots.txt means the site is skipped.

#### Are results charged when nothing is found?

No. Only `high` and `medium` rows are charged. Rows with `low` or `none` confidence and rows with unusable input are free, and every person still gets a row.

### Related tools

- **Company Enrichment API: Domain to Tech, Jobs & Socials**: add tech stack, jobs and social profiles to the same domains.
- **Hiring Signals: Tech Stack & Headcount Growth from Job Posts**: find companies that are hiring and growing.
- **Google Sheets Import & Export: Apify Data, No Login**: send the results to a Google Sheet.

# Actor input Schema

## `mode` (type: `string`):

find-email returns the most likely work email for each person in People. email-pattern takes only company domains (see Domains) and returns the email pattern learned for each domain, with the published addresses it is based on.

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

The people to find a work email for, as a JSON array. Each item needs a first name and last name (or one full name) and the company's domain or website. Keys: "firstName", "lastName" (or "fullName"), "domain" (or "website" / "companyUrl"), optional "id" that is echoed back. A string "Jane Doe | acme.com" also works. Example: \[{"firstName": "Jane", "lastName": "Doe", "domain": "acme.com"}, {"fullName": "Dr. Omar Haddad Jr.", "website": "https://www.acme.com/about"}]. Free-mail domains such as gmail.com are rejected with a reason.

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

Company domains or website URLs for email-pattern mode, one per line. Example: acme.com, https://www.example.org/contact. Ignored in find-email mode.

## `maxPeople` (type: `integer`):

Hard cap on how many people (find-email) or domains (email-pattern) are processed. Extra ones are skipped. Also caps your cost.

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

Public pages read per company domain: the homepage plus contact, team, about, careers or imprint pages found from it. A domain is read once, however many people share it. robots.txt requests are not counted.

## `concurrency` (type: `integer`):

How many different company domains are read at the same time. One domain is always read one request at a time.

## `delaySecs` (type: `integer`):

Pause between two requests to the same website. A longer Crawl-delay in the site's robots.txt (up to 10 s) wins.

## `timeoutSecs` (type: `integer`):

Time allowed for one page request, redirects included. Slow or unreachable sites are skipped, and the person still gets a result row.

## `evidenceMode` (type: `string`):

masked shows the addresses a pattern was learned from in a shape-preserving mask (j\*\*\*.d\*\*\*@acme.com) with the page URL, so you can check the source without copying other people's addresses. full shows them as published. Role mailboxes such as info@ are always shown in full.

## `maxAlternates` (type: `integer`):

How many other likely addresses (other patterns, spelling variants) to list next to the best one. 0 turns them off.

## `useNicknames` (type: `boolean`):

Also list alternates for common short names (Robert to bob, William to bill) and for nicknames written in brackets or quotes, such as Robert (Bob) Smith.

## Actor input object example

```json
{
  "mode": "find-email",
  "people": [
    {
      "firstName": "Ada",
      "lastName": "Example",
      "domain": "apify.com"
    },
    {
      "fullName": "Sam Sample",
      "website": "https://www.apify.com"
    }
  ],
  "maxPeople": 500,
  "maxPagesPerDomain": 6,
  "concurrency": 5,
  "delaySecs": 1,
  "timeoutSecs": 10,
  "evidenceMode": "masked",
  "maxAlternates": 4,
  "useNicknames": false
}
```

# Actor output Schema

## `results` (type: `string`):

One item per person (or per domain in email-pattern mode): likely work email, confidence, pattern, evidence and reason (all result items of this run, as JSON, CSV, Excel and more).

## `records` (type: `string`):

Key-value store records: the run summary (SUMMARY: counts by confidence, charged results, spending-limit flag).

# 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": [
        {
            "firstName": "Ada",
            "lastName": "Example",
            "domain": "apify.com"
        },
        {
            "fullName": "Sam Sample",
            "website": "https://www.apify.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("data-done-right/work-email-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": [
        {
            "firstName": "Ada",
            "lastName": "Example",
            "domain": "apify.com",
        },
        {
            "fullName": "Sam Sample",
            "website": "https://www.apify.com",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("data-done-right/work-email-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": [
    {
      "firstName": "Ada",
      "lastName": "Example",
      "domain": "apify.com"
    },
    {
      "fullName": "Sam Sample",
      "website": "https://www.apify.com"
    }
  ]
}' |
apify call data-done-right/work-email-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,data-done-right/work-email-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/yxNrnnFhmWyslegHW/builds/VPHiFt11RCLL41kvt/openapi.json
