# B2B Email Finder & Verifier — Find Emails, LinkedIn & Phones (`memo23/email-finder`) Actor

Find emails for any company or LinkedIn profile in bulk. Pick the decision-makers you want (founder, CEO, VP) and get names, titles, LinkedIn URLs, phones and MX-verified B2B emails, ranked by confidence. Search and preview free — pay only for verified, deliverable contacts. No API keys.

- **URL**: https://apify.com/memo23/email-finder.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Lead generation, AI, Agents
- **Stats:** 148 total users, 68 monthly users, 99.6% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $10.00 / 1,000 company enricheds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Finder & Verifier — Find Decision-Makers & Verify Their Emails

<p align="center">
  <img src="https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/email-finder-logo.png" alt="Email Finder logo" width="220" />
</p>

**Find the decision-makers at any company for free — then pay only for the ones that resolve to a verified, deliverable email.** Give it a company domain and the roles you want (founder, CEO, VP, head of sales, marketing lead); it reads the team, about, and leadership pages, pulls the real people with their titles, infers each business address, MX-verifies it, and cross-checks it against the emails the company actually publishes. Finding the people costs **nothing**. A verified person-email is **$0.02** — a fraction of the **$0.028–$0.14 per verified contact** the pay-per-verify lead tools charge — and every result ships with ranked candidate addresses, the mail provider, and a confidence score, not a single blind guess.

**The whole funnel, in one line:**

```
Discover people  →  infer + rank addresses  →  MX-verify + site cross-check  →  you pay only on a mail-capable hit
   (free)                  (free)                       (free)                          ($0.02 / verified email)
```

| Input | Row(s) emitted |
| --- | --- |
| **Discovery query (roles × company / location)** | One `discovered-person` row per person found — name, title, LinkedIn + verified email. **Finding is free; you pay only for verified hits.** |
| Domain or company name | One company row — ranked emails + phones, provenance, confidence |
| Person (`fullName` + `companyDomain`) | One `person` row — best inferred email, ranked candidates, MX + provider |

> Pure HTTP. No API keys, no LinkedIn login, no cookies — and no 5,000-row dumps of unverified guesses you pay for by the row.

### Why Use This Scraper?

- **Find free, pay only for verified.** Discovering who works at a company — and inferring, ranking, and MX-checking their addresses — costs nothing. You're billed **only when a person resolves to a mail-capable business email** ($0.02). A run full of misses costs $0. That's the opposite of bulk dumpers, who bill for every unverified row they emit.
- **Cheaper per verified email than the pay-per-verify tools.** $0.02 per verified person-email. The dedicated "verified contact" finders charge **$0.028 to $0.14** for the same unit — you land 30% to 7× below them while adding verification depth and ranking they don't ship.
- **You verify AND rank — most finders just hand you an address.** Every person row is MX-verified, cross-checked against the addresses the company actually publishes (a published match returns as `site-match`, high confidence), and comes with the **full ranked candidate list** (`first.last@`, `flast@`, `first@`, …) plus scores, the mail provider (Google Workspace, Microsoft 365, …), and a `source`/`confidence` pair. You see how each answer was derived, not just the answer.
- **Target the decision-makers, by role.** Pick a preset with `targetPersona` — Founder/CEO, Sales leader, Finance leader, and so on — and the actor turns a plain list of domains into a decision-maker search, no per-company query to write. Or filter discovery by exact job title (`["founder", "ceo", "vp", "head of sales"]`) when you want fine control. Either way you get named executives with titles and verified emails, not a generic `info@`.
- **No API keys, no login, no cookies.** The core path — team-page discovery, site scrape, pattern inference, MX verification — is fully self-contained. Nothing to sign up for, no LinkedIn session, no headless browser. (Optional keys only *add* coverage: RocketReach for open-web search, Hunter.io for a domain-search fallback.)
- **Phones included.** Company rows carry every phone number harvested from the same contact pages — no extra charge, no second tool.
- **Bulk by design.** Paste arrays, spreadsheet columns, or a comma-separated blob of hundreds of companies; mix companies, people, and discovery queries in one run.
- **Monetization qualification for free.** Flip `qualifyByPayment` and each company row is flagged with `takesPayments` + `paymentProcessors` (Stripe, Shopify, PayPal, Paddle, …) — reusing pages already fetched, zero extra cost.

### Overview

Email Finder is a **find-and-verify lead engine**. The first three modes mix freely in one run; the fourth is a standalone verifier:

- **Discovery** — the funnel. You know only the role/company/location; the actor finds the people (company team pages by default, open-web RocketReach search with your own API key), then resolves each person to a verified email. Discovery itself is **free** — you're billed only per verified email found.
- **People** — you already know the person; `{fullName, companyDomain}` returns their most-likely business email with ranked candidates, MX verification, and site cross-checking.
- **Companies** — by domain (Track A: the site is scraped directly, most accurate) or by name (Track B: the website is resolved first behind a name-match precision guard, then scraped). Output: ranked emails + phones per company.
- **Verify-only** — paste a raw list of emails and the actor validates each (syntax, MX, disposable/role/free, and a valid/risky/invalid verdict) and exits. A bulk email verifier when that's all you need — billed $0.001 per address.

A realistic note on hit rates: on-page emails exist for companies that publish a contact address — most small and mid-sized businesses, agencies, shops, and firms. Discovery finds people on sites that publish a team/about/leadership page (very common for agencies, startups, professional services); enterprises that hide both go through the person-inference or BYO-key paths. Every row tells you exactly where its answer came from, so quality is never a guess.

### Supported Inputs

Mix and match in one run — all merged and de-duplicated:

- **`domains`** — company websites/domains (Track A, most accurate).
- **`companyNames`** — company/brand names to resolve, then find emails (Track B).
- **`inputText`** — paste a blob of domains or names (new-line / comma / semicolon separated).
- **`companies`** — advanced structured rows mixing `name` + `website` + `city`/`state`.
- **`people`** — person-level lookups: `{ "fullName": "Patrick Collison", "companyDomain": "stripe.com" }` or `"Patrick Collison, stripe.com"` strings.
- **`discoverPeople`** *(new)* — discovery queries: `{ "website": "stripe.com", "jobTitles": ["engineering"] }`, `{ "company": "Notion", "personName": "Ivan" }`, or with a RocketReach key `{ "jobTitles": ["CTO"], "locations": ["Austin"] }`. Plain strings work too: `"CTO @ stripe.com"`.
- **`emailList`** *(verify-only)* — paste emails you already have to validate them. When set, the actor runs in **verify-only** mode (no finding): each email gets syntax, MX, disposable, role-based and free-provider checks plus a **valid / risky / invalid** verdict (and an `smtp` object when `smtpCheck` is on). Billed per email via `emails-verified` ($0.001).

#### Verify a list of emails

```json
{
    "emailList": ["jeff@amazon.com", "noreply@stripe.com", "test@mailinator.com"]
}
```

Output per email:

```json
{ "mode": "verify", "email": "noreply@stripe.com", "syntaxValid": true, "domain": "stripe.com", "mxFound": true, "isDisposable": false, "isRoleBased": true, "isFreeProvider": false, "verdict": "risky" }
```

### Use Cases

| Who | What they use it for |
| --- | --- |
| **Sales & lead-gen teams** | Turn a target-account list into verified decision-maker emails — including accounts where they don't know a single name yet. |
| **Agencies & freelancers** | Build a niche prospect list (dentists, law firms, e-commerce shops) from names or domains, with phones. |
| **Recruiters** | Find the hiring manager or team lead at a company (Discovery by title), then their verified email. |
| **Market researchers** | Attach contact emails, phones, and resolved websites to a company list for a study or directory. |
| **RevOps / data teams** | Enrich a CRM segment with verified emails, provenance, and payment-stack qualification in bulk. |

### How It Works

![How the Email Finder works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-email-finder.png)

1. **Input** — companies (domains / names / pasted blob), known people, and/or discovery queries (title × company × location).
2. **Discover** *(Discovery mode)* — the company's team, about, and leadership pages are scanned for real people (JSON-LD Person schema, LinkedIn profile cards, team-card markup — strict name validation, no fabrications). With your own RocketReach API key, open-web search by title/location runs instead, using only the free search teaser.
3. **Resolve & verify** — every person is resolved to their most-likely address: all standard corporate patterns generated and ranked, the domain MX-verified, the mail provider detected, and the result cross-checked against emails actually published on the site (a published address = `site-match`, high confidence). Company rows get their contact pages scraped, junk stripped, role emails ranked first, phones harvested — with optional per-address verification.
4. **Export** — one flat row per company / person / discovered person, streamed to the dataset: JSON, CSV, Excel, or API.

### Input Configuration

| Field | Type | Description |
| --- | --- | --- |
| `domains` | array | Company websites/domains to find emails for (Track A, most accurate). |
| `companyNames` | array | Company names to resolve to a website, then find emails (Track B). |
| `targetPersona` | array | **Decision-maker presets** — pick roles (Founder/CEO, Full C-suite, Sales leader, Marketing leader, Engineering leader, Finance leader, HR leader, Operations leader) and the actor auto-runs a people search for each `domain`/`companyName`. Turns a plain domain list into a decision-maker search — no per-row `discoverPeople` query needed. |
| `defaultCountry` | string | Country hint applied to every persona search (e.g. `United States`). |
| `inputText` | string | Paste a blob of domains or names (new-line / comma / semicolon separated). |
| `companies` | array | Advanced — structured `{ name, website, city, state }` rows. |
| `people` | array | Person lookups — `{ fullName, companyDomain }` objects or `"Name, domain.com"` strings. |
| `discoverPeople` | array | **Discovery queries** — `{ company?, website?, personName?, jobTitles?, locations?, maxPeople? }` objects or `"CTO @ acme.com"` strings. Finds the people, then resolves their emails. `locations` applies on the RocketReach path (team pages don't publish per-person locations). |
| `rocketreachApiKey` | string | Optional official RocketReach API key — upgrades Discovery to open-web search by title/location/industry with no company needed. Search teaser only: **your per-contact unlock credits are never spent**. |
| `maxDiscoveredPeople` | integer | Global cap on people processed by Discovery per run (default 200; per-query default 25 via `maxPeople`). |
| `guardMode` | string | Domain-match strictness for name→website discovery: `business` (default), `off`, `residential`. |
| `onlyWithEmails` | boolean | **On by default** — emit and bill only company rows where an email was found, so you never pay for a no-email company. Set `false` to also get no-email rows (for coverage auditing); those are billed when emitted. |
| `verifyEmails` | boolean | Validate every found company email (syntax + MX + disposable/role/free flags); adds `emailVerifications`, `validEmails`, `bestValidEmail`. Billed per email verified. |
| `smtpCheck` | boolean | Opt-in best-effort SMTP mailbox probe on top of verification (EHLO → MAIL FROM → RCPT, plus catch-all detection); adds an `smtp` object per checked email. Honest fallback: port 25 is blocked on many networks, so it often can't connect and defers to the DNS/MX verdict — only a clean 550 marks an address invalid. No extra charge. |
| `qualifyByPayment` | boolean | Flag each company with `takesPayments`, `paymentProcessors` (Stripe/Shopify/…), `stripeLiveKey` — free, reuses pages already fetched. |
| `hunterApiKey` | string | Optional Hunter.io API key for a paid domain-search fallback. |
| `maxItems` | integer | Hard cap on companies processed this run. |
| `maxConcurrency` | integer | How many jobs to run in parallel. |
| `proxy` | object | Leave empty — traffic already routes through the actor's built-in residential proxy at no extra cost. |

#### Example input

```json
{
  "domains": ["ahrefs.com", "sitepoint.com"],
  "companyNames": ["Basecamp"],
  "people": [{ "fullName": "Patrick Collison", "companyDomain": "stripe.com" }],
  "discoverPeople": [{ "website": "posthog.com", "jobTitles": ["founder"] }],
  "guardMode": "business",
  "verifyEmails": true
}
```

### Output Overview

Every result is one flat JSON object. Company rows have no `rowType`; person rows carry `rowType: "person"`; discovery rows carry `rowType: "discovered-person"`. Fields that don't resolve come back `null` or empty rather than missing, so the schema stays consistent. Download as JSON, CSV, Excel, or via the Apify API.

### Output Samples

A domain that publishes emails (Track A) — note the phones, harvested from the same pages:

```jsonc
{
  "query": null,
  "inputWebsite": "ahrefs.com",
  "companyName": null,
  "website": "https://ahrefs.com/",
  "email": "support@ahrefs.com",
  "emails": ["support@ahrefs.com", "pr@ahrefs.com"],
  "emailCount": 2,
  "phone": "+16502530000",
  "phones": ["+16502530000"],
  "phoneCount": 1,
  "source": "site-scrape",
  "confidence": "high",
  "foundAt": "2026-07-23T05:20:41.000Z"
}
```

A person lookup (`people` input) — `rowType: "person"`:

```jsonc
{
  "rowType": "person",
  "fullName": "Patrick Collison",
  "companyDomain": "stripe.com",
  "email": "patrick.collison@stripe.com",
  "pattern": "first.last",
  "candidates": [
    { "email": "patrick.collison@stripe.com", "pattern": "first.last", "score": 40 },
    { "email": "patrick@stripe.com", "pattern": "first", "score": 15 },
    { "email": "pcollison@stripe.com", "pattern": "flast", "score": 14 }
    // …every standard corporate pattern, ranked
  ],
  "mxValid": true,
  "emailProvider": "google-workspace",
  "source": "pattern",          // "site-match" = published on the company site · "site-pattern" = org's naming style learned from the site · "pattern" = frequency prior
  "confidence": "medium",       // high = site-match · medium = MX-verified inference · low = domain accepts no mail
  "foundAt": "2026-07-23T10:41:00.000Z"
}
```

A discovery result (`discoverPeople` input) — `rowType: "discovered-person"`, the person AND their verified email in one row:

```jsonc
{
  "rowType": "discovered-person",
  "fullName": "James Hawkins",
  "title": "Co-founder",
  "companyName": null,
  "companyDomain": "posthog.com",
  "location": null,
  "linkedinUrl": null,
  "discoverySource": "site-team-page",   // or "rocketreach-search" with a BYO key
  "discoveryQuery": "posthog.com",
  "email": "james.hawkins@posthog.com",
  "pattern": "first.last",
  "candidates": [ /* ranked as above */ ],
  "mxValid": true,
  "emailProvider": "google-workspace",
  "source": "pattern",
  "confidence": "medium",
  "foundAt": "2026-07-23T10:41:02.000Z"
}
```

With **`verifyEmails: true`**, company rows also carry per-address validation:

```jsonc
{
  // …the normal finder fields…
  "validEmails": ["jane.doe@acme.com"],
  "validEmailCount": 1,
  "bestValidEmail": "jane.doe@acme.com",
  "emailVerifications": [
    { "email": "jane.doe@acme.com", "syntaxValid": true, "domain": "acme.com", "mxFound": true,
      "isDisposable": false, "isRoleBased": false, "isFreeProvider": false, "verdict": "valid" },
    { "email": "info@acme.com", "syntaxValid": true, "domain": "acme.com", "mxFound": true,
      "isDisposable": false, "isRoleBased": true, "isFreeProvider": false, "verdict": "risky" }
  ]
}
```

`verdict` is `valid` (syntax + MX ok, not disposable), `risky` (role-based or free-provider), or `invalid` (bad syntax, no MX, or disposable).

### Key Output Fields

**Company rows**

- `website` — the resolved company website · `email`/`emails`/`emailCount` — ranked, de-duplicated
- `phone`/`phones`/`phoneCount` — phones from the same contact pages
- `takesPayments`/`paymentProcessors`/`stripeLiveKey` — with `qualifyByPayment`

**Person & discovered-person rows**

- `email` — best inferred address · `candidates` — every pattern, ranked with scores
- `mxValid` — domain accepts mail (the billing gate) · `emailProvider` — google-workspace, microsoft-365, …
- `title`/`linkedinUrl`/`location`/`discoverySource`/`discoveryQuery` — discovery rows only

**Provenance (all rows)**

- `source` — `site-scrape` / `hunter` / `site-match` / `site-pattern` / `pattern` / `none`
- `confidence` — `high` / `medium` / `low` · `foundAt` — ISO timestamp

### Pricing

Pay-per-event — you're charged for results, not runtime:

| Event | When it fires | Rate |
| --- | --- | --- |
| Person email found | A person (looked-up **or discovered**) resolves to a mail-capable business email. Never fires on dead domains. | $0.02 |
| Company enriched | One company row emitted (emails + phones + optional payment qualification). | $0.01 |
| Email verified | One address validated when `verifyEmails` is on. | $0.001 |
| Actor start | Once per run. | $0.005 |

**Discovery is free.** Finding the people costs nothing — you pay only when a discovered person resolves to a verified, mail-capable address. A 100-person discovery run where 60 resolve: $0.005 + 60 × $0.02 = **$1.21**, and the 40 misses cost nothing.

**Where $0.02 per verified email sits.** The dedicated verified-contact finders bill the same unit — one verified business email — at several times the price:

```
  THIS ACTOR        LinkedIn verified-email finders      Decision-maker finders
   $0.02  ●───────────────── $0.028 ───────────────────────── $0.14
   per verified email        (~40% dearer)                    (7× dearer, per verified contact)
```

Same deliverable — a resolved, MX-verified business email — for a fraction of the price, and you also get the ranked candidates, the site cross-check, and the mail provider that the pricier tools leave out. Discovery stays free on top.

### How this compares

| Capability | Bulk dump actors | Pay-per-verify contact finders | This actor |
| --- | --- | --- | --- |
| Price per verified business email | n/a (sells dumps) | **$0.028 – $0.14** per contact | **$0.02** |
| The find / discovery step | billed per row | often billed too ($0.004–$0.06 per profile or domain) | **free** |
| Billing unit | every emitted row, verified or not | per verified contact | **only mail-capable results** |
| Ranked candidate list with scores | ✗ | ✗ (one address handed back) | ✓ full pattern list, scored |
| Cross-check vs the company's own site | ✗ | ✗ | ✓ (`site-match` / `site-pattern`) |
| Finds the people, not just the addresses | ✗ (bring your own list) | ✓ | ✓ Discovery mode, **free** |
| Mail provider + `source`/`confidence` per row | ✗ | rarely | ✓ every row |
| API keys required | varies | often | **none** (optional keys only add coverage) |
| Phones on company rows | ✗ | varies | ✓ included |
| Payment-stack qualification | ✗ | ✗ | ✓ free opt-in |

The pay-per-verify finders do one thing — hand back a verified address — and charge $0.028 to $0.14 for it. This actor resolves the same verified address for **$0.02**, keeps the discovery step **free**, and ships the ranked candidates, the site cross-check, the mail provider, and the provenance in the row you pay for. A 5,000-row dump you have to verify elsewhere isn't cheaper either — it's the same work moved to your desk, plus a second tool's bill.

### Switching from another email or lead finder?

The most common complaints on other lead/email finders come down to paying for empty or junk results. Here's how each is handled here — every answer is a capability the actor already ships, not a promise:

| A complaint you may recognise | How this actor handles it |
| --- | --- |
| "Ran and consumed my credit with no output" · "all my credits gone for 2 emails" | Person results bill **only on a mail-capable, MX-verified hit** — misses cost $0. Company rows gate with `onlyWithEmails`. |
| "Not accurate" · "half the verified emails were undeliverable" | Each address is MX-checked and cross-checked against the company's own published emails, with a `valid`/`risky`/`invalid` verdict and a confidence score — not a blind guess. |
| "Duplicate emails and random results" · "90% the same leads every run" | Inputs are normalised and de-duplicated to one billable row per company/person; every row is flat, self-contained, and carries its provenance. |
| "It just keeps running" · "one crawl returned 1,800 rows and drained my credits" | Hard caps you set: `maxItems`, `maxDiscoveredPeople`, `maxVerifiedEmailsPerCompany`, and per-query `maxPeople`. It never runs away. |
| "Can't get phone numbers" | Phones are harvested from the same contact pages and included on company rows at no extra charge. |
| "Unexpected / unknown enrichment charges" | Four flat pay-per-event rates, listed in Pricing; person hits bill only when mail-capable. Nothing hidden. |

### Notes & limitations

- Site-engine Discovery finds people on companies that **publish a team/about/leadership page** (very common for agencies, startups, and professional services). Companies without one return no discovered people — honestly, rather than fabricating names. The open-web path (BYO RocketReach key) covers the rest.
- `locations` filters apply on the RocketReach path; public team pages don't publish per-person locations.
- Verification is DNS/MX-based by default (does the domain accept mail?) plus site cross-checking. Live SMTP mailbox probing is available as an opt-in (`smtpCheck`) but is best-effort: mail servers and networks widely block outbound port 25 (much of Apify's infrastructure included), so it frequently can't connect and honestly falls back to the DNS/MX verdict rather than guessing. Treat a returned `smtp.accepted: true/false` as a bonus signal when present, not a guarantee.
- Company email hit-rates depend on the company publishing an address; `source: "none"` rows tell you exactly which didn't.

### FAQ

**How is Discovery different from the `people` input?**
`people` needs a name you already know. `discoverPeople` finds the names: it scans the company's team/about/leadership pages (or searches the open web with your RocketReach key), extracts real people with their titles, and then runs each through exactly the same email-inference + verification pipeline. One run, one row per person, discovery free, billed only on verified hits.

**Does Discovery spend my RocketReach credits?**
No. The actor calls only the RocketReach **search** endpoint, whose results include the person's professional email *domain* as a teaser. The actual address is resolved by this actor's own inference + MX verification. Per-contact unlock credits are never touched.

**Why did a discovery query return few (or no) people?**
The default engine reads what the company publishes. If there's no team/about page — or it's rendered entirely client-side — there's nothing trustworthy to extract, and the actor returns nothing rather than inventing names (strict validation drops nav labels, section headings, and marketing cards). Add a `rocketreachApiKey` for open-web coverage of those companies.

**What's the difference between passing a domain and a company name?**
A domain (Track A) is scraped exactly as given — no guessing. A company name (Track B) is resolved to a website first behind a name-match guard so the wrong company's domain is never attached. If you have domains, prefer them.

**Do I pay for companies or people where nothing was found?**
Person-level results (looked-up or discovered) are charged **only when mail-capable**. Company rows are charged when emitted, and `onlyWithEmails` is **on by default** — so no-email companies are dropped and not billed unless you set `onlyWithEmails: false` to keep them for coverage auditing.

**How are the emails ranked?**
Role/outreach addresses (`info@`, `sales@`, `contact@`, `hello@`) float to the top of company rows; junk (tracking pixels, asset filenames, placeholders, `no-reply@`) is stripped. Person rows rank every corporate pattern by real-world frequency, boosted by what the company's own site reveals.

**How does the person lookup work — is it scraped or guessed?**
Both, in the right order. All standard patterns are generated and ranked, the domain is MX-verified, the provider detected — and the company site is scanned: a published address returns as `site-match` (high confidence); published addresses revealing the org's naming style boost the matching pattern (`site-pattern`).

**Do I need a Hunter.io or RocketReach key?**
No. The free paths (site scrape, team-page discovery, pattern inference) work on their own. Keys are optional upgrades: Hunter.io adds a paid domain-search fallback; RocketReach upgrades discovery to open-web title/location search.

**How fast is it, and how many companies can it handle?**
Jobs run in parallel (tune `maxConcurrency`) and scale from a handful to thousands. Traffic routes through the actor's built-in residential proxy at no extra cost.

**What export formats are supported?**
Every run writes to an Apify dataset — JSON, CSV, Excel, XML, JSONL, or the Apify API — and wires into scheduling, webhooks, Google Sheets, Zapier, or Make.

### Support

- **Found a bug or need a new field?** Open a ticket on the **Issues** tab of this actor — it's the fastest way to reach me and I actively maintain this actor.
- **Email:** <muhamed.didovic@gmail.com>
- **Website:** [muhamed-didovic.github.io](https://muhamed-didovic.github.io/)

### Additional Services

Need something beyond the standard output? I build and maintain custom actors and data pipelines. Happy to help with:

- Deeper person-level contact discovery (titles, LinkedIn profiles, custom sources)
- Custom fields, filters, or export formats tailored to your workflow
- Private or dedicated actors for high-volume or compliance-sensitive use

Reach out via the Issues tab or email and describe what you need.

### Explore More Scrapers

Browse my other Apify actors — job boards, business directories, review sites, social platforms, and more — on my profile: [memo23 on Apify](https://apify.com/memo23).

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/email-finder`).

**Purpose:** find and verify business emails (plus phones on company rows) by company domain/name, by person (name + domain), or by discovery mode (job title / company / location → people → MX-verified emails); billed only on mail-capable hits.

**Minimal input:**

```json
{ "domains": ["stripe.com"], "maxItems": 50 }
```

Person mode: `{ "people": ["Patrick Collison, stripe.com"] }` · Discovery mode: `{ "discoverPeople": [{ "website": "posthog.com", "jobTitles": ["founder"] }] }` — modes can be mixed in one call.

**Output:** company rows carry `website, email, emails[], emailCount, phone, phones[], phoneCount, source, confidence, foundAt`. Person rows (`rowType: "person"`) carry `fullName, companyDomain, email, pattern, candidates[] {email, pattern, score}, mxValid, emailProvider, source, confidence`. Discovery rows (`rowType: "discovered-person"`) carry the person fields plus `title, companyName, location, linkedinUrl, discoverySource, discoveryQuery`.

**Behaviors an agent should know:**

- `maxItems` caps companies processed (default 100000); `maxDiscoveredPeople` caps discovery output (default 200) — set both low to bound cost.
- Mix `domains`, `companyNames`, `people`, and `discoverPeople` in one run; `discoverPeople` accepts loose strings like `"CTO @ acme.com"`.
- Without `rocketreachApiKey`, discovery queries need a `company` or `website`; the actor never spends RocketReach unlock credits.
- `onlyWithEmails` defaults to **true** (no-email company rows are dropped and not billed); set it `false` to keep them. `verifyEmails: true` adds per-address validation; `smtpCheck: true` adds an opt-in best-effort SMTP probe (often blocked on port 25 → falls back to DNS/MX).
- Billing: `company-enriched` $0.01 per company row (primary event); `person-email-found` $0.02 only when `mxValid` and an email resolved; email verified $0.001 each with `verifyEmails`; actor start $0.005. Discovery is free.

### ⚠️ Disclaimer

This actor discovers **publicly available** business contact information — the same email addresses, phone numbers, and team-page listings any visitor can see on a company's public website, plus (optionally, with your own API key) data from providers you hold an account with. It does not access private data, bypass authentication, or scrape gated content.

You are responsible for how you use the data you collect. Use it in compliance with applicable laws (including the GDPR, CAN-SPAM, CASL, and other data-protection and anti-spam regulations) and any contractual obligations you may have. Finding an email address does not constitute consent to contact it — ensure your outreach complies with the relevant marketing and messaging laws in your jurisdiction. Do not use this tool to facilitate spam, harassment, or any violation of privacy.

### SEO Keywords

email finder, bulk email finder, business email finder, company email lookup, domain email finder, email scraper, email extractor, find company emails, b2b email finder, lead generation tool, sales prospecting, contact email finder, email discovery, company contact scraper, domain to email, email hunter alternative, cold outreach emails, crm enrichment, bulk email lookup, email finder api, person email finder, find email by name and company, name to email, email pattern finder, email permutator, verified business email, mx verification, find employees by company, find people by job title, decision maker finder, team page scraper, apollo alternative, rocketreach alternative, discover leads by title, verified leads finder, email verification built in

# Actor input Schema

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

Person-level lookups. Each item is {"fullName": "Patrick Collison", "companyDomain": "stripe.com"} (plain strings like "Patrick Collison, stripe.com" also work). For each person you get the most-likely business email with a ranked candidate list, live MX verification, the company's mail provider, and a confidence score. The company site is also scanned so a published address or the org's real naming pattern beats a blind guess. Billed per person with a mail-capable result.

## `emailList` (type: `array`):

Paste emails to validate — one per line. When this is filled the actor runs in <b>verify-only</b> mode: it validates each email and exits (no finding/discovery). Each email returns syntax, MX, disposable, role-based, free-provider flags and a <b>valid / risky / invalid</b> verdict. Billed per email via the <code>emails-verified</code> event.

## `discoverPeople` (type: `array`):

Discovery mode — don't know the people yet? Each query finds them AND resolves their emails in one run. Example: {"website": "stripe.com", "jobTitles": \["engineering", "marketing"]} scans the company's team/about/leadership pages, extracts every person matching your title filter, then runs each through the same verification pipeline as the People input (pattern inference, MX check, cross-checked against the site). Add a RocketReach API key below to also search the open web by job title + location WITHOUT naming a company, e.g. {"jobTitles": \["CTO"], "locations": \["Austin"]}. Fields per query: company, website, personName, jobTitles, locations, maxPeople. Plain strings work too ("CTO @ stripe.com"). Billed per person with a mail-capable result — discovery itself is free.

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

Company websites or domains to find emails for (most accurate — the site is scraped directly). One per entry, e.g. stripe.com or https://stripe.com.

## `companyNames` (type: `array`):

Company / brand names to discover a website for, then find emails. Use when you don't have the domain. One per entry.

## `targetPersona` (type: `array`):

Pick the decision-maker roles to find at every domain / company you supply above — the actor auto-builds the people search for each, so a plain domain list becomes a decision-maker search. Combine several. Leave empty to use the Discover people queries instead.

## `defaultCountry` (type: `string`):

Bias the persona people-search toward one country/region (e.g. "United States", "United Kingdom"). Applied to every auto-built persona query. Leave empty for no location filter.

## `inputText` (type: `string`):

Alternative — paste a blob of domains OR company names separated by new lines, commas, or semicolons. Domains (containing a dot) are treated as websites; everything else as a company name.

## `companies` (type: `array`):

Advanced: mix name + website + city/state per row for best disambiguation. Merged with the fields above.

## `guardMode` (type: `string`):

How strictly a discovered domain must match the company name. 'business' = name match (default, recommended). 'off' = accept any Clearbit match. 'residential' = property/real-estate mode.

## `onlyWithEmails` (type: `boolean`):

On by default: emit (and bill) only company rows where at least one email was found — so you pay only for successful hits, never for a no-email company. Set to false to also emit no-email rows (useful for auditing coverage); those company rows are billed when emitted.

## `verifyEmails` (type: `boolean`):

Validate every email found: checks syntax, looks up the domain's MX record (does it accept mail?), and flags disposable, role-based (info@, sales@) and free-provider (gmail, etc.) addresses. Adds an `emailVerifications` array plus `validEmails`, `validEmailCount` and `bestValidEmail` — so you get deliverable addresses, not just candidates. Reliable DNS/list-based validation by default; add SMTP mailbox probing with the toggle below. Billed per email verified.

## `smtpCheck` (type: `boolean`):

On top of DNS/MX verification, open a real SMTP conversation with the domain's mail server (EHLO → MAIL FROM → RCPT TO) to ask whether the specific mailbox is accepted, and detect catch-all domains. Adds an `smtp` object per checked email (reachable, accepted, catchAll, code). <b>Best-effort and honest:</b> port 25 is blocked on many networks (including much of Apify's infrastructure), so this often reports <code>reachable:false</code> and falls back to the DNS/MX verdict — it never marks a valid-looking address invalid on a failed connection; only a clean mailbox rejection (550) does. No extra charge — billed under the same per-email verification event.

## `maxVerifiedEmailsPerCompany` (type: `integer`):

Optional ceiling on how many emails are verified per company. Verification is billed per email, and one company can legitimately expose dozens of addresses, so this is how you bound that spend. Any emails beyond the ceiling are still returned — just unverified and unbilled. Leave empty to verify everything found (the default behaviour).

## `qualifyByPayment` (type: `boolean`):

Scans each business's website — reusing the pages already fetched for email discovery, so no extra cost or time — for payment processors and e-commerce platforms (Stripe, Shopify, PayPal, Paddle, Lemon Squeezy, WooCommerce, Square, Chargebee and more). Adds `takesPayments` (is this a real paying business?), `paymentProcessors` (which stack), `stripeLiveKey` (the public key if exposed) and `paymentConfidence`. Turn raw contacts into monetization-qualified leads. No extra charge — included with each enriched company (billed under the existing Company enriched event).

## `hunterApiKey` (type: `string`):

Optional — supply your own Hunter.io key to enable a paid domain-search fallback when the site scrape finds no emails. Leave empty to use the free site-scrape path only.

## `rocketreachApiKey` (type: `string`):

Optional — an OFFICIAL RocketReach API key (rocketreach.co → Settings → API) upgrades Discovery mode to open-web people search: filter by job title, location, industry and employer without naming a company. The actor only calls the SEARCH endpoint and resolves emails with its own verification pipeline — it never triggers RocketReach per-contact lookups, so your unlock credits are not spent. Leave empty to use the built-in team-page discovery engine.

## `maxDiscoveredPeople` (type: `integer`):

Global cap on people processed by Discovery mode this run (across all discovery queries). Per-query caps can be set with each query's maxPeople field (default 25).

## `maxItems` (type: `integer`):

Hard cap on how many items this run processes. The budget is SHARED: companies are taken first, and person lookups get whatever is left over, so a long company list can leave no room for the `people` you also submitted (the run log warns when that happens). Free plans are capped at 25.

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

How many companies to enrich in parallel.

## `proxy` (type: `object`):

Leave empty — the actor already routes all traffic through its own built-in residential proxy at no extra cost to you. Only set this if you want to use your own proxies.

## Actor input object example

```json
{
  "people": [
    {
      "fullName": "Patrick Collison",
      "companyDomain": "stripe.com"
    }
  ],
  "emailList": [
    "jeff@amazon.com",
    "noreply@stripe.com",
    "not-a-real-address@example.invalid"
  ],
  "discoverPeople": [
    {
      "website": "stripe.com",
      "jobTitles": [
        "engineering"
      ]
    }
  ],
  "domains": [
    "stripe.com",
    "notion.so"
  ],
  "guardMode": "business",
  "onlyWithEmails": true,
  "verifyEmails": false,
  "smtpCheck": false,
  "qualifyByPayment": false,
  "maxDiscoveredPeople": 200,
  "maxItems": 100000,
  "maxConcurrency": 10
}
```

# Actor output Schema

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

One row per address with the deliverability verdict and the checks behind it: syntax, MX record, disposable, role-based and free-provider flags.

# 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": [
        {
            "fullName": "Patrick Collison",
            "companyDomain": "stripe.com"
        }
    ],
    "emailList": [
        "jeff@amazon.com",
        "noreply@stripe.com",
        "not-a-real-address@example.invalid"
    ],
    "discoverPeople": [
        {
            "website": "stripe.com",
            "jobTitles": [
                "engineering"
            ]
        }
    ],
    "domains": [
        "stripe.com",
        "notion.so"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/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": [{
            "fullName": "Patrick Collison",
            "companyDomain": "stripe.com",
        }],
    "emailList": [
        "jeff@amazon.com",
        "noreply@stripe.com",
        "not-a-real-address@example.invalid",
    ],
    "discoverPeople": [{
            "website": "stripe.com",
            "jobTitles": ["engineering"],
        }],
    "domains": [
        "stripe.com",
        "notion.so",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/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": [
    {
      "fullName": "Patrick Collison",
      "companyDomain": "stripe.com"
    }
  ],
  "emailList": [
    "jeff@amazon.com",
    "noreply@stripe.com",
    "not-a-real-address@example.invalid"
  ],
  "discoverPeople": [
    {
      "website": "stripe.com",
      "jobTitles": [
        "engineering"
      ]
    }
  ],
  "domains": [
    "stripe.com",
    "notion.so"
  ]
}' |
apify call memo23/email-finder --silent --output-dataset

```

## MCP server setup

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