Website Contact & Email Scraper
Pricing
from $3.00 / 1,000 verified contacts
Website Contact & Email Scraper
Extract emails, phone numbers and social profiles from any list of websites. Finds an email on 73% of domains - and you pay only for contacts found.
Pricing
from $3.00 / 1,000 verified contacts
Rating
0.0
(0)
Developer
Rafal Sav
Maintained by CommunityActor stats
2
Bookmarked
3
Total users
0
Monthly active users
3 days ago
Last modified
Categories
Share
What does Website Contact & Email Scraper do?
Website Contact & Email Scraper is an accurate email scraper and contact extractor: give it any list of websites and it pulls emails, phone numbers and social media profiles into clean, structured data — one record per site, with MX-validated emails and a confidence score — ready for your CRM, outreach tool or lead-generation pipeline.
It runs on the Apify platform, so you get API access, scheduling, integrations (Make, Zapier, n8n), proxy rotation and run monitoring out of the box.
Why use Website Contact & Email Scraper?
Most contact scrapers miss data that is right there on the page — social links, obfuscated emails, contacts hidden on subpages — and return lists full of junk and empty fields. This Actor is built around one thing: accuracy. Fewer garbage rows, more real hits, validated and scored.
On a random sample of 150 company domains it found an email on 73.3% of them, against 54.0% for the category incumbent, at a lower cost per email found — the numbers and the method are below.
Use cases
- Sales prospecting — you have a list of target companies from Crunchbase, a directory or a Google Maps scrape, and you need a reachable inbox for each. Feed the domains in, get
best_emailper company, push it to your CRM. - Lead-list enrichment — an existing list has company names and websites but no contacts. This fills in emails, phones and social profiles without paying a per-record data-provider fee.
- Agency and freelancer outreach — scrape the websites of a niche (dentists, law firms, e-commerce shops in one city) and get the contact page details that a Google Maps listing does not carry.
- Recruiting — collect careers and HR contacts from a list of employer sites, including the ones that hide addresses behind Cloudflare or
name [at] domain. - Market and competitor research — map which companies in a segment publish a phone number, which only take contact forms, and which social platforms they actually use.
- GDPR / imprint compliance checks — verify that a list of sites publishes a reachable data-controller contact on its privacy policy or
Impressumpage.
How the accuracy engine works
For every site it goes deeper and recovers data other scrapers drop:
- Deep crawl — not just the homepage, but the
/contact,/about,/teamand/impressumsubpages where contacts actually live (bounded bymaxContactPages, and it stays on the site's own domain). - Hidden-email recovery — decodes Cloudflare
data-cfemail/email-protectionlinks, un-obfuscatesname [at] domain [dot] com, and readsmailto:links and JSON-LD / schema.org metadata. - Validation & scoring — checks each email against its domain's MX records, drops example/placeholder/asset noise, flags generic role inboxes (
info@,sales@), and attaches a confidence score so you can pick thebest_email. - Phones & socials — parses phone numbers into E.164 with Google's libphonenumber, and extracts LinkedIn / X / Facebook / Instagram / YouTube / TikTok profiles while filtering out share/intent links.
It respects robots.txt and rate-limits its requests.
How to use Website Contact & Email Scraper
- Add one or more URLs under Start URLs.
- (Optional) Set Max contact subpages, toggle Validate emails / Extract social profiles, set a Default phone region, or switch JavaScript rendering to
autofor sites that need a browser (raise run memory to 2048 MB first). - Click Start.
- Download your results as JSON, CSV, Excel or HTML from the Output tab, or pull them via the Apify API.
Input
| Field | Type | Description |
|---|---|---|
startUrls | array | Websites to extract contacts from (one record per URL). Required. |
maxContactPages | integer | How many contact-like subpages to deep-crawl per site (0 = homepage only). Default 5. |
validateEmails | boolean | Validate emails via MX records and attach a confidence score. Default true. |
includeSocials | boolean | Extract LinkedIn / X / Facebook / Instagram / YouTube / TikTok profiles. Default true. |
renderJs | string | off (default), auto or on — load pages in a real browser. See JavaScript rendering. Needs ≥2048 MB run memory. |
phoneRegion | string | ISO country code (e.g. PL, US) for parsing local-format phone numbers. Empty = auto-detect from the domain's TLD, then from page signals (address country, og:locale, html lang) so local numbers are still recovered on .com/.org sites. |
proxyConfiguration | object | Optional proxy. Apify Proxy needs a paid plan; disabled by default (direct connection). |
JavaScript rendering (renderJs)
Some sites hand a plain HTTP client nothing useful: they block non-browser clients outright (HTTP 403), or they build their footer and contact details client-side. Set renderJs to reach them.
| Mode | What happens | When to use it |
|---|---|---|
off (default) | No browser. Fastest and cheapest. | Most sites — the accuracy engine already handles obfuscation, JSON-LD and Cloudflare-protected addresses. |
auto (recommended) | Scans without a browser first; if a site yields no contact at all, the whole site is re-crawled in a browser. | Mixed lists of sites, where you don't know in advance which ones need a browser. |
on | Always uses a browser. | You already know your targets are browser-only. |
auto is the cheap option by design: the browser starts only for sites the fast path could not read, so a list of ordinary sites costs the same as renderJs=off. On the four browser-only sites in our golden set this is the difference between 0-28% recall without a browser and 100% with one, while the browser stays switched off for the other 28 sites.
Requirements: auto and on need at least 2048 MB of run memory (the browser needs roughly a gigabyte of its own). The default 512 MB is enough for renderJs=off. Each record carries a rendered flag so you can see which sites needed a browser.
What a browser does not fix: some sites reject requests based on the IP address rather than the client, and a datacenter IP — which is what every cloud run uses — gets an HTTP 403 whether or not a browser is driving it. renderJs reaches sites whose content is built in JavaScript and sites that turn away plain HTTP clients; it does not defeat IP-reputation protection. For those you need residential proxies (proxyConfiguration, paid Apify plans). When this happens the run log says so explicitly (Browser got HTTP 403 for …) instead of quietly returning nothing.
Output
You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.
{"domain": "https://acme.example","status": "ok","best_email": "anna.nowak@acme.example","emails": [{ "email": "anna.nowak@acme.example", "valid_mx": true, "role": false, "confidence": 0.9 },{ "email": "info@acme.example", "valid_mx": true, "role": true, "confidence": 0.85 }],"phones": ["+48221234567"],"socials": {"linkedin": ["https://linkedin.com/company/acme"],"instagram": ["https://instagram.com/acme"]},"email_count": 2,"pages_crawled": 4,"rendered": false}
When a site comes back unreachable
unreachable means no page was downloaded at all — the site refused the request or timed out — so it is not the same as "this company has no contact details". Nothing is charged for it. Two things are worth trying before you write such a domain off:
- Set
renderJstoauto. Sites that turn away plain HTTP clients often serve a real browser normally. - Add a proxy under
proxyConfiguration. Some sites reject datacenter IP addresses no matter what client asks, and any cloud run uses one; residential proxies (paid Apify plans) are the way around that.
A site that answered but simply has nothing to give is reported as no_contacts, with pages_crawled showing how many pages were read.
Data fields
| Field | Description |
|---|---|
domain | The site the record is for. |
status | ok (contacts found), no_contacts (pages read, nothing to find) or unreachable (the site could not be fetched — see above). |
best_email | Highest-confidence email (usually a named person over a role inbox). |
emails | All emails found, each with valid_mx, role and confidence (0–1). |
phones | Phone numbers normalized to E.164. |
socials | Social profile URLs grouped by platform, deduplicated per account (a site linking both twitter.com/acme and x.com/acme yields one profile, not two). |
email_count | Number of kept emails. |
pages_crawled | How many pages were fetched for this site. |
rendered | Whether a browser was needed for this site (see renderJs). |
How it compares
We benchmarked this Actor against the category incumbent (vdrmota/contact-info-scraper, 12M+ runs) on an independently hand-verified set of 28 real websites - German legal Impressum pages, US nonprofits, SaaS contact pages, PL companies - each scraping a single page, no deep crawl, no browser, so it is a like-for-like test of extraction accuracy. Ground truth was read out of each page's raw HTML by hand and both Actors were scored identically.
| Field | Website Contact & Email Scraper | Incumbent |
|---|---|---|
| Emails (recall) | 100% | 94% |
| Phone numbers (recall) | 100%, clean E.164 | 0% usable |
| Social profiles (recall) | 100% | 98% |
The biggest differences generalize to any site, not just the benchmark:
-
Phone numbers you can actually use. We return normalized E.164 numbers. The incumbent returned zero confident phone numbers across all 28 sites - it dumps raw strings into a separate "uncertain" bucket, mixing genuine numbers with garbage like
888 888 88888and leaving the formatting to you. -
Hidden emails other scrapers drop. We recover Cloudflare-protected addresses, HTML-entity and
name at domainobfuscations, and emails that live only in JSON-LD/schema.org or a<link rel="author">- cases where the incumbent returns nothing. And we don't invent them: prose like a heading "Advertising at PHP.net" is not turned into an email. -
Browser rendering without the browser-sized bill. Browser-only sites are covered by
renderJs(see above), and the contacts it recovers cost exactly the same as any other — a flat fee per contact. The incumbent bills browser usage as a separate paid extra on top of its per-page fee.
Head-to-head on an uncurated list
We ran this Actor and the incumbent (vdrmota/contact-info-scraper, 12M+ runs) over the same 150 randomly sampled company domains on the same day, at the same crawl depth:
| Metric | This Actor | Incumbent |
|---|---|---|
| Domains with an email | 73.3% | 54.0% |
| Emails found | 192 | 117 |
| Phone numbers | 12 usable (E.164) | 4 confident, 38 unnormalized |
| Cost for the run | $0.89 | $0.97 |
| Cost per email found | $0.0046 | $0.0083 |
On 30 domains only we returned an address; the reverse happened once. Full method, configuration and caveats: benchmark/head_to_head_yc150.md.
What it yields on a list you didn't curate
Accuracy on hand-picked pages is one thing; what matters when you buy a run is how many domains actually give you something. We scanned 300 randomly sampled Y Combinator companies (public API, seed 42 — a deliberately hard list: modern JS-heavy startup sites, often with a contact form instead of an address):
| Metric | Value |
|---|---|
| Yielded at least one contact | 81.3% |
| Yielded an email | 60.7% |
| Emails found | 247 — 47% addressed to a person, 95% MX-valid |
| Could not be fetched | 1.0% |
| Cost | $1.33 for 300 domains (~$0.0044 per domain) |
Re-running only the domains that yielded nothing, with renderJs: auto, recovered 24 of 56 (43%) for a further $0.09 — taking coverage of the list from 81.3% to 89.3%, and domains with an email from 60.7% to 66.3%.
Full write-up and reproduction steps: benchmark/field_report_yc300.md.
The full per-site table is in benchmark/vdrmota_comparison.md; you can reproduce it with python benchmark/run_benchmark.py. The golden set now holds 32 sites — the 28 above plus 4 browser-only ones added to prove the renderJs path (python benchmark/run_benchmark.py --render-js auto, which scores 100% precision and recall across emails, phones and socials).
Check the numbers yourself
The golden set, the 300-domain sample list and the scoring script are published as a public dataset: github.com/RafalSav/website-contact-scraper-benchmark. Every accuracy claim on this page can be audited or re-run against any scraper's export. Corrections to the labels are welcome.
Scraping German sites?
There is a variant tuned for the DACH market: Impressum Email Scraper — same engine, with Impressum-first crawling, DE phone parsing and /datenschutz as a fallback source.
How much does it cost?
You pay only for what is found — never for empty pages. This Actor uses Apify's pay-per-event pricing, and the price follows what a result is actually worth to you:
- Verified contact — $0.003 (~$3 per 1,000): an email or a phone number, i.e. something you can reach a person at.
- Social profile — $0.001 (~$1 per 1,000): a clean, de-duplicated profile link. A footer link is worth less than a verified inbox, so it costs a third as much (and
twitter.com/acme+x.com/acmecount once, not twice). - Actor start: Apify's standard ~$0.05 per 1,000 runs.
- Free to try: the Apify Free plan includes $5 in monthly credits.
Turn off includeSocials if you only want reachable contacts — then social links are neither extracted nor billed.
Because you are billed per result and not per page, deep-crawling extra subpages to recover a hidden email never inflates your bill on sites that have nothing to give. You can also cap spend per run with the maximum cost setting — the Actor stops charging as soon as your budget is reached.
FAQ
How do I find the email address of a company from its website?
Paste the company's domain into Start URLs and run the Actor. It opens the homepage, then the contact-like subpages (/contact, /about, /team, /impressum, and privacy or terms pages as a fallback), collects every address it can prove is real, and returns the most promising one as best_email. No manual clicking through pages.
How many emails will I actually get?
On hand-picked contact pages, effectively all of them (100% recall on our 32-page verified benchmark). On an uncurated list of company domains, expect an email on roughly two thirds to three quarters of them: on 150 random company sites we returned an email for 73.3%. Sites that only offer a contact form are the usual reason for a miss — nobody can scrape an address that was never published.
Does it verify that the emails are valid?
Yes. Every address is checked against its domain's MX records (valid_mx), filtered against placeholder and asset noise, flagged as a role inbox (info@, sales@) or a named person, and scored with a confidence value between 0 and 1. Unlike most competitors, verification is included in the price rather than sold as a paid add-on.
What is the difference between best_email and the emails list?
emails holds everything found. best_email is the single highest-confidence pick — a named person's address outranks a general contact inbox, which outranks a technical or legal mailbox. It is the field to use when you want exactly one contact per company.
Can it scrape sites that need JavaScript?
Yes, via renderJs. Set it to auto (recommended) and a real browser is used only for sites the fast path could not read, so an ordinary list costs the same as running without a browser. Note that a browser does not defeat IP-reputation blocking — see renderJs.
Why did a site come back empty?
Check the status field. no_contacts means pages were read and there was genuinely nothing to extract (typically a contact-form-only site). unreachable means nothing could be downloaded — retry with renderJs: auto or a residential proxy. Empty results are never charged.
How much does it cost to scrape 1,000 websites?
You pay per contact found, not per page. On the 300-domain field test the total was $1.33 for 300 domains (~$0.0044 each), which extrapolates to roughly $4.4 per 1,000 typical company websites — and domains that yield nothing cost you nothing. The Apify Free plan's $5 monthly credit covers about a thousand domains at that rate. Curated contact pages, which are richer in contacts, cost more per domain; empty ones cost zero.
Can I run it on a schedule or from my own code?
Yes. It is a standard Apify Actor: schedule it in the Console, call it from the Apify API or any of the client libraries, or connect it to Make, Zapier or n8n. The output dataset exports as JSON, CSV, Excel or HTML.
Is scraping contact details from websites legal?
Collecting business contact details that a company publishes on its own website is generally permitted, but the responsibility for how you use them is yours: under GDPR and similar regimes you are the data controller, you need a lawful basis for outreach, and you must honour opt-outs. This Actor respects robots.txt and rate-limits its requests. See the disclaimer below.
How is this different from the other contact scrapers on Apify?
Two things: it recovers addresses others drop (Cloudflare-protected, HTML-entity and name at domain obfuscation, JSON-LD, <link rel="author">, privacy-policy pages), and it charges for results rather than pages, so a deep crawl that finds nothing is free. Every accuracy claim in this README comes from a benchmark you can re-run yourself.
Disclaimer
This Actor is a tool. You are the data controller for any personal data you collect and are responsible for using it in compliance with applicable laws (GDPR and equivalents), the target site's Terms of Service, and robots.txt. It does not build or sell contact databases.
Support
Found a site where contacts were missed? Open an issue on the Issues tab — accuracy reports directly shape the roadmap.