# Email Format Finder by Domain (`b2bdata/email-pattern-by-domain`) Actor

Moved to https://apify.com/b2bdata/company-email-format-lookup (new price, same job). This older copy is deprecated and kept for reference.

- **URL**: https://apify.com/b2bdata/email-pattern-by-domain.md
- **Developed by:** [Open Enrich](https://apify.com/b2bdata) (community)
- **Categories:** Lead generation, Marketing
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 email pattern delivereds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 Format Finder by Domain

Email format finder by domain: give a list of company domains, get the work email pattern each company uses (first.last, flast, first and more), with the number of people behind it. No login, no cookies, no start fee. You pay only for a pattern you receive.

**No login | Domains, URLs or emails in | One pattern per row | Thin evidence skipped, never guessed | Unknown domains = free | JSON/CSV export**

> **Domain in, email format out.** Cold outreach needs the shape of a company's addresses: is it `first.last@`, `flast@` or `first@`? This actor reads up to 500 of a company's current people, looks at the addresses on record for them on that domain, and counts which name-based format each address follows. The format most people fit is the answer. It returns the pattern and the evidence, not a list of addresses. Where the evidence is thin, you get a free notice instead of a guess.

[See all actors by b2bdata](https://apify.com/b2bdata)

***

### 💳 Pricing and plans

One event, `email-pattern`, charged once per pattern row delivered. Prices per 1,000 patterns (set on the Pricing tab):

| Event | When it is charged | Free plan | Gold plan |
|---|---|---|---|
| `email-pattern` | A pattern row is delivered to your dataset | $1.796 | $1.396 |

Bronze and Silver prices are on the Pricing tab. Higher Apify plans only lower the price. Empty runs, unknown domains, skipped domains, failed lookups, unreadable entries and notices are free, and there is no start fee.

**Plan-credit math.** The free plan's $5 monthly credit covers about 2,780 patterns at the Free price.

#### Success-only billing

You are charged only for a pattern that passes three floors: at least 5 people confirm it, at least 10 people have an address on the domain, and at least 40% of those people follow it. Everything else gets one free notice row and is listed in `MISSES` with its position and a reason: `unresolved_domain`, `no_addresses`, `no_pattern`, `weak_evidence`, `low_confidence`, `shared_domain` or `unreadable_answer`. A notice never shows the pattern. An entry that is not a domain, a repeat, or a lookup that fails is listed in `MISSES` and costs nothing.

#### The free plan

Free Apify accounts get up to 100 domains looked up per run, and as many runs as their Apify credit covers. Nothing is stored on our side. When a list is longer, the first 100 valid domains are looked up and one free notice row (`reason: "free_plan_limit"`) says how many were not looked up; they are not charged. Run again with the rest, or use a paid Apify plan for no per-run limit.

#### Keep a run small while you test

`limit` caps the whole run (default and maximum 800). Try one domain with `limit: 1`, read the row, then scale up. A lookup takes about 3 to 4 seconds, so 800 domains take about 45 minutes.

***

### ⚡ Why this email pattern actor?

- **Evidence, not a bare guess.** Every row says how many people confirm the pattern, what share of the people with an address follow it, and which other formats appeared.
- **It does not guess on thin data.** One person fitting a format, or two, is not a company format. Those return a free notice.
- **No addresses leave the actor.** Sample addresses are masked by the data (`j***.d**@acme.com`). You get the shape, not a contact list. To find a person's address, combine the pattern with a name or use [email-finder](https://apify.com/b2bdata/email-finder).
- **Success-only, no start fee.** Unknown domains, hosting platforms, thin patterns, bad entries and repeats are free.

***

### 🎯 Use cases: turn a domain column into email formats

- **Cold outreach lists.** You hold names and company domains and need the address shape before you build addresses.
- **Account research.** Check whether a target account uses `first.last@` or `flast@` before a campaign.
- **List cleaning.** Spot addresses that do not follow the company's own format.
- **Agent workflows.** An AI agent holds a domain and needs the address convention in a single call.

***

### 🚀 Quick start: email formats for a list of domains

#### 1. One domain

```json
{ "items": ["stripe.com"], "limit": 1 }
```

#### 2. A list, in any form

```json
{ "items": ["openai.com", "https://www.shopify.com/about", "sales@airbnb.com"], "sampleSize": 300 }
```

Each entry becomes its bare domain before the lookup.

***

### ⚙️ Inputs

| Key | Type | Default | What it does |
|---|---|---|---|
| `items` | array of strings | required | Company domains, website URLs or email addresses. A company name is not a domain and is listed in `MISSES` as `invalid_domain`. The same domain written twice is looked up once. Free plan: 100 domains per run. Maximum 800 entries. |
| `sampleSize` | integer | 200 | How many of the company's current people are read for evidence, 50 to 500. Small companies are read in full whatever you set. The price does not change. |
| `limit` | integer | 800 | Most patterns delivered in the whole run, which also caps what is charged. Maximum 800. Domains not reached are listed in `MISSES`. |
| `includeAudit` | boolean | false | Adds an `audit` object to each row. |

Any other field stops the run before a lookup with `remove unsupported fields: ...`. Nothing is charged.

***

### 📤 Output

One result row per domain with a credible pattern. Rows keep input order. Empty values are left out.

| Key | Type | Meaning, and when it is missing |
|---|---|---|
| `domain` | string | The domain from your entry, cleaned |
| `pattern` | string | The winning format, see the table below |
| `emailFormat` | string | The pattern written out: `first@stripe.com` |
| `example` | string | A masked address that follows the pattern |
| `confirmations` | integer | People whose address on record follows the pattern |
| `agreement` | number | `confirmations` divided by `withAddress`, 0 to 1 |
| `withAddress` | integer | People read who have an address on the domain on record |
| `sampled` | integer | People read |
| `coverage` | number | `withAddress` divided by `sampled`, 0 to 1 |
| `otherPatterns` | array | Other formats at least one person fits: `{pattern, confirmations}`, best first. Can be empty |
| `samples` | array | Up to three masked addresses on the domain |
| `audit` | object | Only with `includeAudit`: `requestId`, `latencyMs`, `sampleSize`, `inputRaw` |

Example output (a real answer for stripe.com, 2026-11-06):

```json
{
  "domain": "stripe.com",
  "pattern": "first",
  "emailFormat": "first@stripe.com",
  "example": "c*****@stripe.com",
  "confirmations": 50,
  "agreement": 0.595,
  "withAddress": 84,
  "sampled": 200,
  "coverage": 0.42,
  "otherPatterns": [
    { "pattern": "fl", "confirmations": 13 },
    { "pattern": "firstlast", "confirmations": 5 },
    { "pattern": "flast", "confirmations": 5 }
  ],
  "samples": ["c*****@stripe.com", "g*****@stripe.com", "b******@stripe.com"]
}
```

#### Pattern names

`first` is the first name, `last` the last name, `f` or `l` a first or last initial. So `first.last` is jane.doe@, `flast` is jdoe@, `firstl` is janed@, `first_last` is jane_doe@, `last.first` is doe.jane@. The data also names `firstlast`, `first-last`, `f.last`, `first.l`, `lastfirst`, `last_first`, `lfirst`, `l.first`, `last.f`, `lastf`, `last`, `f_last` and `fl` (taken as first initial plus last initial).

#### How to read the rows

- **`agreement` is the number to watch.** `bbc.co.uk` came back at 0.91 (first.last, 68 people), `hubspot.com` at 0.86 (flast), `stripe.com` at 0.60 (first). A company that changed its format over time or lets people pick an alias scores lower; the rows still return the winner with its evidence.
- **It is a pattern read from addresses on record, not a delivery test.** A pattern says how a company usually writes addresses. It does not prove that a given address exists or accepts mail. Verify before you send, with [email-verifier](https://apify.com/b2bdata/email-verifier).
- **Large companies are read in part.** The default reads 200 current people. `coverage` shows how many of them had an address on record, typically 30% to 70% in tests.
- **Shared domains can mix companies.** When a few companies declare one domain, the evidence comes from all of them. Domains declared by more than 50 companies (public mail, website builders) are refused for free (`shared_domain`).
- **Small companies are often skipped.** With fewer than 10 people with an address, the row is a free `low_confidence`, `no_addresses` or `unresolved_domain` notice.

#### Other records in the run

- **`MISSES`** (key-value store): `[{index, input, reason}]` for every entry without a result row. Reasons: the notice reasons above, `invalid_domain`, `duplicate_input` (the same domain as an earlier entry; `duplicateOf` names it), `not_looked_up_limit` (the run `limit` was reached first), or `Upstream HTTP <code>` / `Upstream unavailable`.
- **`errors-email-pattern-by-domain`** (key-value store): the lookups that failed for a technical reason, so you can re-run only those domains.
- **`RUN_SUMMARY`**: counts (requested, valid, looked up, patterns delivered, how many per pattern).
- **Notice rows** (`type: "notice"`) appear for every free outcome, a free-plan list longer than 100 (`free_plan_limit`), a reached `limit` (`limit_reached`), a reached spend limit (`charge_limit_reached`) or invalid input (`invalid-input`). Notices are never charged.

#### What is NOT returned

- Real email addresses (samples are masked)
- A verified or deliverable address (use [email-verifier](https://apify.com/b2bdata/email-verifier))
- An address for a named person (use [email-finder](https://apify.com/b2bdata/email-finder))
- The company behind a domain (use [company-by-domain](https://apify.com/b2bdata/company-by-domain))

***

### 🛡️ Reliable results

**Our data promise.** We work to keep the data current, and we stand behind it. If a pattern looks wrong for a company you know, a company is missing or a row looks off, open the Issues tab with your run link. We review every report and sort it out with you, case by case.

- **No data is free and visible.** Every domain without a row is in `MISSES` with its position and a reason and is never charged.
- **No guesses on thin evidence.** Fewer than 5 confirming people, fewer than 10 addresses or less than 40% agreement returns a free notice.
- **Repeats are charged once.** The same domain written twice is one row.
- **Bounded pace.** Lookups run one after another, about 3 to 4 seconds each, so a long list takes time. The run timeout is one hour.
- **Typos fail early.** An unsupported input field, a bad `sampleSize` or `limit` or a list above 800 entries stops the run before any charge.

***

### 🤖 For AI agents (MCP)

This actor runs through the Apify MCP server. Prompt to copy:

> "Use b2bdata/email-pattern-by-domain with items=\["stripe.com"] and limit=1. Return pattern, emailFormat and agreement."

**When to use it.** You hold a company domain and need the shape of its work email addresses.
**When not to.** You need a person's address (use b2bdata/email-finder), a deliverability check (use b2bdata/email-verifier) or the company (use b2bdata/company-by-domain).
**Input contract.** `items` array of domains, URLs or emails (at most 800); optional `sampleSize` 50-500, optional `limit` 1-800.
**Output contract.** One row per domain with `domain`, `pattern`, `emailFormat`, `confirmations`, `agreement`, `withAddress`, `sampled`. Output is deterministic and schema-stable. Domains without a credible pattern are a free notice row and listed in `MISSES`.
**Smoke test.** `limit: 1` on a well-known domain returns one row.
**Error cases.** `No valid domains` means no entry was a domain. `remove unsupported fields`, a bad number or too many entries ends the run SUCCEEDED with one free `invalid-input` notice row and nothing charged.

***

### 🔌 Integrations: send results anywhere

1. **API.** Fetch rows from the run's default dataset: `GET /v2/datasets/{datasetId}/items?format=csv`.
2. **Webhook** on run success to push rows to your own endpoint.
3. **n8n, Make, Zapier.** Use the native Apify nodes: run the actor, then read the dataset.
4. **Google Sheets, Airtable, HubSpot, Salesforce, Clay.** Join on `domain` and use `emailFormat` to build addresses from names.

***

### 🕐 Schedule examples

- **Weekly list prep.** A schedule with a saved task that points at your target-account domain list.
- **New-account enrichment.** A webhook from your CRM starts a run with the new domains.

***

### 🔗 Related actors

- `b2bdata/email-finder`: the work email of one named person at a company. [Open](https://apify.com/b2bdata/email-finder)
- `b2bdata/email-verifier`: check whether an address is deliverable before you send. [Open](https://apify.com/b2bdata/email-verifier)
- `b2bdata/company-by-domain`: the company behind a domain, with industry and size. [Open](https://apify.com/b2bdata/company-by-domain)
- `b2bdata/people-at-company`: the people at a company, one row per person, by domain. [Open](https://apify.com/b2bdata/people-at-company)

[See all actors by b2bdata](https://apify.com/b2bdata)

***

### ❓ FAQ

#### Data and coverage

**How do I find the email format of a company?**
Put the company domain in `items` and run. You get one row with the pattern, such as `first.last`, and how many people confirm it.

**Why did a domain return a notice instead of a pattern?**
No company is on file for it, nobody read has an address on it, the addresses follow no name-based format, or too few people confirm one format. All are free and are in `MISSES` with the reason.

**Why is `gmail.com` refused?**
It is a public mail domain, declared by thousands of companies, so it has no company email format. It is a free `shared_domain` notice.

**Is the pattern always right?**
No. It is the format most people on record follow. `agreement` shows how strongly. Companies with several formats, aliases or recent changes score lower. Verify addresses before you send.

**Do I need a LinkedIn account or cookies?**
No. It is a lookup without login, cookies or an account to ban.

#### Pricing

**What do I pay when a domain has no pattern?**
Nothing. There is no start fee, and unknown domains, skipped domains, thin patterns, bad entries, repeats and notices are free.

***

### 🛟 Support

Use the **Issues** tab on this actor's Apify page for bugs, questions and feature requests. We answer within 48 hours on working days. Please include the run ID and the input you used. Published on Apify by b2bdata. Not affiliated with, endorsed by, or sponsored by any company or person named in the examples above.

# Changelog

This Actor's version history is a separate document: https://apify.com/b2bdata/email-pattern-by-domain/changelog.md

# Actor input Schema

## `items` (type: `array`):

Company domains to look up. Example: \["stripe.com", "https://www.shopify.com/about", "jane@openai.com"]. A website URL or an email address is reduced to its domain; a company name is not a domain and is listed in the MISSES record as invalid_domain. Each domain is looked up once; repeats are listed in MISSES as duplicate_input. A domain with no credible pattern returns one free notice row. Free plan: 100 domains per run. Maximum 800 per run.

## `sampleSize` (type: `integer`):

How many of the company's current people are read for evidence. More people means a steadier pattern on large companies; small companies are read in full whatever you set. From 50 to 500, default 200. The price per pattern does not change.

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

Most email patterns delivered in the whole run, which also caps what is charged. Domains not reached are listed in MISSES and are not charged. Default and maximum 800.

## `includeAudit` (type: `boolean`):

Adds requestId, latencyMs, sampleSize and inputRaw to each row under audit. Omit for clean rows.

## Actor input object example

```json
{
  "items": [
    "stripe.com"
  ],
  "sampleSize": 200,
  "limit": 800,
  "includeAudit": false
}
```

# Actor output Schema

## `patterns` (type: `string`):

One row per domain: the pattern, how many people confirm it, other formats seen

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

Counts, patterns seen and stop reason for this run

# 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 = {
    "items": [
        "stripe.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2bdata/email-pattern-by-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 = { "items": ["stripe.com"] }

# Run the Actor and wait for it to finish
run = client.actor("b2bdata/email-pattern-by-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 '{
  "items": [
    "stripe.com"
  ]
}' |
apify call b2bdata/email-pattern-by-domain --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,b2bdata/email-pattern-by-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/w8qsaw6a6LZk7L7MR/builds/m41c9p9ptrizxLphc/openapi.json
