Decision-Maker Email Finder avatar

Decision-Maker Email Finder

Pricing

from $20.00 / 1,000 email founds

Go to Apify Store
Decision-Maker Email Finder

Decision-Maker Email Finder

Find real, named contact emails for any company straight from their own website — not generic info@ addresses. Crawls team/about pages, generates verified patterns, checks MX validity, and scores confidence transparently.

Pricing

from $20.00 / 1,000 email founds

Rating

0.0

(0)

Developer

CANCAP

CANCAP

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

17 days ago

Last modified

Categories

Share

Finds real, named contact emails for a company by crawling only that company's own public website — no LinkedIn, no login-walled platforms, no paid third-party email-verification APIs.

What it does

  1. Takes a list of companies (domain + optional name and target job titles).
  2. Crawls a small set of likely pages on the company's own website: homepage, /about, /team, /contact, etc.
  3. Extracts:
    • Emails already on the page (mailto: links, visible text)
    • Names + job titles from team/about sections (best-effort heuristic — real-world HTML varies)
  4. If no email is found directly but a named person is, generates likely email patterns (first.last@domain, flast@domain, etc.) — pure logic, no API.
  5. Optionally verifies candidate emails for free:
    • MX lookup (DNS) — confirms the domain can receive mail at all
    • SMTP handshake probe (RCPT TO check, no message ever sent) — estimates deliverability
  6. Falls back to generic addresses (info@, contact@) only if nothing better was found, or if explicitly requested.
  7. Outputs one row per candidate email with a transparent 0–100 confidence score.

Why "zero cost to the builder"

Every step uses either:

  • The user's own Apify compute (billed to whoever runs the actor, via Apify's usual platform-usage billing), or
  • A free, no-account-needed mechanism (DNS MX lookup, raw SMTP socket connection, regex/HTML parsing).

No step calls a paid API (no Hunter.io, NeverBounce, ZeroBounce, Clearbit, etc.) and no step requires you, the actor's author, to hold any third-party API key or pay for any external service.

Changelog

v0.1.1 — fixes from real-world testing on Windows

A user ran this on a real Windows machine against apify.com and caught two real bugs:

  1. mxValid incorrectly returned false for a domain with valid MX records. Root cause: DNS resolution can intermittently fail on some Windows/VPN/corporate network setups (no error was being surfaced, just a silent failure treated as "no MX records"). Fixed by adding a retry and explicit error logging in mxCheck.js, so failures are now diagnosable in the run log instead of silently miscategorized.
  2. A project showcase block was misidentified as a person. Apify's homepage features community projects (e.g. a tool with "Founder" in its description), and the original heuristic loosely matched any short Title-Case heading near the word "founder" as a person + job title — producing garbage like personName: "Hlidac Shopu" with a run-on sentence as the job title. Fixed by making extractPeople() in pageExtract.js require the "title" to come from a genuinely separate sibling element, be short (≤60 chars), and not contain sentence punctuation — rejecting description blurbs while still matching real <h3>Jane Doe</h3><p>CEO</p>-style team cards.

Both fixes were verified with targeted unit tests (one positive case, one reproducing the exact false-positive pattern) plus a full end-to-end re-run. See git history / ask the author for the before/after test output if you want to verify yourself.

  1. ECONNREFUSED on MX lookup, specifically on a Windows machine using a mobile hotspot. The OS-level resolver (Windows nslookup) worked fine for MX queries on that same network, but Node's built-in DNS resolver (c-ares) failed to even reach a DNS server. This is a known class of issue where Node's resolver path behaves differently from the OS's own resolver on certain Windows network setups (VPNs, hotspots, some routers). Fixed by explicitly setting public DNS servers (dns.setServers([...]) — Google + Cloudflare) instead of relying on whatever the OS hands Node by default. This makes MX lookups more robust across the wide range of networks real users will run this actor from, not just the one we debugged.

Honest limitations (please read before publishing)

  • SMTP verification is a signal, not a guarantee. Gmail, Microsoft 365, and many providers accept all RCPT TO requests at the SMTP layer and only bounce later — so smtpCheck: "accepted" means "didn't reject it immediately," not "100% confirmed deliverable." This is documented in the output schema description on purpose, so users aren't misled.
  • Some networks block outbound port 25 entirely. When that happens, smtpCheck returns "unknown" rather than a false negative. This is expected and handled gracefully — it will NOT crash the run.
  • Name/title extraction is heuristic. Team page HTML varies enormously across the web. It will work well on many sites and miss on others. This is a best-effort layer, not a guarantee — the confidence score reflects that (pattern-generated emails always score lower than found-on-page emails).
  • This does not scrape LinkedIn or any login-walled site. That's intentional — it keeps the actor legally and technically simple, and avoids account-ban risk entirely.

What I tested before handing this to you, and what I could NOT fully test

I ran this in a sandboxed environment with restricted outbound networking (only a fixed domain allowlist), so I was able to fully verify:

  • ✅ All JSON config files parse correctly (actor.json, input_schema.json, dataset_schema.json, package.json)
  • npm install completes cleanly with no errors
  • ✅ Email pattern generation logic (unit tested directly)
  • ✅ Confidence scoring logic (unit tested directly)
  • ✅ MX/DNS lookups against a real domain (apify.com — returned real Google MX records)
  • ✅ Page extraction (emails + people) against sample HTML, including correctly rejecting an image filename like logo@2x.png
  • ✅ Full end-to-end run via npm start against a real live website (github.com), producing clean, correctly-shaped output rows with sensible fallback behavior

What I could not fully verify from my sandbox:

  • ⚠️ SMTP probing (smtpProbe.js) against a real mail server. My sandbox's network blocks outbound port 25 entirely (confirmed directly — connections to aspmx.l.google.com:25 silently time out with no error, even via raw curl). The code handles this failure gracefully and returns "unknown" rather than crashing, which I confirmed. But I could not confirm it returns "accepted"/"rejected" correctly against a live mail server, because my network can't reach port 25 at all.

When you run this on real Apify infrastructure, please specifically check:

  1. Run with verifyEmails: true against a domain where you know a real email exists, and confirm smtpCheck returns "accepted" (not always "unknown").
  2. Run against a domain you know does NOT have a particular address, and confirm smtpCheck returns "rejected" rather than always defaulting to "unknown".
  3. If smtpCheck is always "unknown" no matter what, that means Apify's network also restricts outbound port 25 for your account/plan — in that case, tell me and I'll adjust the actor to make SMTP verification optional-with-a-clear-warning rather than a default-on feature.

Local testing

npm install
# Edit storage/key_value_stores/default/INPUT.json with your test input (see input_schema.json for shape)
APIFY_LOCAL_STORAGE_DIR=./storage npm start
# Check storage/datasets/default/*.json for output

Deploying to Apify

npm install -g apify-cli
apify login
apify push

Then open the actor in the Apify Console, set your input, click Start, and check the Output tab.

Pricing suggestion

Set this up as Pay Per Event (PPE) with an event like email-found (charged only when email is non-null and source is not "none"). This way users only pay for results, which builds trust faster than charging per page crawled.