To scrape email addresses from a list of websites, paste your domains or page URLs into the Website Email Scraper on Apify. The Actor visits each site, checks likely contact pages and saves the published email addresses it finds. Download the results as CSV, keeping the website and source page beside every contact.
This works with a website list from a spreadsheet, CRM, directory or Google Maps export. You can run it through the Apify input form without code, or ask an AI agent connected through Apify MCP to prepare the list, run the crawl and organize the results.
Step 1. Prepare a website list you can trace back
Keep an original sheet with a row ID, business name and website URL. Copy the sites into a separate crawl list. Deduplicate identical inputs there, while preserving their connection to every original business row.
The Actor accepts bare domains such as example.com and full URLs such as https://example.com/contact. When you already know the contact page, submit that URL directly. Keep meaningful location paths if different branches publish different contacts.
- Choose five public business websites for a first test.
- Remove blank cells and spaces around each copied URL.
- Keep one website input per item in the Actor form.
- Keep full contact or branch-page paths where they matter.
- Save the input list with your source row IDs before starting.
A domain may serve several locations. Crawl a shared homepage once if you need the company’s general inbox, then map that result back to the source rows. For local contacts, keep the relevant branch URLs separate and verify each email’s source page.
Step 2. Set up the email Actor in Apify Console
Open the Website Email Scraper on Apify, click Try for free and sign in if prompted. Open the Actor’s editable Input form in Apify Console.
- Paste the five website inputs into Domains or website URLs.
- Choose
allWebsitesfor Result mode during this test. - Set Maximum saved results to 5, giving every test website room for a saved record.
- Use 3 under Pages to check per website.
- Keep Check JavaScript-rendered contacts enabled and Same-domain crawl only on.
- Set
includePersonalDatato false if you mainly need generic company inboxes. Disable phone and social extraction if you only need email. - Review pricing, set a run spending limit and click Start.
The public input reference lists the current controls. The screenshot is a read-only reference; your website list goes into the editable Console form.

JSON template for a small test
Replace the two placeholder URLs below with real sites. For two distinct inputs, a maxResults value of 2 leaves room for both website records.
{
"domains": [
"https://YOUR_FIRST_BUSINESS_WEBSITE",
"https://YOUR_SECOND_BUSINESS_WEBSITE"
],
"maxResults": 2,
"resultMode": "allWebsites",
"maxPagesPerWebsite": 3,
"includePersonalData": false,
"extractPhones": false,
"extractSocials": false,
"sameDomainOnly": true,
"renderJavaScript": true
}The Actor starts with the supplied URL or homepage, then follows promising internal pages such as contact, about, team, legal or imprint pages. Three pages is a shallow first pass. You can raise the page limit for sites where the contact evidence suggests a deeper check would help.
Choose which website results to save
The result mode controls which scanned websites appear in the Dataset. The maximum results setting caps saved website records. A website record can contain several email addresses, so the saved-row count and email count are different measurements.
| Mode | Use it when |
|---|---|
emailsOnly | You want to save websites with email matches. |
contactsOnly | Other enabled contacts, such as phones or social profiles, are useful too. |
allWebsites | You want saved website-level outcomes, including unsuccessful or no-contact records. |
In allWebsites mode, saved no-contact and unsuccessful records can also incur the Actor’s result charge. Check the current Pricing tab. Keep the original input sheet whichever mode you choose, because a cap or spending limit can still leave some inputs without a saved row.
Step 3. Review email addresses with their source evidence
When the run finishes, open its Dataset. Read several rows before downloading the whole list. Check that the website is the one you intended and that its source page actually associates the address with the business.
| Field | Why retain it |
|---|---|
input, url, domain | Match the crawl result to your submitted website. |
emails | Read the addresses found for that input. |
emailDetails | Keep each email with its sourceUrl, available type and domainMatch. |
status, pagesFetched | Understand the scan outcome and depth. |
fetchedUrls, pageDiagnostics | Check which pages were actually reached. |
scanLimitations, errors | Explain incomplete or missing contacts. |
A company homepage can publish addresses for support, sales or a third party such as its web agency. Review the page context before accepting the address as your intended contact. Turning off personal data keeps mostly generic company inboxes; it does not replace that review.
A published email is evidence of where the address appeared. Check deliverability separately when needed, and keep the contact’s purpose and company scope with the record.
Step 4. Export a clean contacts CSV
- Choose CSV in the Dataset’s export action and save the original file.
- Download JSON too when you need the nested details in
emailDetails. - Build a contacts sheet with one row per website input and email.
- Keep the source URL and original business row ID with each match.
- Deduplicate repeated website-and-email pairs in the working sheet.
- Keep a website summary sheet with one outcome per crawl input.
If several businesses share one website, join the website summary back to each original row. Keep multiple addresses in a separate Contacts sheet or a combined field so they do not accidentally multiply your business count. The Maps enrichment guide shows this mapping for Place IDs and branch websites.
Understand no-contact, blocked and incomplete results
fetched_no_contacts means the Actor completed its bounded checks without finding contacts in the scanned pages. It does not prove the entire website has no email. A contact form may be the only published route, or an address may be on a page beyond your limit.
blocked, incomplete and failed need different follow-up. Read diagnostics before treating a blank result as a successful scan. A timeout, page challenge or size limit can leave part of a site unread.
Check JavaScript rendering and redirects
With renderJavaScript: true, the Actor begins with HTTP and can use a browser fallback when a page is blocked, incomplete or returns no contacts. Check extractionMethod, browserStatus and RUN_SUMMARY.renderJavaScript to confirm the actual path. Browser checks are also bounded and can miss slow or interactive content.
With same-domain protection enabled, a redirect to another domain can be excluded from extraction and listed in rejectedRedirectUrls. Verify the destination if the business has rebranded or moved. Submit the confirmed new URL separately and preserve the mapping to your original input.
Retry the inputs you can improve
If the contact page is missing from fetchedUrls, try its exact URL. If several relevant pages remain, raise maxPagesPerWebsite from 3 to 5 or 6 for those sites. The supported range is 1–25. Keep completed no-contact scans separate from failed and unprocessed inputs.
Process large website lists in batches
The Actor accepts a saved-record ceiling up to 1,000,000 and configurable website concurrency. Split large lists into manageable batches with their own inputs, run IDs and Dataset IDs. Increase concurrency only after checking that your sample sites are responding reliably.
Read RUN_SUMMARY for inputWebsites, processed, saved, filteredOut, failed, chargeLimitReached and exhaustionReason. Reconcile the saved records against your original list. Keep all processed pages when reading a large Dataset through an API or agent.
Ask your AI agent to run the crawl through Apify MCP
Connect an MCP-compatible agent to https://mcp.apify.com and authorize your Apify account using the official MCP setup guide. Supply your website file and use a task such as this one.
Use x_guru/website-email-phone-finder.
Input: [MY_WEBSITE_LIST_OR_FILE]
Approved run budget in USD: [AMOUNT]
Inspect the current schema. Start with
five distinct real website inputs.
Keep source row IDs and branch paths.
Use allWebsites, maxResults=5 and
maxPagesPerWebsite=3. Set
includePersonalData=false,
extractPhones=false, extractSocials=false,
sameDomainOnly=true and
renderJavaScript=true.
Apply the approved cap through
callOptions.maxTotalChargeUsd.
Wait for completion. Read the full Dataset
and RUN_SUMMARY. Save a website status
table and a contacts CSV. Keep every email
with its input website and sourceUrl.
Report incomplete scans, redirects,
counts, run ID and Dataset ID.
Wait before expanding to the full list.The agent can start the Actor with call-actor, check completion with get-actor-run and read successive result pages with get-dataset-items. Its output should retain the input-to-contact mapping and the evidence needed to interpret missing results.
For a phone-first list, follow the website phone number guide. To discover businesses before crawling their sites, start with the Google Maps CSV guide. For a repeatable application, use the Apify run and export guide.



