B2B Contact & Email Finder — Decision Makers by Domain avatar

B2B Contact & Email Finder — Decision Makers by Domain

Pricing

from $0.02 / contact

Go to Apify Store
B2B Contact & Email Finder — Decision Makers by Domain

B2B Contact & Email Finder — Decision Makers by Domain

Turn a company domain into named decision makers: full name, job title, seniority, department, LinkedIn URL and a work email built from the person's name, with a live DNS/MX check and full company firmographics attached to every contact.

Pricing

from $0.02 / contact

Rating

0.0

(0)

Developer

Yuliia Kulakova

Yuliia Kulakova

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

4 days ago

Last modified

Share

B2B Contact & Email Finder

B2B Contact & Email Finder

Give it a company domain, get named decision makers back. Full name, job title, seniority, department, LinkedIn URL and company firmographics — plus a work-email address for each person, with the domain's mail setup checked live and the exact evidence attached to every record.


What it does

Point the actor at one or more company domains. For each one it:

  1. Reads the company's own public pages, and falls back to a real browser for the ones that are rendered by JavaScript.
  2. Extracts named people — full name, job title, seniority, department and any publicly linked LinkedIn / social profiles — plus company firmographics (founded year, HQ, phone, tech stack, description, social profiles).
  3. Builds the work email when it isn't published. For a named person with no address on the page it generates the likely work-email patterns ({first}.{last}@domain, …), picks the highest-priority one, and records which pattern produced it in emailPattern.
  4. Checks the address — syntax, role account, disposable domain and a live DNS/MX lookup of the domain — and returns a status, a 0-100 confidence score, the raw evidence and a timestamp.

Built for sales teams doing targeted outbound, recruiters sourcing named people at specific companies, and agencies building or cleaning contact lists for clients.

It works from public company websites plus generated patterns — it does not scrape LinkedIn and does not resell a third-party contact database.


What "checked" means here — read this before you buy

Email checking has levels, and this actor tells you exactly which level each record got.

CheckWhat it provesRuns on Apify
Syntaxthe address is well formed✅ always
Role accountinfo@, sales@ … — a shared inbox, not a person✅ always
Disposable domainthrowaway mail provider✅ always
Live DNS / MX lookupthe domain really accepts mail, and through which server✅ always
Mailbox probe (SMTP RCPT TO)that one specific mailbox exists❌ needs outbound port 25

The mailbox probe does not run on Apify. Apify — like almost every cloud provider — filters outbound port 25, so no actor running there can complete an SMTP handshake with a mail server. The code for it is in the actor and switches on by itself wherever the port is open (emailVerification: "full"), but on the platform it detects the block, writes smtpBlocked: true into the record and falls back to the MX check. It never invents a verdict it could not confirm.

So what you get per person is: a real name, a real title, and a pattern-built work address on a domain that is confirmed to accept mail — reported as

emailStatus: "unknown"
with emailConfidence: 50, because that is what was actually established. If a tool promises you a per-mailbox valid verdict from inside a cloud platform, ask it which port it used.

Addresses that are genuinely published on the company site are returned as found, not guessed — the source field tells you which is which.


Key features

Per-record evidence, not a black-box score

  • emailStatus — on this platform you will see unknown (domain accepts mail, mailbox not provable), role, disposable or invalid. The values valid and catch-all exist in the code but require the mailbox probe, so they do not occur on Apify.
  • emailConfidence — 0-100. What you will actually see: MX confirmed = 50, role = 40, disposable = 8, invalid or no MX = 0-3. (catch-all = 55 and a probed mailbox = 95 belong to the mailbox probe.)
  • emailVerificationmode, mxFound, mxHost, smtpChecked, smtpBlocked, smtpCode, catchAll, disposable, role and a human-readable note.
  • lastVerifiedAt — ISO-8601 stamp for freshness.

Browser-rendered team-page extraction

When the plain HTTP crawl finds zero people (JS-rendered leadership/team pages), a real browser renders them and re-runs the same extractors. Controlled by renderJs (auto = only when needed, always, off).

Company firmographics

Every contact carries a nested company object: name, website, industry, size, revenue range, founded year, HQ address, phone, LinkedIn, social profiles, detected tech stack and description.

No invented people, and no address that implies a job someone doesn't have

Every name and title comes off the page as written — nothing is inferred. People who appear on a company page but are not employees — board members, investors, advisors, partners at other firms — are returned with their real title and affiliation: "board/external", and their company address is left null on purpose rather than built from a pattern.

The work address itself is either found on the site or built from the person's name, and the record always says which: emailPattern names the pattern used, and source says where the person came from (<domain> (team page), <domain> (json-ld), <domain> (site), <domain> (role-probe), apollo (…)).

Word-accurate title filtering, and titles that carry two hats

Seniority and department are matched on whole words. Asking for CTO returns CTOs — not every Director; asking for COO does not sweep in every Coordinator.

A title often belongs to more than one bucket: "CEO & Co-Founder" is both c_suite and founder, "Chief Product & Technology Officer" is both product and engineering. Every matching label is kept in seniorityAll / departmentAll, and your seniorities / departments filters match against all of them — so filtering for the C-suite returns the founder-CEO instead of quietly dropping them. seniority and department stay single-valued for convenience and hold the primary label.

DNC / suppression list

suppressionList drops any email or whole domain you paste in — a simple opt-out / GDPR do-not-contact control applied before results are written.

Role / shared-inbox separation

Generic inboxes (info@, sales@, support@, …) are never counted as people. The opt-in roleEmailProbe writes them to a separate role-inboxes dataset, so they never inflate your contact count and are never billed as contacts.

Optional: Apollo firmographic search (bring-your-own-key)

If you need free-text firmographic people search with no seed domain (industry / title / seniority / headcount at scale), set mode: "search" and supply your own Apollo API key (apolloApiKey). The query runs on your Apollo account, quota and terms; Apollo returns the people and firmographics, and this actor builds and checks the emails. In this mode domains and companyNames act as employer filters on the Apollo query — they are not also crawled, so a search run never quietly bills you for a site crawl you did not ask for. This is a secondary add-on — most users should use the default domain mode, which needs no external key.


Input

The default and recommended mode is domainToContacts. Minimal example:

{
"mode": "domainToContacts",
"domains": ["hubspot.com", "datadoghq.com"],
"jobTitles": ["VP Marketing", "Head of Sales", "CFO"],
"emailVerification": "mx",
"requireEmail": true,
"maxResults": 50,
"maxPagesPerDomain": 12,
"renderJs": "auto",
"suppressionList": ["optout@example.com", "competitor.com"],
"proxyConfiguration": { "useApifyProxy": true }
}

Core

FieldTypeDefaultDescription
modeenum domainToContacts | searchdomainToContactsPipeline mode. domainToContacts crawls the domains you give it; search is the optional Apollo/firmographic path.
domainsarray<string>["hubspot.com"]Company domains to mine (e.g. hubspot.com). In search mode, an employer filter.
companyNamesarray<string>[]Company name filter / seed.
personNamestring""Filter or seed by a specific person's name.
maxResultsinteger25Maximum contacts to output.

Contact filters & output control

FieldTypeDefaultDescription
jobTitlesarray<string>[]Keep people whose title contains any of these as a whole word (case-insensitive).
excludeJobTitlesarray<string>[]Drop people whose title matches any of these.
senioritiesarray<enum>[]founder, c_suite, partner, vp, head, director, manager, senior, entry.
departmentsarray<enum>[]executive, engineering, product, sales, marketing, finance, hr, operations, legal, it.
locationsarray<string>[]Include locations (city / state / country substrings).
excludeLocationsarray<string>[]Exclude locations.
keywordsarray<string>[]Include keywords found on company pages.
excludeKeywordsarray<string>[]Exclude keywords.
matchLogicenum AND | ORANDCombine the filters above with AND or OR.
requireEmailbooleantrueOnly return contacts with a non-invalid email.
requirePhonebooleanfalseOnly return contacts with a personal phone number. The site crawl yields company numbers, not per-person ones — with this on, domainToContacts runs usually return nothing. Useful mainly in Apollo search mode.
dedupbooleantrueDeduplicate by email + LinkedIn URL.
suppressionListarray<string>[]Emails or domains to always drop (opt-out / DNC).

Email verification

FieldTypeDefaultDescription
emailVerificationenum off | mx | fullmxoff = syntax / role / disposable only, no network; mx = also a live DNS/MX lookup; full = additionally attempt a mailbox probe, which needs outbound port 25 and falls back to mx automatically where the port is filtered (it is on Apify).
emailPatternsarray<string>8 defaultsPatterns generated for named people. Tokens: {first} {last} {f} {l} {domain}.
smtpTimeoutMsinteger8000Timeout per SMTP connection. Only used by emailVerification: "full"; where port 25 is filtered this is how long each blocked attempt waits before falling back.
phoneEnrichmentbooleanfalseAlso collect phone numbers found on the company site into company.phone.
roleEmailProbebooleanfalseOpt-in. Also check common shared inboxes (info@, sales@, …). Written to a separate role-inboxes dataset; never counted as people and never billed.

Crawl & rendering

FieldTypeDefaultDescription
maxPagesPerDomaininteger12Crawl budget per company domain.
renderJsenum auto | always | offautoBrowser fallback for JS-rendered team pages. auto renders only when the HTTP crawl finds 0 people.
proxyConfigurationobject{ "useApifyProxy": true }Proxy for the site crawl and the browser fallback. Residential works best on protected sites.

Firmographic search filters (search mode)

FieldTypeDefaultDescription
domainMatchenum strict | containsstrictHow to match domains in search mode.
industriesarray<string>[]Industry filter.
headcountRangesarray<enum>[]1-1010001+.
revenueRangesarray<enum>[]<1M>1B.
fundingStagearray<enum>[]bootstrapped, pre_seed, seed, series_aseries_e_plus, ipo, acquired.
techStackarray<string>[]Required technologies.

Apollo (optional, bring-your-own-key)

FieldTypeDefaultDescription
apolloApiKeystring (secret)""Your own Apollo master API key. Enables free-text firmographic people search with no seed domain. Runs on your Apollo account / quota / ToS.
apolloSearchUrlstring""Optional. Paste an Apollo people-search URL; its filters are parsed into the search. Requires apolloApiKey.
apolloRevealEmailsbooleanfalseOptional. Unlock each email via Apollo enrichment (consumes your own Apollo credits). Off = emails are built from name patterns and checked against DNS/MX at no extra cost.

Output

One flat record per person, with a nested company object. Below is a real record from a run against hubspot.com in mx mode, trimmed only by dropping the empty fields. This address was not published on the site — it was built from {first}.{last}@{domain}, and what was confirmed is that the domain accepts mail. The record says exactly that.

{
"recordType": "person",
"fullName": "Yamini Rangan",
"firstName": "Yamini",
"lastName": "Rangan",
"jobTitle": "Chief Executive Officer",
"seniority": "c_suite",
"seniorityAll": ["c_suite"],
"department": "executive",
"departmentAll": ["executive"],
"affiliation": null,
"companyName": "HubSpot",
"companyDomain": "hubspot.com",
"linkedinUrl": "https://www.linkedin.com/in/yaminirangan",
"email": "yamini.rangan@hubspot.com",
"emailStatus": "unknown",
"emailConfidence": 50,
"emailPattern": "{first}.{last}@{domain}",
"emailVerification": {
"mode": "mx",
"mxFound": true,
"mxHost": "smtp.google.com",
"smtpChecked": false,
"smtpBlocked": false,
"smtpCode": null,
"catchAll": null,
"disposable": false,
"role": false,
"note": "MX found; SMTP not attempted (mx mode)"
},
"lastVerifiedAt": "2026-09-09T14:43:54.515Z",
"source": "hubspot.com (team page)",
"company": {
"name": "HubSpot",
"domain": "hubspot.com",
"website": "https://hubspot.com",
"size": "5001-10000 employees",
"foundedYear": "2006",
"hq": "2 Canal Park, Cambridge, MA, 02141, US",
"phone": "+1888HUBSPOT",
"linkedinUrl": "https://www.linkedin.com/company/hubspot",
"techStack": ["Google Analytics", "HubSpot", "Cloudflare", "Google Fonts", "Webflow"],
"description": "HubSpot is an AI-powered customer platform with software for marketing, sales, customer service, content management, operations, and commerce.",
"dataFreshness": "2026-09-09T14:43:54.523Z"
}
}

When the mailbox probe is blocked

Running emailVerification: "full" on a network that filters port 25 — which includes the Apify platform — produces this, a real captured object. Note that it reports the block instead of returning a verdict:

"emailStatus": "unknown",
"emailConfidence": 50,
"emailVerification": {
"mode": "full",
"mxFound": true,
"mxHost": "aspmx.l.google.com",
"smtpChecked": false,
"smtpBlocked": true,
"smtpCode": null,
"catchAll": null,
"disposable": false,
"role": false,
"note": "SMTP unreachable (ETIMEDOUT); fell back to MX-only"
}

Because the fallback is automatic and the outcome is the same as mx, running full on Apify only costs you the timeout per domain. Use mx.

No company address for people who don't work there

Board members, investors and partners at other firms are returned with their real title and no company address — like this:

{
"fullName": "Shardul Shah",
"jobTitle": "Partner, Index Ventures",
"companyName": "Datadog",
"email": null,
"emailStatus": null,
"emailConfidence": null,
"affiliation": "board/external",
"source": "datadoghq.com (team page)"
}

Field reference — person: recordType, fullName, firstName, lastName, jobTitle, headline, seniority, seniorityAll, department, departmentAll, affiliation, affiliationReason, companyName, companyDomain, linkedinUrl, email, emailStatus, emailConfidence, emailPattern, emailVerification, lastVerifiedAt, personalEmail, phone, mobile, city, state, country, socials[], source. Company:

company.{ name, domain, website, industry, size, revenueRange, totalFunding, marketCap, foundedYear, hq, phone, linkedinUrl, socials[], techStack[], description, dataFreshness }
.


Pricing

You pay for contacts, not for runtime. The full price list is two entries:

EventWhen it is charged
lead-returnedOne contact record written to your dataset — the person, their title and labels, LinkedIn URL, work address with its evidence, and the whole company object.
apify-actor-startOnce when a run starts (Apify charges one of these per GB of the memory you run with).

What is deliberately not on that list:

  • Board members, investors and partners at other firms are returned for context but are not charged as contacts.
  • Role / shared inboxes (info@, sales@ …) go to a separate dataset and are not charged either.
  • Email checking is not billed separately. A separate "email verified" charge would be billing you for a mailbox probe that cannot run on this platform, so there isn't one.
  • Phone numbers are not billed separately. In Apollo mode the number came out of your own Apollo credits; charging again for it would be charging twice.

Set maxResults to cap what a single run can cost you.


Use cases

  • Targeted outbound to specific companies — you already have an account list; feed the domains in and get named decision makers with work addresses for your sequencer.
  • Account-based marketing (ABM) — map the buying committee at each target account (title + seniority + department) with company firmographics attached.
  • Recruiting / sourcing — find named people at specific target employers without scraping LinkedIn.
  • List enrichment — attach titles, seniority, department, LinkedIn URLs and firmographics to a list of domains you already have.

Honest limitations

  • No per-mailbox verification on this platform. Outbound port 25 is filtered on Apify, so the SMTP RCPT TO probe cannot complete and no valid / invalid per-mailbox verdict is produced here. You get syntax, role, disposable and a live MX check. If you need a per-mailbox verdict, run the output through a dedicated verification service before you send. The actor is explicit about this in every record rather than dressing a guess up as a confirmation.
  • A pattern-built address is a best guess on a confirmed domain. The pattern used is in emailPattern, and source tells you whether the address was found on the site or built.
  • Catch-all domains can't be confirmed per mailbox by anyone. Where detectable they are returned as catch-all (confidence 55), flagged honestly, not as valid.
  • Coverage depends on the public site. People and titles come from the company's own team/leadership/about pages. Some companies deliberately publish none — Stripe is one: it has no leadership page at all, so a run against stripe.com returns firmographics and zero named people. That is the site being empty, not the actor failing, and the log says so explicitly.
  • Firmographic people-search without a seed domain needs your own Apollo key. The default domain mode needs no external key and is what most users should use.
  • No LinkedIn scraping. LinkedIn URLs are surfaced only when publicly linked on the site; the actor does not log in to or scrape LinkedIn.

FAQ

Where do the emails come from? From the company's own site (mailto: links, contact/team pages) and, for named people without a published address, from generated name patterns. Nothing is bought or resold from a third-party database.

Is the email verified? The domain is verified live — MX lookup, plus syntax, role and disposable checks. The individual mailbox is not, because that requires outbound port 25, which this platform filters. Each record states exactly which checks ran.

What does emailConfidence mean? A 0-100 score derived from the evidence actually collected: catch-all domain = 55, MX confirmed but no mailbox probe = 50, role account = 40, disposable = 8, invalid or no MX = 0-3. The raw evidence sits in emailVerification, so you can set your own threshold.

Should I use emailVerification: "full"? Not on Apify. It attempts a mailbox probe, waits out the blocked connection per domain and then falls back to mx anyway — same result, slower. Use mx.

Do board members and shared inboxes count as contacts? No. External/board people are returned with affiliation: "board/external" and no company email; role inboxes (opt-in) go to a separate role-inboxes dataset. Neither is billed.

Do I need an Apollo key? No — the default domainToContacts mode needs no external key. An Apollo key only unlocks the optional free-text firmographic search mode.

Is this compliant for cold outreach? The actor supports responsible use: a source field for provenance, a suppressionList for opt-out/DNC, and field minimization. B2B cold email is generally defensible under legitimate interest (GDPR Art. 6(1)(f)), but you are the data controller — keep a documented basis and honour opt-outs.