# Work Email Finder: Name + Domain → Verified Email (`cleanrowdata/work-email-finder`) Actor

Find a person's work email from first name, last name and company domain. Generates common corporate patterns, resolves MX, and checks mailboxes via a live HTTPS verification API (Apify blocks port 25, so no direct SMTP on platform). Catch-all flagged. Pay only on finds.

- **URL**: https://apify.com/cleanrowdata/work-email-finder.md
- **Developed by:** [Clean Row Data](https://apify.com/cleanrowdata) (community)
- **Categories:** Lead generation, Agents, MCP servers
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $12.60 / 1,000 found work emails

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: name + domain → verified work email

**ClearPath-style email finding with no third-party contact database.** Give a first name, last name and company domain; the Actor generates common corporate patterns (`first.last`, `flast`, `firstlast`, …), resolves MX records, detects catch-all domains, and **verifies mailbox existence with a live check**.

Built to chain after [Hiring Signals API](https://apify.com/cleanrowdata/hiring-signals-api) and [Hiring Intent Contacts](https://apify.com/cleanrowdata/hiring-intent-contacts): once you know the company domain, find the right person.

**Price: $18 per 1,000 found emails** ($0.018 each). **Misses are free.** We do not race to sub-$1/1k — verification quality matters more than a race to the bottom.

#### How verification works (honesty)

Apify platform runners **block outbound TCP port 25**, so this Actor cannot open a raw SMTP conversation to the recipient MX from inside Apify. Claiming “SMTP verified” on platform without a relay would be a lie.

**What we do instead:** when `ABSTRACT_API_KEY` is set (default path), each candidate is checked through **Abstract Email Reputation** over **HTTPS**. Abstract performs live SMTP + MX checks on their infrastructure and returns deliverability. We only mark `verification=valid` when the API says:

- `status=deliverable`
- `is_smtp_valid=true`
- `is_catchall` is **not** true

Catch-all domains are returned as `verification=catch_all` (never `valid`) under the default policy.

Direct SMTP RCPT (`verifyProvider=smtp`) still exists for off-platform / self-hosted runners where port 25 is open.

| `verification` | Meaning |
|---|---|
| `valid` | Live check accepted this mailbox; domain is not catch-all |
| `catch_all` | Domain accepts arbitrary locals — pattern is a guess |
| `invalid` | Live check rejected the candidates we tried |
| `unknown` | Could not complete a live check (quota, timeout, no key, etc.) |

#### Free-tier limits (Abstract)

Clean Row Data’s bundled free Abstract key is **100 checks / month** (shared). Each catch-all probe + each pattern attempt costs one check. The Store example input (Dylan Field / figma.com) is limited to **one** check per run. For Store volume beyond that, bring your own Abstract key (`abstractApiKey` input or Actor env) or ask us to enable a paid plan / self-hosted SMTP relay.

**Bring your own free Abstract key (`abstractApiKey`) for unlimited checks on your side.** Without your own key, runs on a free Apify plan use the shared key and are limited to 9 mailbox checks per run (about 3 finds); misses and unchecked people are never charged. Get a free key at https://www.abstractapi.com/api/email-verification-validation-api (Email Reputation API, no card) and paste it into the `abstractApiKey` input.

#### Quick start

```json
{
  "firstName": "Dylan",
  "lastName": "Field",
  "domain": "figma.com",
  "catchAllPolicy": "flag",
  "verifyProvider": "auto"
}
```

Batch:

```json
{
  "people": [
    {"firstName": "Dylan", "lastName": "Field", "domain": "figma.com"},
    {"firstName": "Guillermo", "lastName": "Rauch", "domain": "vercel.com"}
  ],
  "catchAllPolicy": "flag",
  "onlyFinds": true
}
```

Or paste CSV (`first,last,domain` per line) into the `csv` field.

#### Output

| Field | Meaning |
|---|---|
| `email` | Best address (null on miss when `onlyFinds` is off) |
| `alternates` | Other candidates tried / plausible |
| `mx_host` | Primary MX |
| `is_catch_all` | Domain accepts arbitrary local parts |
| `verification` | `valid` / `invalid` / `unknown` / `catch_all` |
| `confidence` | 0–1 |
| `pattern` | e.g. `first.last`, `flast`, `first` |
| `verify_source` | `https_abstract` or `smtp` |
| `detail` | Reason code (e.g. `https_abstract_deliverable`) |

#### Catch-all policy

Many corporate domains accept any local part. Claiming those as "verified" is dishonest.

| Policy | Behavior |
|---|---|
| `flag` (default) | Return best pattern with `verification=catch_all`, never `valid` |
| `skip` | Treat as a miss (free, no row by default) |
| `accept` | Return first pattern as `catch_all` with low confidence |

#### Integrations (n8n, Make, Zapier, Clay, AI agents)

Every integration calls the same Apify API, so the price is the same everywhere (pay per found email; misses are free).

- **Any HTTP client / Clay HTTP API column**: `POST https://api.apify.com/v2/acts/cleanrowdata~work-email-finder/run-sync-get-dataset-items` with header `Authorization: Bearer <APIFY_TOKEN>` and a JSON body like:
  ```json
  {"people": [{"firstName": "{{first}}", "lastName": "{{last}}", "domain": "{{domain}}"}], "onlyFinds": true}
  ```
  The response is the dataset rows. In Clay, call it from an HTTP API column and use it as one step of a waterfall.
- **n8n**: use the Apify node (verified community node) to run Actor `cleanrowdata/work-email-finder`, or an HTTP Request node with the call above.
- **Make**: Apify *Run an Actor* module, then *Get Dataset Items*. **Zapier**: Apify *Run Actor* action, then *Fetch dataset items*.
- **AI agents (Claude, Cursor, ChatGPT and others) via the Apify MCP server**: connect `https://mcp.apify.com?tools=cleanrowdata/work-email-finder` and ask, e.g. "Find the work email for Dylan Field at figma.com".

#### Pricing (pay per event)

| Event | Price | When |
|---|---|---|
| `email_found` | **$0.018** | Row with a find (`valid` or `catch_all` under flag/accept) |
| Misses | **$0** | Not pushed when `onlyFinds` is on |
| `apify-actor-start` | Apify default | Run start |

#### What this is not

- Not a LinkedIn scraper
- Not a phone finder
- Not a bulk B2B database dump
- Does not buy or resell third-party contact data
- Does **not** claim direct SMTP from Apify runners (port 25 is blocked)

If this saved you time, a quick review on the Store helps a lot, and feature requests go in the Issues tab; we usually ship them within days.

#### Compliance

Mailbox verification uses Abstract’s live checks (or direct SMTP when you run where :25 is open). Some networks and servers always return 250 (catch-all) — we flag those. Results include `verification`, `confidence`, and `verify_source` so you can filter. You are responsible for lawful use of any email you send to.

# Changelog

This Actor's version history is a separate document: https://apify.com/cleanrowdata/work-email-finder/changelog.md

# Actor input Schema

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

Person's first name (single lookup). Prefer people\[] or csv for batches.

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

Person's last name.

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

Company email domain, e.g. figma.com (not gmail.com).

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

List of {firstName, lastName, domain}. Overrides single fields when set.

## `csv` (type: `string`):

Optional CSV/TSV blob. Header row optional.

## `catchAllPolicy` (type: `string`):

How to handle domains that accept any local part. Never reported as verification=valid.

## `smtpVerify` (type: `boolean`):

Legacy toggle for direct SMTP. Prefer verifyProvider. Ignored when verifyProvider=https or auto with ABSTRACT_API_KEY (HTTPS path used because Apify blocks outbound TCP/25).

## `maxProbes` (type: `integer`):

How many pattern candidates to probe after catch-all detection.

## `onlyFinds` (type: `boolean`):

When on (default), misses are omitted and free. Turn off to see invalid/unknown rows (still free).

## `limit` (type: `integer`):

Cap on charged finds for this run.

## `smtpTimeout` (type: `number`):

Per-connection timeout. Session is reused across catch-all + pattern probes; multiple MX hosts are tried on connect failure.

## `sourceDatasetId` (type: `string`):

Optional Apify dataset ID of people rows ({firstName,lastName,domain}) or hiring-contacts items with a people\[] list. Chains after hiring-intent-contacts / custom enrichment.

## `verifyProvider` (type: `string`):

How to verify mailbox existence. Apify firewalls outbound port 25, so direct SMTP times out on platform. Auto uses Abstract HTTPS when ABSTRACT_API_KEY is configured (Actor secret or abstractApiKey input). valid = deliverable + SMTP ok + not catch-all.

## `abstractApiKey` (type: `string`):

Bring your own free Abstract key (Email Reputation API, 100 free checks/month, no card: https://www.abstractapi.com/api/email-verification-validation-api) for unlimited checks on your side. Without it, free-plan runs share our key and are capped at 9 mailbox checks per run (~3 finds). Each catch-all probe + each pattern candidate costs one check.

## Actor input object example

```json
{
  "firstName": "Dylan",
  "lastName": "Field",
  "domain": "figma.com",
  "catchAllPolicy": "flag",
  "smtpVerify": true,
  "maxProbes": 8,
  "onlyFinds": true,
  "limit": 100,
  "smtpTimeout": 8,
  "verifyProvider": "auto"
}
```

# Actor output Schema

## `emails` (type: `string`):

One row per find (misses are free and omitted by default).

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

Finds, misses, catch-all count.

# 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 = {
    "firstName": "Dylan",
    "lastName": "Field",
    "domain": "figma.com",
    "catchAllPolicy": "flag",
    "verifyProvider": "auto"
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleanrowdata/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 = {
    "firstName": "Dylan",
    "lastName": "Field",
    "domain": "figma.com",
    "catchAllPolicy": "flag",
    "verifyProvider": "auto",
}

# Run the Actor and wait for it to finish
run = client.actor("cleanrowdata/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 '{
  "firstName": "Dylan",
  "lastName": "Field",
  "domain": "figma.com",
  "catchAllPolicy": "flag",
  "verifyProvider": "auto"
}' |
apify call cleanrowdata/work-email-finder --silent --output-dataset

```

## MCP server setup

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