# Website Email & Contact Scraper (`maximedupre/website-emails-scraper`) Actor

Find public contact email addresses from the website URLs you submit. Get one unique, normalized email per dataset row with the source and seed URLs attached.

- **URL**: https://apify.com/maximedupre/website-emails-scraper.md
- **Developed by:** [Maxime Dupré](https://apify.com/maximedupre) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 1,244 total users, 57 monthly users, 97.9% runs succeeded, 28 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$5.80 / 1,000 email addresses

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### ✉️ Find public contact emails from website pages

For sales teams, lead researchers, and developers who already have public website URLs, Website Emails Scraper finds public contact email addresses. It returns one clean row for each unique, normalized email, with the source page and submitted seed URL. Rows can also include public phone numbers, social profile links, and MX records found for the email domain. This helps you review contact data without opening every page by hand.

- Collect public contact emails from submitted sites with **[Website Email Scraper](https://apify.com/maximedupre/website-emails-scraper/examples/website-email-scraper)**.
- Save each address once with **[Website Emails: One Row Per Unique Email](https://apify.com/maximedupre/website-emails-scraper/examples/website-emails-one-row-per-unique-email)**.
- Check a seed and its linked contact or about pages with **[Website Email Scraping, Shallow by Default](https://apify.com/maximedupre/website-emails-scraper/examples/website-emails-shallow-by-default)**.
- Skip repeat addresses and repeat charges with **[Website Email Scraping With Duplicate-Safe Billing](https://apify.com/maximedupre/website-emails-scraper/examples/website-emails-duplicate-safe-billing)**.
- Test a short seed list before a larger crawl with **[Website Emails: Small Seed List First](https://apify.com/maximedupre/website-emails-scraper/examples/website-emails-small-seed-list-first)**.

#### 📬 See clean email rows with their source pages

Each saved row has an email address, the page where it was first found, and the submitted seed URL. The email is accepted, normalized, and unique within the run. A row can also include public phone numbers, social profile links, and MX records found for the email domain. The Actor does not return nearby page text.

#### 🚀 Crawl a small set of pages first

**Quick start**

1. Open the [Input](https://console.apify.com/actors/3C5pf0e2XqFhVcSSj/input) tab and add public website URLs.
2. Leave the email limit empty to return all available results until the source is exhausted. Set a limit when you want a smaller first run.
3. Run the Actor and open the dataset from the Output tab.

**What happens in a run**

The Actor checks submitted public URLs and linked contact and about pages allowed by your page settings. A blocked or unreadable page does not show whether the site has an email, and it does not create a saved email row. The first eligible occurrence of each normalized email is saved. Later matches are ignored, so they do not create another row.

#### ⚙️ Input

Add one or more public website URLs. This example is copied from the public input of a successful current-beta default-input run.

**Input example**

```json
{
  "urls": [
    {
      "url": "https://www.w3.org/contact/"
    },
    {
      "url": "https://www.apache.org/foundation/contact"
    },
    {
      "url": "https://www.ietf.org/contact/"
    },
    {
      "url": "https://www.freebsd.org/administration/"
    },
    {
      "url": "https://www.openbsd.org/mail.html"
    }
  ],
  "maxNbEmailsToScrape": 10
}
```

**Input fields**

| Field | Type | What it does |
| --- | --- | --- |
| `urls` | array of objects | List one or more complete public website URLs to check. |
| `urls[].url` | string | One complete public website URL. The Actor checks it and linked contact and about pages. |
| `maxNbEmailsToScrape` | integer | Optional maximum number of accepted emails for the whole run. Leave it empty to return all available results until the source is exhausted. |
| `phoneCountryCode` | string | Country code with two uppercase ISO letters for local phone numbers. Numbers that start with + keep their own country code. |
| `includePaths` | array of strings | Limit linked contact and about pages to these path prefixes. Leave empty to allow all paths; submitted URLs are still checked. |
| `excludePaths` | array of strings | Skip linked contact and about pages with these path prefixes. Leave empty to skip none; submitted URLs are still checked. |
| `includeSubdomains` | boolean | Allow linked contact and about pages on subdomains. Leave off to stay on each submitted URL's host. |

**Input rules**

Set the email limit for the whole run to a positive integer. Leave it empty to return all available results until the source is exhausted. Path filters do not skip submitted URLs.

#### 🧾 Output

The [Output](https://apify.com/maximedupre/website-emails-scraper/output-schema) tab exposes `results`, a link to the default dataset. Each dataset row has the same public shape. Optional fields appear when the source page or MX check provides them.

**Output fields**

| Field | Type | What it does |
| --- | --- | --- |
| `results` | string | Link to the default dataset of saved email addresses. |

**Dataset fields**

| Field | Type | What it does |
| --- | --- | --- |
| `email` | string | One accepted, normalized email address. It appears at most once per run. |
| `url` | string | Page URL where the first accepted match was found. |
| `seedUrl` | string | Submitted website URL that produced the row. |
| `phoneNumbers` | array of strings | Public phone numbers found on the source page, when available. |
| `socialLinks` | array of strings | Public social profile links found on the source page, when available. |
| `domainReachable` | boolean | Whether MX records were found for the email domain. This field is omitted when the check is unavailable. |

**Output example**

This complete row came from the newest successful current-beta run. It shows a saved email with its source and seed URLs, social links, and MX status.

```json
{
  "url": "https://www.glassdoor.com/about/newsroom/",
  "seedUrl": "https://www.glassdoor.com/about/newsroom/",
  "email": "pr@glassdoor.com",
  "socialLinks": [
    "https://www.linkedin.com/company/glassdoor",
    "https://www.facebook.com/Glassdoor/",
    "https://twitter.com/Glassdoor",
    "https://www.youtube.com/Glassdoor",
    "https://www.instagram.com/glassdoor",
    "https://youtube.com/Glassdoor",
    "https://instagram.com/glassdoor",
    "https://tiktok.com/@glassdoor"
  ],
  "domainReachable": true
}
```

#### 💳 Pricing

This Actor uses pay-per-event pricing. You are charged one event for each accepted, unique email address saved to the default dataset. See the Apify pricing panel for current rates.

**Buyer-facing event**

**Email address**

One accepted, unique email address is saved to your dataset.

#### 🔌 Integrations

Review rows in the Apify Console, read the default dataset with the Apify API, or export the dataset. This short guide shows a connected Actor workflow:

https://www.youtube.com/watch?v=bNACk1_S_6w\&list=PLObrtcm1Kw6MUrlLNDbK9QRg8VDJg0gOW\&index=4

#### ❓ FAQ

##### Will the Actor return phone numbers or nearby page text?

It saves accepted, normalized email addresses. A row can also include public phone numbers and social profile links when they are returned with the source page. It does not include nearby page text.

##### What happens when the same email appears on more than one page?

The Actor saves the first eligible occurrence of a normalized address. Later matches are ignored, so the saved row keeps the first source page and seed URL. Later matches do not create another row or charged event.

##### How far does it crawl?

It checks submitted URLs and linked contact and about pages allowed by your path settings. It does not promise to crawl every page on a site.

##### Can I limit the number of emails?

Yes. Set the email limit for the whole run to a positive integer. Leave it empty to return all available results until the source is exhausted.

##### What if a submitted URL has no email or cannot be read?

Only accepted emails from public pages are saved. A blocked or unreadable page does not show whether the site has an email, and a missing row by itself does not explain why no email was saved.

##### Does it access private pages or verify mailboxes?

No. It reads public pages only. MX records show that the email domain can route mail; they do not show that a specific mailbox is active or accepts messages.

##### How does this differ from an email finder?

This Actor starts with website URLs you submit and finds emails published on those public pages. It does not find websites by keyword.

##### What do `url` and `seedUrl` mean?

`url` is the page where the first accepted email match was found. `seedUrl` is the website URL you submitted that led to the row.

***

### 📝 Actor Release Notes

**v4.0** (08-10-2026)

- Choose a country code to read local phone numbers.
- Filter linked contact and about pages by path and, if needed, include subdomains.
- Find emails in structured page data and some click-to-reveal links.
- Pages blocked by `robots.txt` are skipped.
- `domainReachable` now reports whether MX records were found. The `emailRole` output is removed.
- Runs need at least 2048 MB of memory.

**v3.0** (07-10-2026)

- Runs now need at least 512 MB of memory.
- You can enter a website domain without adding `https://`.

**v2.1** (27-09-2026)

- Email rows can include public phone numbers, social profile links, domain reachability, and email role context when available.
- Each website starts with datacenter access and can move to residential access when a clear source access block remains after retries.

**v2.0** (10-09-2026)

- Proxy settings are no longer part of the input. The Actor uses its managed connection for public pages.
- Fixed template addresses such as `support@yourdomain.com` and `hello@yourdomain.com` are not saved or charged.

**v1.0** (05-09-2026)

- You can limit emails for each website. Other valid sites keep running if one site fails.
- Each accepted email includes the page where it was found and the website URL you submitted.

**v0.0**

- Initial release.

***

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~website-emails-scraper/issues) and I'll fix or implement it in less than 24h 🫡

***

### 🔗 Related Actors

- [Website URL Crawler & Link Extractor](https://apify.com/maximedupre/website-url-crawler) to find public page URLs that can become seeds for email crawls.
- [Product Hunt Scraper](https://apify.com/maximedupre/product-hunt-scraper) to collect product websites and launch pages before checking them for public contact emails.
- [Tiny Startups Scraper](https://apify.com/maximedupre/tiny-startups-scraper) to collect startup websites and optional public emails before a website email check.
- [Uneed Scraper](https://apify.com/maximedupre/uneed-scraper) to collect product websites and public contact data before a website email check.
- [Email MX Verifier](https://apify.com/maximedupre/email-mx-verifier) to check syntax and MX records after you collect email addresses.

***

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

## `urls` (type: `array`):

Add one or more complete public website URLs. The Actor also checks linked contact and about pages for each website.

## `maxNbEmailsToScrape` (type: `integer`):

Optional maximum number of accepted emails for the whole run. Leave it empty to return all available results until the source is exhausted.

## `phoneCountryCode` (type: `string`):

Choose a two-letter country code for local phone numbers, such as GB. Use any uppercase ISO country code. Phone numbers that start with + keep their own country code.

## `includePaths` (type: `array`):

Limit linked contact or about pages to path prefixes you list, such as /contact. Leave this empty to allow all paths. Submitted URLs are still checked.

## `excludePaths` (type: `array`):

Skip linked contact or about pages with a path prefix you list, such as /privacy. Leave this empty to skip none. Submitted URLs are still checked.

## `includeSubdomains` (type: `boolean`):

Allow linked contact or about pages on subdomains of each submitted website. Leave this off to stay on each submitted URL's host.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://www.w3.org/contact/"
    },
    {
      "url": "https://www.apache.org/foundation/contact"
    },
    {
      "url": "https://www.ietf.org/contact/"
    },
    {
      "url": "https://www.freebsd.org/administration/"
    },
    {
      "url": "https://www.openbsd.org/mail.html"
    }
  ],
  "maxNbEmailsToScrape": 10
}
```

# Actor output Schema

## `results` (type: `string`):

Accepted, normalized email addresses with source and seed URLs.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        {
            "url": "https://www.w3.org/contact/"
        },
        {
            "url": "https://www.apache.org/foundation/contact"
        },
        {
            "url": "https://www.ietf.org/contact/"
        },
        {
            "url": "https://www.freebsd.org/administration/"
        },
        {
            "url": "https://www.openbsd.org/mail.html"
        }
    ],
    "maxNbEmailsToScrape": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/website-emails-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": [
        { "url": "https://www.w3.org/contact/" },
        { "url": "https://www.apache.org/foundation/contact" },
        { "url": "https://www.ietf.org/contact/" },
        { "url": "https://www.freebsd.org/administration/" },
        { "url": "https://www.openbsd.org/mail.html" },
    ],
    "maxNbEmailsToScrape": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("maximedupre/website-emails-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    {
      "url": "https://www.w3.org/contact/"
    },
    {
      "url": "https://www.apache.org/foundation/contact"
    },
    {
      "url": "https://www.ietf.org/contact/"
    },
    {
      "url": "https://www.freebsd.org/administration/"
    },
    {
      "url": "https://www.openbsd.org/mail.html"
    }
  ],
  "maxNbEmailsToScrape": 10
}' |
apify call maximedupre/website-emails-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maximedupre/website-emails-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/3C5pf0e2XqFhVcSSj/builds/QkY4yGiWIO8pQVnh8/openapi.json
