Contact Email Finder — B2B Email Discovery & Verification avatar

Contact Email Finder — B2B Email Discovery & Verification

Pricing

from $20.00 / 1,000 email verifieds

Go to Apify Store
Contact Email Finder — B2B Email Discovery & Verification

Contact Email Finder — B2B Email Discovery & Verification

Company domain in, verified email addresses out. MX checks, pattern detection, deliverability scoring. Stop bouncing.

Pricing

from $20.00 / 1,000 email verifieds

Rating

0.0

(0)

Developer

Creator Fusion

Creator Fusion

Maintained by Community

Actor stats

0

Bookmarked

97

Total users

11

Monthly active users

4 days ago

Last modified

Share

Company domain in. Verified emails out. A typed contact-discovery API for sales and outreach agents: Crawlee scrape of the company site + MX validation + role-pattern fallback + 1-5 deliverability confidence score. Returns typed JSON rows — one row per domain, schema below. A Hunter.io / Apollo alternative at a fraction of the cost.

Why agents use this actor

  • Deterministic typed output — every row matches the published dataset schema; emails[] entries always carry address, source, found_on, mx_valid, confidence, format.
  • Confidence-scored + MX-validated — filter on confidence >= 4 or mx_valid = true instead of guessing which addresses are real.
  • Cost-predictable — per-event pricing ($0.03 start + $0.02 per result), no proxy by default, safe for autonomous budgets.
  • No auth, cookies, or login — crawls public company pages only.
  • Clear error semantics — invalid input fails fast with exit code 1 and a status message; no silent SUCCEEDED-with-0-items runs, no billable error rows in the dataset.
  • Never zero results — role-pattern fallback (info@, contact@, sales@, ...) guarantees candidates even when the site exposes no addresses.

Input schema

FieldTypeRequiredDefaultDescription
domainsarray of stringsno*["stripe.com"] (prefill)Company domains to find emails for (e.g. stripe.com). Preferred field.
urlsarray of stringsno*LEGACY. URLs accepted for v0.0.4 compatibility; hostname is extracted and treated as a domain.
urlstringno*LEGACY single-URL field. Use domains for new integrations.
include_patternsbooleannotrueAlways return common-format role candidates (info@, contact@, ...) even if not scraped.
mx_validatebooleannotrueVerify the domain has working MX servers before scoring. Disable to save ~500ms/domain.
max_emails_per_domainintegerno10Cap on emails returned per domain (1-50).
maxUrlsintegerno30LEGACY alias: cap on total domains/URLs processed (1-200).
proxyConfigurationobjectnooffProxy is OFF by default to save you money. Enable Apify Proxy only on 403/429s; proxy data is billed to YOUR Apify account.

* At least one of domains / urls / url must yield a valid domain, or the run fails with exit code 1.

Email Output Schema

One dataset row per input domain. All fields present in every row.

FieldTypeNullableDescription
domainstringnoNormalized bare hostname (e.g. stripe.com).
emailsarraynoDiscovered emails, sorted by confidence desc, capped at max_emails_per_domain.
emails[].addressstringnoLowercase address, always @<domain>.
emails[].sourcestringno"scraped" (found on the site) or "pattern" (generated role candidate).
emails[].found_onstringyesPage URL where scraped. null for source="pattern".
emails[].mx_validbooleannoDomain has working MX records. false if lookup failed or mx_validate disabled.
emails[].confidenceintegerno1 (low) to 5 (high) deliverability score — see table below.
emails[].formatstringnorole, first.last, firstlast, firstinitial.last, first_last, first, last.first, f.last, or unknown.
mx_recordsarray of stringsnoMX exchange hosts, lowest priority first. Empty if none or mx_validate disabled.
summary.scraped_countintegernoReturned emails with source="scraped".
summary.pattern_countintegernoReturned emails with source="pattern".
summary.high_confidence_countintegernoReturned emails with confidence >= 4.
scraped_atstringnoISO 8601 timestamp for the domain's processing start.
errorstringyesLegacy field on rows from older builds only; current builds never push error rows.

Real example row:

{
"domain": "stripe.com",
"emails": [
{
"address": "careers@stripe.com",
"source": "scraped",
"found_on": "https://stripe.com/careers",
"mx_valid": true,
"confidence": 5,
"format": "role"
},
{
"address": "info@stripe.com",
"source": "pattern",
"found_on": null,
"mx_valid": true,
"confidence": 3,
"format": "role"
}
],
"mx_records": ["aspmx.l.google.com", "alt1.aspmx.l.google.com"],
"summary": { "scraped_count": 3, "pattern_count": 7, "high_confidence_count": 3 },
"scraped_at": "2026-08-15T04:00:28.381Z"
}

Confidence scoring (1-5)

SourceMX validFormat matchScore
scrapedyesrole/first.last5
scrapedyesother4
scrapednorole/first.last4
patternyesrole3
patternnorole2

Error semantics

  • No valid domains in input → run FAILS with exit code 1 and status message "No domains provided...". Nothing is pushed to the dataset (you are not billed for error rows).
  • Unexpected runtime error → run FAILS with exit code 1 and "Run failed: <message>" — never a silent SUCCEEDED.
  • Per-domain crawl failure (site down, hard blocks) → the domain still yields a row with pattern candidates; the failure is recorded in the SUMMARY key-value record under failed_crawls: [{domain, error}].
  • Zero scraped emails → not an error; pattern fallback fills the row.

Agents: retry on non-zero exit; treat SUMMARY.failed_crawls as per-item degradation, not run failure.

Use from AI agents (MCP)

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=apricot_blackberry/contact-email-finder",
"headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
}
}
}

Works in Claude, Cursor, ChatGPT deep research connectors, and any MCP client; the input schema above is the tool's parameter schema.

Use from code

curl:

curl -X POST "https://api.apify.com/v2/acts/apricot_blackberry~contact-email-finder/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domains": ["stripe.com"], "mx_validate": true, "max_emails_per_domain": 10}'

JavaScript (apify-client):

import { ApifyClient } from "apify-client";
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor("apricot_blackberry/contact-email-finder").call({
domains: ["stripe.com"],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Python (apify_client):

from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("apricot_blackberry/contact-email-finder").call(
run_input={"domains": ["stripe.com"]}
)
items = client.dataset(run["defaultDatasetId"]).list_items().items

Use from automation platforms

  • n8n / Make / Zapier: use the native Apify integration and pick contact-email-finder by name.
  • LangChain / LlamaIndex: use the Apify actor tool wrappers with actor id apricot_blackberry/contact-email-finder.
  • Webhooks: configure an Apify webhook to fire on run completion (ACTOR.RUN.SUCCEEDED/FAILED) to push results into your pipeline without polling.

Pricing

$0.03 start + $0.02 per result delivered (PAY_PER_EVENT).

Cost transparency — what you pay for

This actor's per-event pricing covers email-discovery work. Two other Apify costs may apply, and they're billed directly to your Apify account, not to us:

CostDefaultWho controls it
Compute units (CU)~0.001-0.01 CU per domainApify (based on runtime)
Apify ProxyOFF by defaultYOU — enable only if needed
Storage (dataset)~free for small runsApify

Proxy specifically: unless you turn it on, the actor crawls public company pages directly with no proxy. Saves you proxy data fees ($1+/GB on residential). If you're getting 403/429s on hostile domains, enable Apify Proxy in the input — but know that Apify will bill the proxy data to your account, on top of the actor's per-event price.

FAQ

Does it verify deliverability by sending mail? No — it validates MX records (the domain can receive mail) and scores confidence from source + format. No SMTP probing, no emails sent.

Why do I get pattern emails I didn't ask for? include_patterns defaults to true so runs never return zero results. Set it to false to get only scraped addresses (a domain with an empty scrape still falls back to patterns).

Are legacy inputs still supported? Yes — urls, url, and maxUrls from v0.0.4 keep working unchanged.