Domain List Email Extractor: Emails & Phones from Websites avatar

Domain List Email Extractor: Emails & Phones from Websites

Pricing

from $1.28 / 1,000 domains

Go to Apify Store
Domain List Email Extractor: Emails & Phones from Websites

Domain List Email Extractor: Emails & Phones from Websites

Domain List Email Extractor API: extract emails from websites in bulk. An email extractor from domains: reads the homepage, contact, about and team pages and returns emails (role or personal), E.164 phones, contact page and social links. One row per domain; pay per domain with contacts.

Pricing

from $1.28 / 1,000 domains

Rating

0.0

(0)

Developer

Data Done Right

Data Done Right

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

This Domain List Email Extractor takes a list of domains or website URLs and returns the emails from websites they publish, plus phone numbers, the contact page and social profile links. It is an email extractor from domains: for each site it reads the homepage and a few contact, about, team, careers or imprint pages (found through the site's own links and common paths such as /contact), and gives you one row per domain, in the same order as your list. No browser, no login, no API key.

It reads what a site publishes in its HTML and nothing else. It does not guess addresses, does not verify mailboxes and does not send email. See Responsible use and Honest limits.

How to use the domain list email extractor

  1. Paste your domains or URLs into Domains or website URLs, one per line (acme.com, https://www.example.org/contact). URLs are cut back to the site root.
  2. Choose how many pages to read per domain (default 5) and whether you want phone numbers, role emails (info@) and personal emails.
  3. Run it. Download one row per domain as JSON, CSV or Excel, or read it through the Apify API.

Every input gets a row, even when nothing is found, so your list stays aligned (the one exception: if a spending limit stops the run early, domains still waiting are not read and get no row). Use inputIndex or the input field to join the result back to your sheet.

Extract emails from websites: what you get

FieldMeaning
statusfound, not_found, blocked, unreachable or invalid (see below)
emails{address, type, sourceUrl, onDomain}: type is role (info@, sales@, support@, hello@, press@...) or personal (jane.doe@), sourceUrl is the page it was published on
phones{e164, raw, sourceUrl}: valid numbers only, normalised offline to E.164 (+14155552671), duplicates merged
contactPageUrlthe domain's contact page, when there is one
socialLinksprofile links by platform (facebook, linkedin, twitter, instagram, youtube, tiktok, github...); share buttons are ignored
pagesCheckedpages requested on the domain
emailAddresses, phoneNumbers, emailCount, phoneCountflat versions of the above for CSV and spreadsheets
reasonplain-language explanation of the status
StatusMeaningCharged
foundAt least one email or phone number was found.yes
not_foundThe site was read, but its HTML publishes no email or phone number (the reason says when it looks JavaScript-rendered).no
blockedrobots.txt forbids reading, the site refuses bots (HTTP 401/403/429/503, bot-check page) or the domain points to an internal address.no
unreachableNo usable answer: DNS failure, timeout, connection error, HTTP 404/5xx.no
invalidNot read: free-mail (gmail.com), social network or large platform, internal name or IP, not a domain, a duplicate of an earlier input, or beyond your maxDomains cap.no

Email extractor from domains: what it reads

  • Pages: the homepage, then links that look like contact, about, team, people, careers, press or imprint pages (also kontakt, contacto, impressum, mentions-legales), then common paths (/contact, /about, /team, /impressum...). Blog, shop and docs pages are skipped. Guessed paths stop after two 404s.
  • Where emails come from: visible text, mailto: links (including percent-encoded ones), JSON-LD data in the page. These are always read.
  • Hidden addresses (Decode hidden addresses, on by default): name [at] site [dot] com, (at) / (dot), {at}, HTML entities such as @, full-width @, and Cloudflare email protection (data-cfemail and /cdn-cgi/l/email-protection links).
  • Placeholders are dropped: you@example.com, name@domain.com, image file names such as logo@2x.png, tracking addresses.
  • Phones: tel: links and numbers in the text, parsed with the offline phonenumbers library. Numbers written without +country code are read in the country of the domain's ending (.de, .co.uk) or the Default phone country you set, otherwise US. Order numbers, dates and IDs are not returned.
  • Roles vs people: a mailbox such as info, sales, support, hello, press, careers, billing or team is role; everything else is personal. The check is by name, so a shared mailbox with an unusual name shows up as personal.
  • Limits per domain: up to 50 emails and 20 phone numbers.

Emails from a list of domains: input example

{
"domains": ["apify.com", "https://www.python.org/about/", "example.com", "gmail.com"],
"pagesPerDomain": 5,
"extractPhones": true,
"includeRoleEmails": true,
"includePersonalEmails": true,
"decodeObfuscated": true,
"concurrency": 10
}

Besides the options above: maxDomains (hard cap, default 1000; domains beyond it get a free invalid row), onlyOwnDomainEmails (drop gmail.com and partner addresses), defaultPhoneCountry ("DE"), delaySecs (default 1), timeoutSecs (default 10) and an optional proxyConfiguration.

Output example

A trimmed row from a real run on the example input (sites change their pages, so your result will differ):

{
"inputIndex": 0, "input": "apify.com", "domain": "apify.com", "status": "found",
"reason": "Found contact details on 5 page(s).",
"emails": [
{"address": "support@apify.com", "type": "role", "sourceUrl": "https://apify.com/contact", "onDomain": true},
{"address": "hello@apify.com", "type": "role", "sourceUrl": "https://apify.com/contact", "onDomain": true}
],
"phones": [],
"emailAddresses": ["support@apify.com", "hello@apify.com"], "phoneNumbers": [], "emailCount": 2, "phoneCount": 0,
"pagesChecked": 5, "contactPageUrl": "https://apify.com/contact",
"socialLinks": {"github": "https://github.com/apify/crawlee", "linkedin": "https://linkedin.com/company/apify",
"twitter": "https://x.com/apify", "youtube": "https://youtube.com/apify", "tiktok": "https://tiktok.com/@apifytech"},
"charged": true
}

A SUMMARY record in the key-value store counts domains by status, charged domains and whether a spending limit stopped the run.

Domain List Email Extractor API

Call it from the Apify API, Python, JavaScript, Make, Zapier or n8n. Example with the Apify Python client:

from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("data-done-right/domain-email-extractor").call(run_input={
"domains": ["acme.com", "https://www.example.org/contact"], "pagesPerDomain": 5})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
print(row["domain"], row["status"], row["emailAddresses"], row["phoneNumbers"])

Join the rows back to your list with inputIndex. Rows with status other than found are free and tell you why in reason.

Use it in Claude, ChatGPT and Cursor

Connect Apify's MCP server (mcp.apify.com) and your AI assistant can pull contact details from a list of sites itself. For example: "extract emails and phone numbers from these 20 company domains and show me which ones are role mailboxes." The only required input is domains.

How much does it cost

Pay per event, with no per-run start fee: $0.0016 per domain with contact details (domain event), charged only when a domain's status is found, so a domain with at least one email or phone number. Domains with not_found, blocked, unreachable or invalid status are free. 1,000 domains with contacts cost about $1.60.

Paid Apify plans get 10% (Silver) to 20% (Gold and up) off. For comparison, the most-used page-based contact scraper on Apify charges $0.002 per scraped page (about $0.01 for a 5-page domain) and one website email scraper $0.02 per website with emails found. maxDomains caps the bill, and if you set a maximum spend per run the Actor stops cleanly when it is reached and keeps the rows already delivered (rows still in progress are not delivered or charged).

Responsible use

Public does not mean free to use. This Actor reads pages that are public and respects robots.txt, but what you do with the addresses is your responsibility.

  • No spam. Do not send unsolicited bulk email. Personal addresses (type: personal) are personal data under GDPR, UK GDPR and similar laws. Turn Include personal emails off if you only need company mailboxes.
  • EU and UK: B2B outreach needs a documented legitimate interest (GDPR Art. 6(1)(f)), an easy way to object and an information notice. Some countries require prior consent. US: CAN-SPAM requires honest headers, a physical address and a working opt-out. Canada (CASL) and others are stricter.
  • Respect opt-outs. Keep a suppression list, honour unsubscribe requests and "do not contact" notes on the page.
  • Store only what you need, for as long as you need it, and delete data when asked.
  • Polite by design. robots.txt is obeyed (a Disallow for the bot is respected; a Crawl-delay slows it down, up to 10 s; 5xx on robots.txt means the site is skipped), requests to one site are spaced out, only a few pages are read, and bot-check pages are not bypassed.
  • Safe by design. The Actor refuses localhost, private networks, link-local and cloud-metadata addresses, also through DNS answers and redirects, and never follows a redirect to another domain. With a proxy, every URL and redirect is still checked, but the connection is not pinned to the checked IP address.
  • Free-mail, social-network and large-platform domains (gmail.com, facebook.com, linkedin.com, github.com, google.com, amazon.com, wikipedia.org and similar) are skipped, so it does not harvest private mailboxes or profile pages.

Honest limits

  • Static HTML only. Contact details that appear only after JavaScript runs (single-page apps, widgets, contact forms that load addresses on click) are not seen. Those domains come back as not_found with a note when the homepage looks script-rendered.
  • Bot protection wins. Sites behind Cloudflare challenges, CAPTCHAs or aggressive WAFs return blocked. The Actor does not try to get around them.
  • Not verified. Emails are what the site publishes. Nothing is sent and no mail server is contacted, so deliverability is unknown. Run your own verification before you send.
  • No pattern guessing. If a site publishes no address you get no address. Use the Work Email Finder for pattern-based likely addresses of named people.
  • Hidden addresses: simple obfuscation is decoded. Emails built by JavaScript, images of addresses, PDFs and contact forms are not.
  • Role vs personal is a name check, not a verdict about who reads the mailbox. Mailing-list addresses can show as personal.
  • Phone numbers: only numbers that are valid for their country are returned; a national-format number in an unusual region needs the Default phone country set.
  • Sites differ. A few pages per domain is a sample, not a full crawl: raise pagesPerDomain (up to 15) for large sites. Subdomains you enter are read as given (eu.acme.com is not shortened to acme.com).

FAQ

How do I extract emails from a list of domains?

Paste the domains into Domains or website URLs and run. You get one row per domain with its emails (role or personal), phone numbers, contact page and social links. Rows are in input order.

Does it find emails that are hidden or obfuscated?

Simple forms, yes: name [at] site [dot] com, (at), HTML entities, full-width @, percent-encoded mailto: links and Cloudflare-protected addresses. Addresses assembled by JavaScript or shown as images are not found.

What if a site has no email?

The row has status not_found (site read, nothing published), blocked or unreachable, with a reason. These rows are free.

Which domains are charged?

Only domains with status found: at least one email or phone number after your settings (role / personal / own-domain filters, phones on or off). Everything else is free, including invalid inputs and duplicates.

Can it find personal Gmail or Outlook addresses of a company's staff?

It does not look up people. It returns addresses a site publishes, including a gmail.com address a small business prints on its contact page (marked onDomain: false; set Only addresses on the site's own domain to drop them). Free-mail domains in your input list are skipped.

Does it respect robots.txt?

Yes. See Responsible use. A site that disallows the bot, or whose robots.txt returns a server error, is not read and gets status blocked.

How fast is it?

Domains are read in parallel (10 by default, up to 50), each one request at a time with a 1 second pause. A 5-page domain takes roughly 5 to 10 seconds, so 1,000 domains take about 10 to 20 minutes at the default settings.

Reading public pages is generally allowed, but using the addresses for marketing is regulated. See Responsible use. This is not legal advice.

  • Work Email Finder: Find Email by Name & Domain (No SMTP): pattern-based likely work emails for named people.
  • Phone Number Validation & Lookup: Format, Carrier, Line Type: check the phone numbers you extract.
  • Company Enrichment API: Domain to Tech, Jobs & Socials: add tech stack, jobs and social profiles to the same domains.
  • Google Sheets Import & Export: Apify Data, No Login: send the results to a Google Sheet.