# Email Finder by Name and Domain: Verified Work Email (`lyfe_tools/email-finder-by-name-domain`) Actor

Email finder by name and domain: get a person's work email address from their name and company domain. Candidates are ranked by pattern and the company's own site, checked by the catch-all verifier, and you pay only for addresses that were found and verified.

- **URL**: https://apify.com/lyfe_tools/email-finder-by-name-domain.md
- **Developed by:** [LYFE Offshore](https://apify.com/lyfe_tools) (community)
- **Categories:** Lead generation, Automation, MCP servers
- **Stats:** 3 total users, 3 monthly users, 83.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 email address 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

## Email Finder by Name and Domain: Verified Work Email

Find a person's work email address from their first name, last name and company domain. It returns the most
likely address, a score, the reasons, and ranked alternatives, and checks the best candidates with a catch-all
email verifier. **You are billed only for addresses that were found and verified** (verifier verdict `keep`), not
for attempts, guesses or unverified results.

### One run, real output

Four invented names, run for real. Two sit at `stripe.com`, whose public site shows the `first.last` address shape; one at `lyfeoffshore.com` (a project-owned domain with no such evidence); one at a domain that does not exist. The names are generic examples, not people, so these are **pattern inferences**, not addresses anyone confirmed.

**Locally, without verification** (pattern + website evidence only; nothing is billed):

| Person | Result | Score | Why |
|---|---|---|---|
| Maria Gonzalez at stripe.com | found `maria.gonzalez@stripe.com` | 0.65 | the site publishes `first.last` addresses |
| John Smith at stripe.com | found `john.smith@stripe.com` | 0.65 | same |
| Jan van der Berg at lyfeoffshore.com | not found; best guess `jan.vanderberg@lyfeoffshore.com` | 0.29 | compound surname, no format evidence, below the 0.60 threshold |
| Ana García-López at a non-existent domain | not found | 0 | "the domain does not exist in DNS": an explicit row, never a silent gap |

"Found" here means a likely address with unverified evidence. It is returned, but **only an address the verifier confirms with `keep` is billed**, so these two cost nothing in this run.

**With the verifier** (an earlier test run of the real verification engine on `lyfeoffshore.com`, three candidates per person):
all three names came back `test` (p = 0.69), so they were returned as best guesses, **not found and not billed**. The
verifier only says `keep` when it has real evidence, and a lone name on a domain with no other addresses to compare
against is often not enough. That is the intended behaviour: an unconvincing address costs you nothing.

### What it does

1. **Normalises the name.** Accents are folded (`Tomáš` → `tomas`, `Søren` → `soren`), German spellings tried as a variant (`Müller` → `muller`, `mueller`), compound surnames split (`van der Berg` → `vanderberg`, `berg`; `García-López` → `garcialopez`, `garcia`, `lopez`), double first names (`Jean-Pierre` → `jeanpierre`, `jean`).
2. **Checks the domain** over DNS: no domain, no MX, or a null MX (RFC 7505) ends in an explicit row with the reason.
3. **Reads the company's own site** (home, contact, about, team; robots.txt honoured; 8-second timeouts; only that one domain) to see which address *shape* it publishes (`first.last`, `flast`, …). Role addresses (`info@`, `sales@`) are ignored. If the exact address is published, it scores 0.97.
4. **Generates and ranks candidates** from twelve conventions (`first.last`, `flast`, `first`, `firstlast`, `f.last`, `first_last`, `firstl`, `last.first`, …).
5. **Verifies** the top *N* (default 3) per person through `lyfe_tools/catchall-email-verifier`, and returns the best one only if the verifier says `keep`. `test`, `review` and `discard` stay a best guess and are free.

### What it does not do

- **It does not guarantee an address exists.** Unverified scores are the share of the candidate weight, not a probability. The prior weights are rough orders of magnitude, not fitted to data. Only the verifier's score is calibrated, and on catch-all domains even that is a judgement, not a fact.
- **No SMTP probing.** Apify blocks outbound mail ports; verification works from DNS and name signals, which is what the verifier was measured on.
- **It does not read LinkedIn or any third-party site**, and it does not search the web for people. It only needs what you give it.
- It finds *work* addresses on company domains. Free-mail domains (gmail.com, …) make no sense as input.

### Input example

```json
{
  "people": [
    { "first_name": "Maria", "last_name": "Gonzalez", "domain": "stripe.com" },
    { "first_name": "Jan", "last_name": "van der Berg", "domain": "lyfeoffshore.com" }
  ],
  "verify": true, "verifyTop": 3, "minScore": 60, "inspectWebsite": true
}
```

`domain` may also be `company_domain`, `website`, `url` or an email at the company; a single `name` (or
`full_name`) column works too, or pass `inputDatasetId` to read the rows from another Actor's dataset.

### Output example

One row per person. This row is from a local run with verification off (so `verified` is false and nothing is
billed):

```json
{
  "firstName": "Maria", "lastName": "Gonzalez", "domain": "stripe.com",
  "email": "maria.gonzalez@stripe.com",
  "found": true, "score": 0.653, "status": "found",
  "verified": false, "billable": false,
  "pattern": "first.last",
  "reasons": ["pattern first.last (65% of candidate weight)"],
  "sitePublishedShape": "dot",
  "alternatives": [
    { "email": "m.gonzalez@stripe.com", "score": 0.115, "pattern": "f.last" },
    { "email": "gonzalez.maria@stripe.com", "score": 0.058, "pattern": "last.first" },
    { "email": "maria.g@stripe.com", "score": 0.038, "pattern": "first.l" },
    { "email": "mgonzalez@stripe.com", "score": 0.037, "pattern": "flast" }
  ]
}
```

| Field | Meaning |
|---|---|
| `email` | The address, or `null` when not found |
| `found` | With verification on: the best candidate scored at least `minScore` and the verifier said `keep`. With it off: the best candidate scored at least `minScore` |
| `billable` | `true` only when found and verified as `keep`; these are the rows you pay for |
| `verified` / `verifierAction` | Whether the verifier checked the best candidate, and its verdict (`keep`, `test`, `review`, `discard`) |
| `score` | 0 to 1. Unverified: share of the candidate weight. Verified: the verifier's probability |
| `pattern` | The address convention of the best candidate (`first.last`, `flast`, ...) |
| `reasons` | Why it scored as it did, or why the person failed |
| `alternatives` | Up to four other candidates with score and pattern |
| `bestGuess` | When not found: the top candidate, never billed |
| `sitePublishedShape` | The address shape seen on the company's own site, if any |
| `status` | `found`, `not_found`, `no_domain`, `no_name`, `domain_null_mx`, `domain_no_domain`, `domain_no_mail`, `domain_dns_error` |
| `firstName`, `lastName`, `domain`, `company` | The person as read from the input |

A `SUMMARY` record in the key-value store has the totals.

### Price, and what verification adds

Price per 1,000 by your Apify plan (Apify applies the right column automatically):

| | Free | Starter | Scale | Business and up |
|---|---|---|---|---|
| Email address found, per 1,000 | $5.00 | $2.40 | $2.00 | $1.60 |

No start fee. The figures below use the Free-plan price; on a paid plan the same run costs less.

| | Price |
|---|---|
| **Email address found and verified** | **$0.005** per address the verifier marks `keep`; everything else is free |

Platform usage is included. **Verification is billed separately, on your account:** with `verify` on, this Actor
starts the verifier once per run as your run, so you pay *its* price too: $0.0012 per candidate address on the Free plan
($0.0006 to $0.0009 on paid plans), no start fee. Only people whose domain passes the DNS check send candidates. At the default 3 candidates per
person:

| People | Verifier (billed by the verifier) | This Actor | Total |
|---|---|---|---|
| 1,000 | up to $3.60 | $0.005 per verified address, at most $5.00 | at most $8.60 |
| 10,000 | up to $36.00 | at most $50.00 | at most $86.00 |

The "at most" assumes every person is found and verified; you pay this Actor only for the ones that are. Fewer
candidates (`verifyTop`) lowers the verifier part, and with `verify: false` (or outside the platform) **nothing is
billed by this Actor at all**. A run's max-charge limit (`ACTOR_MAX_TOTAL_CHARGE_USD`) caps the number of people
processed.

### Use it from code or an AI agent

```bash
curl -X POST "https://api.apify.com/v2/acts/lyfe_tools~email-finder-by-name-domain/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"people": [{"first_name": "Maria", "last_name": "Gonzalez", "domain": "stripe.com"}], "verify": true}'
```

The response is the dataset rows as a JSON array. The Actor also works through the Apify MCP server
(https://mcp.apify.com), so AI agents such as Claude or Cursor can call it as a tool: an agent sends `people`
(`first_name`, `last_name`, `domain`) and reads `email`, `found`, `billable` and `reasons`; when `email` is null,
`bestGuess` is an unconfirmed candidate, not a found address.

### Part of a lead-list workflow

1. **Find: Email Finder by Name and Domain (this Actor)**
2. Clean: [Email List Cleaner](https://apify.com/lyfe_tools/email-list-cleaner)
3. Verify: [Catch-All Email Verifier](https://apify.com/lyfe_tools/catchall-email-verifier)
4. Check the domain: [SPF, DKIM & DMARC Checker](https://apify.com/lyfe_tools/domain-email-health-check)

Next step: pass this run's dataset ID as `inputDatasetId` to the
[Email List Cleaner](https://apify.com/lyfe_tools/email-list-cleaner). It reads the `email` column and skips
people without a found address for free. Each later step accepts the previous run's dataset in `inputDatasetId`
too. With `verify` on, found addresses here have already been through the verifier.

### FAQ

**How do I find someone's work email from their name and company?**
Send `first_name`, `last_name` and the company `domain`. The Actor builds candidates from twelve address
conventions, ranks them using the formats the company's own site publishes, and verifies the top ones.

**Do I pay for emails that are not found?**
No. This Actor bills only rows with `billable: true` (found and verified as `keep`). The verifier, when on, bills
its own per-candidate price for every person it checks.

**Is the email verified?**
With `verify` on, the top candidates go through the Catch-All Email Verifier, and only a `keep` counts as found.
There is no SMTP probing, so on catch-all domains the verdict is a calibrated judgement, not proof.

**Does it handle accents and compound surnames like van der Berg or García-López?**
Yes: accents are folded, German spellings tried as a variant, and compound surnames and double first names
split into several candidates.

**Does it search LinkedIn or the web?**
No. It reads only the company's own site (up to four pages, robots.txt honoured) and DNS.

Issues and questions: tools@lyfeoffshore.com.

# Actor input Schema

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

One object per person: first_name, last_name and domain (also accepted: company_domain, website, or a single name field). Accents and compound surnames (van der Berg, García-López) are handled.

## `inputDatasetId` (type: `string`):

Read the rows from an existing dataset in addition to the list above.

## `verify` (type: `boolean`):

Runs the top candidates per person through the Catch-All Email Verifier (lyfe_tools/catchall-email-verifier). That Actor runs on YOUR account and bills you its own price on top of this one: about $0.0012 per candidate address plus $0.01 per verifier run, so roughly $0.004 per person at 3 candidates. Only addresses verified as keep are billed by this Actor. Turn off for free, unverified guesses ranked by pattern and the company's website (nothing is billed).

## `verifyTop` (type: `integer`):

How many of the most likely addresses per person go to the verifier. Each one is billed by the verifier.

## `minScore` (type: `integer`):

Only addresses at or above this score are returned as found; of those, only the verified ones are billed. Lower finds more but guesses more.

## `inspectWebsite` (type: `boolean`):

Fetches up to four pages (home, contact, about, team) from the company's domain, honouring robots.txt, to see which address format they publish. Only the company's own site is read.

## Actor input object example

```json
{
  "people": [
    {
      "first_name": "Maria",
      "last_name": "Gonzalez",
      "domain": "stripe.com"
    },
    {
      "first_name": "John",
      "last_name": "Smith",
      "domain": "stripe.com"
    },
    {
      "first_name": "Jan",
      "last_name": "van der Berg",
      "domain": "lyfeoffshore.com"
    }
  ],
  "verify": true,
  "verifyTop": 3,
  "minScore": 60,
  "inspectWebsite": true
}
```

# Actor output Schema

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

Best email (or null), score, reasons and ranked alternatives per person.

## `summary` (type: `string`):

People, domains, how many were found.

# 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": [
        {
            "first_name": "Maria",
            "last_name": "Gonzalez",
            "domain": "stripe.com"
        },
        {
            "first_name": "John",
            "last_name": "Smith",
            "domain": "stripe.com"
        },
        {
            "first_name": "Jan",
            "last_name": "van der Berg",
            "domain": "lyfeoffshore.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lyfe_tools/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 = { "people": [
        {
            "first_name": "Maria",
            "last_name": "Gonzalez",
            "domain": "stripe.com",
        },
        {
            "first_name": "John",
            "last_name": "Smith",
            "domain": "stripe.com",
        },
        {
            "first_name": "Jan",
            "last_name": "van der Berg",
            "domain": "lyfeoffshore.com",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("lyfe_tools/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 '{
  "people": [
    {
      "first_name": "Maria",
      "last_name": "Gonzalez",
      "domain": "stripe.com"
    },
    {
      "first_name": "John",
      "last_name": "Smith",
      "domain": "stripe.com"
    },
    {
      "first_name": "Jan",
      "last_name": "van der Berg",
      "domain": "lyfeoffshore.com"
    }
  ]
}' |
apify call lyfe_tools/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,lyfe_tools/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/3O2y3jzkH8Xo3LE8Z/builds/hIxzXS2AFBaOToWRo/openapi.json
