Email Finder — Website Emails, Phones & Socials — $2/1k
Pricing
from $2.00 / 1,000 contact rows
Email Finder — Website Emails, Phones & Socials — $2/1k
Email finder for company websites: crawls contact, about, team and imprint pages for emails, phones, socials and addresses. Bulk domains from CSV or Google Sheets, deobfuscation, DNS MX check, empty domains not charged. No login or cookies. MCP-ready for AI agents. $2 per 1,000 records.
Pricing
from $2.00 / 1,000 contact rows
Rating
0.0
(0)
Developer
Khrystyna Skotte
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
17 days ago
Last modified
Categories
Security
Limited permissionsShare
Email Finder — Website Emails, Phones & Socials
Give it a list of company websites and get back the email addresses, phone numbers, social profiles and postal addresses those sites publish, with the page each contact came from. It is an email finder for domains you already have — from Google Maps exports, directories, CRMs or a spreadsheet column — not a people database. No login, no cookies, no browser: plain HTTP, 512 MB of memory, about 5–10 seconds per website.
What makes it different from the other email finders in the Store:
- You are not charged for domains that return nothing. A website with no email, phone or social profile is listed in the run's OUTPUT summary with the reason (
blocked,timeout,no_contacts…) instead of producing a paid empty row. - Every email is auditable. Each address carries the
sourceUrlit was found on and acontextsnippet of the surrounding text, so you can see why it was extracted. - Pages are crawled in the order that matters: home page → contact → imprint/legal notice → about/team/people → support → press → careers → privacy, plus footer links and
sitemap.xmlhints. Ten pages per site is usually enough to find everything. - Obfuscated addresses are decoded:
name [at] domain [dot] com, HTML entities, Cloudflare email protection (__cf_email__), andmailto:links with subjects. - Deduplicated, lowercased, typed. Every email is tagged
generic(info@, sales@, press@…) orpersonal(jason@, first.last@) so you can route role inboxes and named people differently. - DNS MX check (
mxValid) tells you whether the email's domain can receive mail at all.
What you get
Per website (outputMode: perDomain, the default):
| Field | Example | Notes |
|---|---|---|
domain | fsf.org | Registered domain of the website |
websiteUrl | https://fsf.org/ | The URL you supplied, normalised |
websiteTitle | Front Page — Free Software Foundation — … | <title> of the home page |
redirectedTo | null | Set when the home page redirected to another domain |
emails[] | { "email": "info@fsf.org", "type": "generic", "isOffDomain": false, "sourceUrl": "https://www.fsf.org/about/contact/", "context": "…please email info@fsf.org. Thank you…", "name": null, "title": null, "mxValid": true } | One object per unique email; isOffDomain is true when the address is on another domain (gmail, agency, partner) |
emailCount | 8 | |
primaryEmail | campaigns@fsf.org | First generic inbox, else the first email found |
phones[] | ["+16175425942"] | From tel: links, JSON-LD and page text; duplicates in national and international form are merged |
socials{} | { "linkedin": "https://www.linkedin.com/company/creative-commons", "github": "…" } | facebook, instagram, linkedin, twitter, youtube, tiktok, github, pinterest, threads, telegram, whatsapp |
addresses[] | ["31 Milk Street # 960789, Boston, MA, 02196, US"] | JSON-LD PostalAddress and <address> blocks |
people[] | [{ "name": "…", "title": "…", "email": "…" }] | From JSON-LD Person markup (only when includeNames is on) |
mxValid | true | MX record exists for the website's own domain |
pagesCrawled | 10 | |
status | ok | ok, partial_timeout, no_contacts (only with includeEmptyDomains) |
scrapedAt | 2026-10-21T16:16:05.950Z |
Per email (outputMode: perEmail) you get one flat row per unique address: email, type, isOffDomain, domain, websiteUrl, websiteTitle, name, title, sourceUrl, context, mxValid, plus the site's phones, socials, addresses, pagesCrawled, status, scrapedAt. This is the shape to import straight into a CRM or an outreach tool.
What people use it for
- Enrich a Google Maps or directory export. Scrape businesses with any Google Maps actor, feed the website column in here, get emails for the ones that publish them.
- Cold outreach lists. Run a CSV of target domains, filter
type = personalandmxValid = true, and you have named contacts with the page that proves the address is public. - Press and partnership lists.
emailFilter: genericreturns only press@, partnerships@, info@ inboxes across hundreds of sites in one run. - CRM hygiene. Re-run your account list monthly; compare
emails[]with what is in the CRM to catch changed or removed addresses. - Agency prospecting. Pull socials and phones together with the email to see which channels a business is active on before you reach out.
- Compliance and research. The imprint/legal-notice priority finds the legally required contact details on EU websites (Impressum, mentions légales, aviso legal).
Input
All fields are optional. With no input at all the actor runs a sample on ten public organisation websites so you can see the output shape.
One row per domain (default)
{"startUrls": [{ "url": "https://www.fsf.org" }, { "url": "creativecommons.org" }],"maxPagesPerDomain": 10,"maxDepth": 2,"includePhones": true,"includeSocials": true,"includeNames": true,"verifyMx": true,"maxItems": 100}
One row per email, named people only, same-domain only
{"domains": ["apify.com", "basecamp.com", "letsencrypt.org"],"outputMode": "perEmail","emailFilter": "personal","onlySameDomain": true,"maxItems": 500}
Bulk list from a CSV or Google Sheet
{"listUrl": "https://docs.google.com/spreadsheets/d/1AbC…/edit#gid=0","outputMode": "perDomain","maxItems": 2000,"maxConcurrency": 10}
The first column of the sheet or CSV is read (a header such as domain, website or url is skipped). Share the sheet as "anyone with the link". You can combine startUrls, domains and listUrl in one run; duplicates are removed.
All inputs
| Field | Default | What it does |
|---|---|---|
startUrls | — | Websites as { "url": … } objects (the Apify URL list editor) |
domains | — | Websites as plain strings; urls is accepted as an alias |
listUrl | — | CSV, TXT, Google Sheet or Google Drive file with one website per line |
outputMode | perDomain | perDomain or perEmail |
maxItems | 10 | Rows to stop at (domains or emails depending on the mode) |
maxPagesPerDomain | 10 | Pages fetched per site (maxPagesPerSite and maxRequestsPerStartUrl are accepted as aliases) |
maxDepth | 2 | Link depth from the home page; 0 = home page only |
emailFilter | all | all, generic (role inboxes) or personal (named people) |
onlySameDomain | false | Drop emails on other domains (gmail, agencies, partners) |
verifyMx | true | DNS MX lookup per email domain → mxValid |
deobfuscate | true | Decode [at]/[dot], entities, Cloudflare protection |
includePhones / includeSocials / includeNames | true | Toggle the extra signals |
includeEmptyDomains | false | Also save (and pay for) rows for domains with no contacts |
perDomainTimeoutSecs | 60 | A slow site stops after this; what was found is kept |
maxConcurrency | 5 | Websites crawled in parallel |
proxyConfiguration | off | Apify proxy for sites that block datacenter IPs (status: blocked) |
Output example
A real perDomain row from a run on 2026-10-21 (trimmed to three emails):
{"domain": "fsf.org","websiteUrl": "https://fsf.org/","websiteTitle": "Front Page — Free Software Foundation — working together for free software","redirectedTo": null,"emails": [{ "email": "campaigns@fsf.org", "type": "generic", "isOffDomain": false, "name": null, "title": null, "sourceUrl": "https://www.fsf.org/", "context": "Send your feedback on our translations and new translations of pages to campaigns@fsf.org.", "mxValid": true },{ "email": "info@fsf.org", "type": "generic", "isOffDomain": false, "name": null, "title": null, "sourceUrl": "https://www.fsf.org/about/contact/", "context": "olunteer opportunities, shop items, meeting staff, etc., please email info@fsf.org. Thank you for your interest", "mxValid": true },{ "email": "membership@fsf.org", "type": "generic", "isOffDomain": false, "name": null, "title": null, "sourceUrl": "https://www.fsf.org/associate/contact.html", "context": "You can email us with any membership questions or support requests: membership@fsf.org or contact the Free Software Foundation.", "mxValid": true }],"emailCount": 8,"primaryEmail": "campaigns@fsf.org","phones": ["+16175425942"],"socials": {},"addresses": ["31 Milk Street # 960789, Boston, MA, 02196, US"],"people": [],"mxValid": true,"pagesCrawled": 10,"status": "ok","error": null,"scrapedAt": "2026-10-21T16:16:05.950Z"}
And a perEmail row from the same day:
{"email": "press@letsencrypt.org","type": "generic","isOffDomain": false,"domain": "letsencrypt.org","websiteUrl": "https://letsencrypt.org/","websiteTitle": "Let's Encrypt","name": null,"title": null,"sourceUrl": "https://letsencrypt.org/contact/","context": "Email: press@letsencrypt.org","mxValid": true,"phones": [],"socials": { "github": "https://github.com/letsencrypt", "linkedin": "https://www.linkedin.com/company/lets-encrypt" },"addresses": [],"pagesCrawled": 10,"status": "ok","scrapedAt": "2026-10-21T16:17:08.754Z"}
The run's OUTPUT record (key-value store) summarises everything: rows saved, emails found, domains requested, and a failed[] list with a reason for every domain that produced no row.
Pricing
Pay per event, no subscription:
- $0.001 per run start (per GB of memory; the actor runs at 512 MB, so one start event).
- $0.002 per row saved — one website in
perDomainmode (with all of its emails, phones and socials), or one email inperEmailmode.
So 1,000 websites cost about $2 in perDomain mode plus Apify platform usage (roughly $0.2 per 1,000 sites at 512 MB — the test run above processed 10 sites for $0.0017 all-in). Domains that return no contacts are not charged. For comparison, the per-page email extractors in the Store charge $0.001–0.002 per page crawled (so $0.01–0.02 per 10-page site), and the per-row ones $0.02 per site; the big "leads finder" databases charge $0.003 per lead and bill you for leads without emails.
Good to know
- No SMTP verification, by design. Apify blocks outbound port 25, so no actor running on the platform can really talk to a mail server.
mxValidis an honest DNS check:truemeans the domain has mail exchangers,falsemeans mail to that domain will bounce,nullmeans DNS did not answer. Treat "verified email" claims from other cloud actors accordingly. - Sites rendered only by JavaScript (and sites behind aggressive bot protection) may return
status: blockedorno_contacts. Turn onproxyConfigurationwith Apify residential proxy for the blocked ones; a browser-based crawler is the fallback for pure single-page apps. - Email coverage depends on the website. Many small businesses only publish a contact form. Those domains show up in the OUTPUT summary as
no contacts foundand are not charged — the dataset only contains sites that actually publish something. - Junk is filtered. Addresses on third-party infrastructure (mail-archive.com, sentry.io, wixpress.com, schema.org, googlegroups.com, CDNs, platforms…),
noreply@/donotreply@senders, image names likelogo@2x.png, hash-like local parts and other sites' mailing lists (lists.*) are dropped. Off-domain addresses that survive are kept but flaggedisOffDomain: true; setonlySameDomain: trueto drop them too. typeis a heuristic. Known role words, hyphenated roles (license-violation@), plural or-ing/-tionnouns and list servers aregeneric; everything else ispersonal. Filter on it, but spot-check.- Names and titles are filled when a person's name sits next to their email on a page (staff or team pages, author boxes) or in JSON-LD
Personmarkup. On fsf.org's staff page, for example, 12 of 13 emails came back with name and title (zoe@fsf.org→Zoë Kooyman,Executive Director). Contact pages with only role inboxes returnnull. - Politeness: one site at a time is crawled with a short random delay between pages, up to
maxPagesPerDomainpages, insideperDomainTimeoutSecs. The crawl never leaves the website's own domain. - Legal: the actor only collects contact details a company publishes on its own website. You are responsible for using them in line with GDPR, CAN-SPAM and the laws that apply to your outreach.
Integrations
- API:
POST https://api.apify.com/v2/acts/chimerical_quicklime~website-email-finder/run-sync-get-dataset-items?token=…with the JSON input above returns the rows directly; for big lists start a run with/runsand read the dataset when it finishes. - Python / JavaScript:
apify-client—client.actor("chimerical_quicklime/website-email-finder").call(run_input={...})thenclient.dataset(run["defaultDatasetId"]).list_items(). - Schedules + webhooks: schedule the actor on your account list and add a webhook on
ACTOR.RUN.SUCCEEDEDto push new rows into your CRM. - Make, Zapier, n8n: use the Apify module/node, choose this actor, map
email,domain,sourceUrlandmxValidto your sheet or CRM fields.perEmailmode gives one record per row, which is what these tools expect. - MCP / AI agents: add the actor to your Apify MCP server; an agent can call it with a list of domains and read the
OUTPUTsummary plus the dataset. - Export: JSON, CSV, Excel, XML and HTML from the dataset tab or the API.
FAQ
Can it find an email for a specific person? No. It finds the emails a website publishes. For "first name + last name + domain" guessing use a pattern finder; for named decision-makers use a people database.
Why did a domain not appear in the dataset? It produced no email, phone or social within maxPagesPerDomain pages and perDomainTimeoutSecs, or it was blocked. The reason is in the OUTPUT record. Set includeEmptyDomains: true if you want a row for it anyway.
Does it crawl the whole site? No — only up to maxPagesPerDomain pages, chosen by relevance (contact and about pages first), never off-domain.
Can I pass full URLs instead of domains? Yes. A deep URL is crawled from that page; links on it are followed within the same domain.
How fast is it? About 5–10 seconds per website at the defaults; maxConcurrency crawls several websites at once. 1,000 websites take roughly 20–30 minutes at maxConcurrency: 10.