Email Pattern Finder - Work Email Format by Name + Domain avatar

Email Pattern Finder - Work Email Format by Name + Domain

Pricing

from $1.40 / 1,000 person processeds

Go to Apify Store
Email Pattern Finder - Work Email Format by Name + Domain

Email Pattern Finder - Work Email Format by Name + Domain

Work email finder: give a person's name and a company domain and get ranked address guesses (first.last@, flast@, first@...). Crawls the company site to detect its real email format and pattern, and MX-checks the domain + provider. Confidence-scored, no API key, pay-per-result.

Pricing

from $1.40 / 1,000 person processeds

Rating

0.0

(0)

Developer

Flash Scrape

Flash Scrape

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

0

Monthly active users

13 hours ago

Last modified

Share

Work Email Finder - Email Format & Pattern by Name + Domain

Email pattern finder for any company: find the email address pattern a company uses and turn plain names into ranked work-email guesses. Give it a company domain and a list of full names; it reads a few public pages of the company's own site to observe its real address format, returns each person's most likely addresses (first.last@, flast@, first@…) scored by evidence and pattern frequency, and runs a DNS check that the domain can actually receive mail and which provider runs it (Google Workspace vs Microsoft 365). It's the natural front end of a find → verify → enrich prospecting flow — no API key, no proxies, no per-seat email-finder subscription, just pay-per-result.

What it does

  • Detects the company's actual email pattern from its website (crawlForPattern, on by default): reads up to 9 public pages (home, about, team, people, staff, contact) for real same-domain addresses. If one matches an input name, or has a personal shape (jane.diaz@), that pattern is promoted to 98 confidence and flagged observed for every name. Role aliases (info@, sales@, customer.service@) never count as evidence.
  • Generates up to 12 ranked email candidates per person from a library of common corporate patterns: first.last, flast, firstlast, f.last, first, first_last, firstl, first-last, lastfirst, last.first, lfirst, last.
  • Scores each candidate 0–99 — 98 means observed on the company's own site; below that, the score is the pattern's real-world frequency prior (first.last = 95 … last = 35) — and surfaces the single best_guess per person.
  • Checks the domain's MX records via DNS-over-HTTPS to confirm the domain can receive mail at all — if no mail server exists, every candidate's confidence is cut to 60% of its base, so dead domains can't inflate your list. If the check itself cannot complete, the row says so (mx_status: lookup_failed, mx_found: null) instead of pretending the domain is dead.
  • Detects the email provider from the MX host (Google Workspace, Microsoft 365, Proofpoint, Zoho, or other) — useful both as a deliverability signal and as a firmographic data point.
  • Normalizes names: strips accents (Joséjose), drops titles and credentials (Dr. Jane Doe, PhDjane.doe@) and parenthesised notes, skips duplicates, and handles single-name inputs (those only get the first@ pattern rather than fabricated surnames).
  • Accepts messy domain inputacme.com, https://www.acme.com/about, acme.com:443, even jane@acme.com — it all resolves to the bare domain.
  • Lets you restrict generation to specific pattern IDs when you already know the company's format and just want it applied to a batch of names.

Everything is read-only and privacy-respecting: the actor builds address strings from the names you provide, performs an MX lookup on the domain, and fetches at most nine public pages of the company's website (no proxies, no login). It never sends an email and never probes individual mailboxes.

Use cases

  • Sales prospecting — you found 20 decision-makers on LinkedIn at a target account but no contact info. Paste the names + domain, get ranked address guesses in seconds instead of paying per-credit on Hunter or Apollo.
  • Recruiting outreach — reach engineers and managers directly at their work address instead of fighting the InMail queue.
  • Lead-list completion for agencies — you're delivering a lead list on Fiverr/Upwork and half the rows have a name and company but no email; batch-fill the gap and mark each row with its confidence score.
  • CRM enrichment — export contacts missing an email, group them by company domain, run each group through the finder, and re-import the best_guess column.
  • Pattern discovery — confirm one address for any employee (from a signature, a press release), see which pattern it matches, then re-run the actor with that pattern locked in via patterns for the whole department.

Input

FieldTypeDefaultDescription
domainstring(required)The company's email domain, e.g. acme.com. A URL, port or pasted address is reduced to the bare domain. One domain per run.
namesarray(required)People to generate addresses for, one full name per line (e.g. Jane Doe). Titles/credentials stripped, duplicates skipped. Each usable name is one billed row.
verifyMxbooleantrueLook up the domain's MX records (DNS-over-HTTPS) to confirm it can receive mail and detect the provider. No mailbox probing, no email sent.
crawlForPatternbooleantrueRead up to 9 public pages of the company site for real same-domain addresses and infer the company's actual pattern from them.
maxCandidatesPerPersoninteger6How many ranked addresses to return per person (1–12). The observed pattern, when there is one, is always in the list.
patternsarray(empty)Optional: only generate these pattern ids (see the list above). Unknown ids are ignored and named in the status message. Leave empty for all 12.

Example input:

{
"domain": "stripe.com",
"names": ["Patrick Collison", "Jane Doe"],
"verifyMx": true,
"crawlForPattern": true,
"maxCandidatesPerPerson": 6
}

Output

One dataset row per person:

FieldDescription
name / first_name / last_nameThe input name and its parsed (lower-cased, accent-free) parts
domainNormalized company domain
mx_foundtrue = mail servers found, false = the domain has no MX record, null = check skipped (verifyMx: false) or could not complete — see mx_status
email_providerDetected provider from MX (e.g. Google Workspace, Microsoft 365)
best_guessThe single most likely address
best_guess_confidenceIts 0–99 score (98 = pattern observed on the company site)
pattern_sourceobserved_name_match (an input name's real address was found), observed_structure (a personal address of that shape was found), or heuristic
observed_patternThe pattern id the site evidence points to, or null
evidence_emailThe real address that proved the pattern, or null
observed_emailsUp to 10 same-domain addresses found on the site (raw, may include role aliases)
candidatesAll ranked guesses, each with email, pattern, confidence (and evidence: "observed" on the promoted one)
best_guess_patternThe pattern id of best_guess
mx_statusfound, none, lookup_failed or skipped
pages_crawledHow many company pages were actually read (0 = the site could not be read; null = crawl off)

Example row:

{
"name": "Jane Doe",
"first_name": "jane",
"last_name": "doe",
"domain": "stripe.com",
"mx_found": true,
"email_provider": "Google Workspace",
"best_guess": "jane.doe@stripe.com",
"best_guess_confidence": 98,
"pattern_source": "observed_structure",
"observed_pattern": "first.last",
"evidence_email": "jane.diaz@stripe.com",
"observed_emails": ["jane.diaz@stripe.com"],
"candidates": [
{ "email": "jane.doe@stripe.com", "pattern": "first.last", "confidence": 98, "evidence": "observed" },
{ "email": "jdoe@stripe.com", "pattern": "flast", "confidence": 88 },
{ "email": "janedoe@stripe.com", "pattern": "firstlast", "confidence": 78 },
{ "email": "j.doe@stripe.com", "pattern": "f.last", "confidence": 72 },
{ "email": "jane@stripe.com", "pattern": "first", "confidence": 65 },
{ "email": "jane_doe@stripe.com", "pattern": "first_last", "confidence": 60 }
],
"best_guess_pattern": "first.last",
"mx_status": "found",
"pages_crawled": 4
}

Export to CSV, Excel, or JSON from the Apify Console or via API.

Example output

A sample with the site crawl off (crawlForPattern: false), so every score is the heuristic prior:

namedomainbest_guessbest_guess_confidencemx_found
Patrick Collisonstripe.compatrick.collison@stripe.com95true
John Smithstripe.comjohn.smith@stripe.com95true
Jane Doestripe.comjane.doe@stripe.com95true
Maria Garciastripe.commaria.garcia@stripe.com95true

How the confidence score works

  • 98 — the pattern was observed on the company's own website (pattern_source tells you whether an input name's real address was found, or a personal address of that shape). Strongest signal the actor can give without probing mailboxes.
  • 95 … 35 — the pattern's frequency prior: how common that format is across corporate mailboxes (first.last 95, flast 88, firstlast 78, f.last 72, first 65, first_last 60, firstl 52, first-last 48, lastfirst / last.first 44, lfirst 40, last 35).
  • × 0.6 (floor 5) when the domain has no MX record (mx_status: none) — it cannot receive mail at that apex. This applies to the observed pattern too (98 → 58), which therefore still ranks first and keeps its evidence: "observed" flag.
  • Unchanged when the MX check could not complete (mx_status: lookup_failed) — the actor never turns a DNS outage into "this domain is dead".

Every run comes with a report

Each delivering run also saves a one-page HTML REPORT in the run's key-value store (Output tab → REPORT, or the link in the run's status message): the delivered count, the detected pattern and its evidence, the MX/provider result, the best-guess pattern split and confidence bands, the first 100 rows, and the run's notes. It is a single self-contained HTML file, safe to forward or screenshot. The dataset stays the source of truth.

What the status message and RUN_SUMMARY tell you

  • A successful run's status message states exactly how many person rows were delivered (= billed), how the pattern was decided (observed pattern first.last (evidence …) or heuristic ranking), the mail provider, and every caveat: names skipped or duplicated, a domain with no MX record, an MX check that could not complete, a company site that could not be read (e.g. HTTP 403 from datacenter IPs), a charge-limit trim, or a run-time budget cut.
  • A zero-row run exits SUCCEEDED (no stack trace) with the real cause — no domain, no names, unknown pattern ids, single-word names with two-part patterns only, or a charge limit that leaves no budget — and always ends with "You were not charged." A failed lookup is never reported as "no results".
  • Every run also writes a RUN_SUMMARY key-value record (status, delivered, charge_requested/charge_confirmed, billing, cause, domain, name counts, mx_status, email_provider, pattern_source, observed_pattern, evidence_email, pages_crawled, crawl_errors, deadline_hit, budget_trimmed, report_url) for pipelines that want a machine-readable verdict. status is delivered, zero_rows, push_failed or — when the run crashed before delivering anything — failed with the exception in cause (the run itself then ends FAILED, and the status message says you were not charged). billing records how the rows were billed (pay-per-event, or platform:<model> when the platform bills per dataset item); charge_confirmed is null in the latter case rather than a misleading 0.

Reliability

  • Every network wait (MX lookup, each page read) is bounded by the run's own remaining time with a reserve for delivery, so a short timeoutSecs still returns rows instead of a timed-out empty run.
  • Page bodies are capped at 1 MB and scanned in linear time — one base64-heavy marketing page can no longer stall the run.
  • Rows are pushed in bounded chunks before billing; exactly the pushed rows are charged, once. If a later chunk fails, only the rows that landed are billed.

Pricing

Pay-per-result: you're charged only per person row delivered — a fraction of a cent each. Names that produced no address, duplicates and rows trimmed by your charge limit are never billed. No subscription, no monthly credit packs that expire. A free Apify plan is enough to run your first batches and test the output on real accounts before paying anything.

Tips / FAQ

Does it verify that the mailbox actually exists?

No — and that's deliberate. The actor generates pattern-based guesses ranked by site evidence and real-world commonality and verifies the domain (MX lookup), not individual mailboxes. It performs no SMTP handshakes and sends nothing. For mailbox-level checking, pipe the best_guess column into the Bulk Email Verifier.

How accurate are the guesses?

When the company site publishes even one personal address, the observed pattern (98) is right for most colleagues at companies with standardized IT. Without evidence, first.last@ alone covers the largest share of corporate mailboxes, which is why it scores 95. Accuracy is lower at very large enterprises with legacy formats or duplicate-name collisions. The confidence score tells you how much to trust each row — and if you can confirm one real address at the company yourself, lock its pattern via patterns for the whole batch.

pages_crawled is 0 — what happened?

The company site could not be read from the Apify datacenter (typically HTTP 403 from a bot shield, or the site is down); the status message names the response. Ranking then falls back to the heuristic priors. pages_crawled > 0 with pattern_source: heuristic means the pages were read but published no personal address.

What happens with single names or names with accents or titles?

Accents and diacritics are transliterated (René Müllerrene.muller@…); Dr., Prof., PhD, Jr. and parenthesised notes are dropped. Tokens that are both a credential and a real name are kept as names unless written in their credential form: Anh Doanh.do@, Hon Lamhon.lam@, but Jane Doe, DOjane.doe@ and Hon. Jane Doejane.doe@. A single-token name (e.g. just Madonna) only generates the first@ pattern — the actor won't invent a surname. Names without Latin letters cannot be built and are skipped without charge.

Why did all my confidences drop for one domain?

The MX check found no mail servers (mx_status: none), so every candidate's score was reduced to 60% of its base (floor 5) and mx_found is false. That usually means the domain can't receive mail at that apex — check whether the company actually uses a different email domain. If mx_found is null with mx_status: lookup_failed, the check itself could not complete and scores were left untouched; re-run to get it.

Can I process people from many different companies in one run?

One run handles one domain with any number of names. For a multi-company list, group your rows by domain and trigger one run per domain via the Apify API — each run is billed only for the people it delivers.

How do I integrate it?

Start runs from the Apify API or SDK, schedule recurring runs with Apify Schedules, and export the dataset as CSV/Excel/JSON or push it onward with Apify integrations (webhooks, Zapier, Make). Read RUN_SUMMARY from the run's key-value store for a machine-readable verdict.


Found a bug or missing a feature? Open an issue on this actor's Issues tab — typical response within 1 business day.


More from Flash Scrape

Same publisher, same rules: no API keys, pay per row, filters run before billing.