# Email Scraper (`thodor/apify-email-scraper-tool`) Actor

Email scraper & extractor tool to pull emails from any website or domain in bulk. Hunter.io alternative.

- **URL**: https://apify.com/thodor/apify-email-scraper-tool.md
- **Developed by:** [Thodor](https://apify.com/thodor) (community)
- **Categories:** Lead generation, Other
- **Stats:** 34 total users, 4 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.20 / 1,000 page checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

A no-code **email extractor** tool that pulls business email addresses from any website. Paste a list of domains, get back one clean row per unique email, deduplicated, validated, and traced to the exact pages it came from, as CSV, Excel, or JSON. A pay-per-page **Hunter.io alternative** with no subscription and no seat fees.

For every domain it visits the pages most likely to hold contact info (contact, about, team, legal, in that order), decodes the tricks sites use to hide addresses (Cloudflare email protection, HTML entities, "name \[at] domain \[dot] com"), filters out junk like `noreply@` and placeholder names, and checks that each address's domain can actually receive mail.

### 📋 How to extract emails from a list of websites

1. Paste your domains or pages into **Start URLs**. Bare domains (`apify.com`) and specific pages (`apify.com/contact`) both work; pages you supply are visited first.
2. Click **Start**.
3. Open the **Output** tab and click **Export** for CSV, Excel, JSON, or HTML.

### 🎁 So what do you get?

| | | |
| --- | --- | --- |
| **📧 Email:** *one row per unique address* | **✅ Domain validation:** *can it receive mail?* | **🔢 Occurrences:** *seen on how many pages* |
| **🔗 Source URLs:** *every page it appeared on* | **🛡️ Hidden emails decoded:** *Cloudflare, \[at]/\[dot], entities* | **🚫 Junk filtered:** *noreply@, placeholders, filenames* |

### ⚖️ Compared to Hunter.io, Snov.io, and Outscraper

| | This actor | Hunter.io | Snov.io | Outscraper |
|---|---|---|---|---|
| 🆓 Free tier | ✅ ~1,650 pages per month, Apify's $5 free credit | ⚠️ 25 searches/mo | ⚠️ 50 credits | ⚠️ Limited |
| 💰 Billing | ✅ Pay per page checked | ❌ Credit subscription | ❌ Subscription | ⚠️ Per result |
| 🔗 Which page each email came from | ✅ Every URL | ❌ | ❌ | ⚠️ Domain-level |
| 🔢 Occurrence counting | ✅ | ❌ | ❌ | ❌ |
| 📥 Bring your own URL list | ✅ Unlimited, parallel | ⚠️ Limited | ⚠️ Limited | ✅ |
| 🎯 Focus | ✅ Emails only, clean rows | ⚠️ Bundled finder suite | ⚠️ Bundled cold-mail sender | ⚠️ Emails + phones + socials |

Mailbox-level probing is deliberately out of scope: most providers block it as abuse, and the domain-level check gives the same practical signal. See the FAQ.

### 🎯 Three things people run this for

| | How |
|---|---|
| 🧱 **Turn any scrape into contacts** | The `website` column from the [Google Maps Scraper](https://apify.com/thodor/google-maps-scraper-with-reviews) or the landing pages from the [Meta Ad Library Scraper](https://apify.com/thodor/meta-ad-library) paste straight into Start URLs |
| 📇 **Enrich a CRM or prospect list** | Run your domain column, keep rows where `valid_email_domain` is `true`, and rank by `occurrences`: an address on five pages is a real, publicised contact |
| 🤖 **Automated enrichment** | A Clay column, an n8n node, or a nightly [Schedule](https://docs.apify.com/platform/schedules) against a growing list. AI agents can run it through the [Apify MCP server](https://mcp.apify.com/) |

### 📥 Input

```json
{
  "start_urls": [
    { "url": "apify.com" },
    { "url": "example.com/contact" }
  ],
  "max_pages_domain": 20
}
```

- `start_urls`: domains or pages, hundreds at a time. URLs on the same domain are grouped and visited first, then the actor explores the rest of the site, subdomains included
- `max_pages_domain`: default `20`, the cap on pages fetched per domain. It is also the cost cap

#### 🎯 Known contact pages only, cheapest

```json
{"start_urls": [{"url": "example.com/contact"}, {"url": "example.com/team"}], "max_pages_domain": 5}
```

#### 🕳️ Deep scan of a large site

Team rosters, regional offices, multi-language sections: contact info buried several clicks deep.

```json
{"start_urls": [{"url": "bigcorp.com"}], "max_pages_domain": 100}
```

### 📤 Output

One row per unique email. Results stream into the dataset while the run is going.

![Email scraper output example: dataset table with one row per unique email, domain mail-server validation, occurrence count, and source URLs](https://api.apify.com/v2/key-value-stores/LHcvkclm26dcJvwP1/records/email-scraper-example-output.png)

```json
{
    "email": "hello@apify.com",
    "domain": "apify.com",
    "valid_email_domain": true,
    "occurrences": 6,
    "urls": [
        "https://apify.com/contact",
        "https://docs.apify.com/legal"
    ]
}
```

> ⚠️ **`valid_email_domain: true` means the domain can receive mail, not that the mailbox exists.** The check confirms the address is syntactically correct and its domain runs a mail server. Whether `jane@` specifically exists can only be proven by probing the mailbox, which providers block as abuse. For mailbox-level certainty, run the output through a verification service like NeverBounce or ZeroBounce.

#### Fields

| Field | Meaning |
|---|---|
| `email` | The address, lowercased. Junk is already filtered out: `noreply@`, `webmaster@`, placeholders like `john.doe@`, and false matches from image filenames never reach the dataset |
| `domain` | The part after the `@` |
| `valid_email_domain` | `true` when the domain is set up to receive mail |
| `occurrences` | How many times the address appeared across the crawled pages. Higher = more likely a real, publicised contact |
| `urls` | Every page where the email was found |

### ⚙️ Use it as an email extraction API

Every run is an HTTP endpoint: POST the same JSON as the form and the rows come back in the response body.

#### Python

```python
import requests

resp = requests.post(
    "https://api.apify.com/v2/acts/thodor~apify-email-scraper-tool/run-sync-get-dataset-items",
    params={"token": "YOUR_APIFY_TOKEN"},
    json={"start_urls": [{"url": "apify.com"}]},
)

for row in resp.json():
    if row["valid_email_domain"]:
        print(row["email"], row["occurrences"])
```

#### Node.js

```javascript
import axios from "axios";

const { data } = await axios.post(
  "https://api.apify.com/v2/acts/thodor~apify-email-scraper-tool/run-sync-get-dataset-items",
  { start_urls: [{ url: "apify.com" }] },
  { params: { token: process.env.APIFY_TOKEN } }
);

console.log(data.map((r) => r.email));
```

#### curl

```bash
curl -X POST "https://api.apify.com/v2/acts/thodor~apify-email-scraper-tool/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"start_urls":[{"url":"apify.com"}]}'
```

#### Clay

Add an HTTP API column: method `POST`, the URL above, body `{ "start_urls": [{ "url": "{{Domain}}" }] }`. Map `email`, `valid_email_domain`, `occurrences`, and `urls` into columns, and use Clay's "Multiple Rows from Array" expander when a domain returns several addresses. n8n and Make use the official Apify modules with the same input; AI agents call it through the [Apify MCP server](https://mcp.apify.com/).

> 💡 **Tip:** no need to write the JSON by hand. Fill in the form on the Input tab, switch the editor from **Form** to **JSON**, and copy the result into your code.

### 💰 How much does it cost to scrape emails?

Billing is per page successfully loaded (HTTP 200 with HTML), at the rate on the price card on this page. Failed fetches, 404s, blocked pages, and non-HTML responses are free. At the default cap of 20 pages per domain, 100 domains cost at most 2,000 page events, and usually fewer, since many sites have fewer than 20 relevant pages. `max_pages_domain` is the cost lever: `5` checks only the obvious contact pages, `100` does a deep enterprise crawl.

### ❓ FAQ

**Can I use this email scraper for free?**
Yes. Registering on Apify comes with $5 of free platform credit every month, no credit card needed, which covers around 1,650 pages: roughly 80 domains at the default settings, more when sites are small.

**Why use this instead of an email scraper Chrome extension?**
An extension grabs the page you are looking at, one tab at a time. This takes hundreds of domains in one run, works unattended on schedules, and returns deduplicated, validated rows instead of text copied from a popup.

**Why are some obvious emails missed?**
Some sites only reveal an address after a click ("Show email") or a delay. The actor reads pages directly, so anything that needs user interaction stays invisible. Open a ticket with the URL if you hit one that matters.

**Does it find emails on social profiles?**
Often, yes. Bio emails on creator pages are usually rendered in the page's server-side JSON or meta tags, which it reads the same way as a contact page.

**Is scraping public emails legal?**
Scraping publicly published contact information is generally permitted, but you are responsible for each site's terms and for anti-spam and privacy law (GDPR, CAN-SPAM). Do not use the output for spam; Apify's terms forbid it.

### 🛟 Support

An email it should have found but didn't, or an obfuscation scheme it doesn't know yet? Message me in the Issues tab with the URL and I'll look into it quickly. I'm a solo dev, so don't hesitate.

Want to know what those domains run before you reach out? The [Tech Stack Detector](https://apify.com/thodor/tech-stack-detector) classifies the same list by CMS and framework.

- Thodor

# Actor input Schema

## `start_urls` (type: `array`):

Websites to scrape for email addresses. Each entry can be either a bare domain (example.com, www.example.com) or a specific page (https://example.com/contact). Mix and match freely. The Actor groups multiple entries on the same domain together and visits your URLs first before exploring the rest of the site.

## `max_pages_domain` (type: `integer`):

Maximum number of pages to fetch per domain. Counts both your supplied URLs and pages the Actor discovers from them.

## Actor input object example

```json
{
  "start_urls": [
    {
      "url": "https://apify.com/"
    }
  ],
  "max_pages_domain": 20
}
```

# Actor output Schema

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

No description

# 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 = {
    "start_urls": [
        {
            "url": "https://apify.com/"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("thodor/apify-email-scraper-tool").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 = { "start_urls": [{ "url": "https://apify.com/" }] }

# Run the Actor and wait for it to finish
run = client.actor("thodor/apify-email-scraper-tool").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 '{
  "start_urls": [
    {
      "url": "https://apify.com/"
    }
  ]
}' |
apify call thodor/apify-email-scraper-tool --silent --output-dataset

```

## MCP server setup

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

```

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/wZdw46ZaKFFx519zW/builds/UYadJ3xo2LKc25hXM/openapi.json
