# Website Email Scraper - All Contacts (`thenetaji/website-email-scraper`) Actor

The Actor crawls a website and returns one evidence-rich record per URL supplied: emails, phone numbers, social profiles, and addresses, each with a confidence score, source page, and detection method. Emails are format-checked and flagged when their domain matches the site's own.

- **URL**: https://apify.com/thenetaji/website-email-scraper.md
- **Developed by:** [The Netaji](https://apify.com/thenetaji) (community)
- **Categories:** Lead generation, Developer tools, Automation
- **Stats:** 1,660 total users, 130 monthly users, 98.3% runs succeeded, 19 bookmarks
- **User rating**: 4.41 out of 5 stars

## Pricing

from $2.00 / 1,000 results

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

## Website Email & Contact Extractor v2.1

### 🔍 Overview

**Website Email & Contact Extractor** is an Apify actor that crawls websites and extracts contact information in a clean, consistent format. It finds emails, phone numbers, social media profiles, and physical addresses — perfect for lead generation, sales outreach, local SEO, and market research.

### ✨ Key Features

- **Contact-First Output**: Emails, phones, social profiles, and addresses in one consistent schema
- **Social Platform Detection**: Automatically identifies LinkedIn, Instagram, Twitter/X, TikTok, YouTube, Facebook, GitHub, Telegram, WhatsApp, Pinterest, Snapchat, and Reddit
- **Cloudflare Protection Bypass**: Decodes Cloudflare-obfuscated email addresses
- **Text + DOM Extraction**: Finds contacts in visible text, `mailto:` / `tel:` links, structured markup, and social links
- **Adaptive Stealth Browser**: Auto-escalates to a headless browser when pages block normal requests
- **Domain Filtering**: Stay on the same domain or crawl freely
- **Consistent Schema**: Every result has the same 9 fields, with `null` for absent values

### 🎯 Use Cases

- **Lead Generation**: Build lists of sales prospects from company websites
- **Sales Outreach**: Extract decision-maker emails and LinkedIn profiles
- **Local SEO**: Collect NAP (Name, Address, Phone) data
- **Market Research**: Map social presence across competitor sites
- **Recruiting**: Find contact details and social profiles of teams

### 🛠️ Input Parameters

```json
{
  "startUrls": [{ "url": "https://example.com/" }],
  "mediaType": "all",
  "maxCrawlDepth": 2,
  "maxConcurrency": 10,
  "maxRequestRetries": 3,
  "maxUrlsToCrawl": 100,
  "useProxy": {
    "useApifyProxy": false,
    "apifyProxyGroups": [],
    "apifyProxyCountry": ""
  }
}
```

#### Parameter Details

| Parameter           | Type    | Description                                                                   |
| ------------------- | ------- | ----------------------------------------------------------------------------- |
| `startUrls`         | Array   | List of URLs where the crawler will begin                                     |
| `mediaType`         | String  | Contact type: `all`, `contact`, `email`, `phone`, `social`, or `address`      |
| `maxCrawlDepth`     | Number  | How many links deep the crawler will go                                       |
| `maxConcurrency`    | Number  | Maximum parallel requests                                                     |
| `maxRequestRetries` | Number  | Number of retry attempts for failed requests                                  |
| `maxUrlsToCrawl`    | Number  | Maximum number of pages to process                                            |
| `useProxy`          | Object  | Configuration for Apify proxy usage                                           |
| `useStealth`        | Boolean | Auto-escalate to stealth browser when blocked; auto-enables proxy if none set |
| `solveCloudflare`   | Boolean | Automatically solve Cloudflare challenges                                     |
| `includeContactText`| Boolean | Scan visible page text for contacts not wrapped in links                      |
| `groupByPage`       | Boolean | Combine all contacts from one page into a single dataset item (default: true) |

### 📊 Output Format

By default (`groupByPage: true`) the actor outputs **one item per crawled page**, combining all contacts found on that page. Set `groupByPage: false` to emit one flat item per contact instead.

#### Grouped output (default)

```json
{
  "sourceUrl": "https://apify.com/",
  "pageTitle": "Apify: Full-stack web scraping and data extraction platform",
  "emails": ["hello@apify.com"],
  "phones": ["+1-234-567-8900"],
  "socials": {
    "github": [
      { "url": "https://github.com/apify", "handle": "apify" }
    ],
    "twitter": [
      { "url": "https://twitter.com/apify", "handle": "apify" }
    ]
  },
  "addresses": ["123 Main St, Los Angeles, CA"],
  "foundAt": "2026-06-15T15:40:51.184Z"
}
```

#### Flat output (`groupByPage: false`)

Every item uses the same 9-field schema:

```json
{
  "type": "contact",
  "contactType": "email",
  "value": "info@example.com",
  "url": null,
  "socialPlatform": null,
  "socialHandle": null,
  "sourceUrl": "https://example.com/contact",
  "pageTitle": "Contact Us",
  "foundBy": "mailto",
  "foundAt": "2026-06-15T04:01:58.105Z"
}
```

#### Output fields (flat mode)

| Field            | Description                                              |
| ---------------- | -------------------------------------------------------- |
| `type`           | Always `"contact"`                                       |
| `contactType`    | `email`, `phone`, `social`, or `address`                 |
| `value`          | The extracted contact value                              |
| `url`            | Web URL for social profiles; `null` for other types      |
| `socialPlatform` | Platform name for social items; `null` otherwise         |
| `socialHandle`   | Username/handle for social items; `null` otherwise       |
| `sourceUrl`      | Page where the contact was found                         |
| `pageTitle`      | Title of the source page                                 |
| `foundBy`        | Detection method: `dom`, `mailto`, `tel`, `text-scan`, `cfemail` |
| `foundAt`        | ISO-8601 timestamp                                       |

#### Examples

```json
{
  "type": "contact",
  "contactType": "email",
  "value": "info@eversquaremedical.ca",
  "url": null,
  "socialPlatform": null,
  "socialHandle": null,
  "sourceUrl": "https://www.eversquaremedical.ca/",
  "pageTitle": "Ever Square Medical",
  "foundBy": "mailto",
  "foundAt": "2026-06-15T04:01:58.105Z"
}
```

**Phone from a `tel:` link**

```json
{
  "type": "contact",
  "contactType": "phone",
  "value": "310-929-6336",
  "url": null,
  "socialPlatform": null,
  "socialHandle": null,
  "sourceUrl": "https://www.conciergehealthcarepartnersinc.com/",
  "pageTitle": "Concierge Healthcare Partners",
  "foundBy": "tel",
  "foundAt": "2026-06-15T04:01:58.084Z"
}
```

**Social profile**

```json
{
  "type": "contact",
  "contactType": "social",
  "value": "https://www.instagram.com/example",
  "url": "https://www.instagram.com/example",
  "socialPlatform": "instagram",
  "socialHandle": "example",
  "sourceUrl": "https://example.com/about",
  "pageTitle": "About Us",
  "foundBy": "dom",
  "foundAt": "2026-06-15T04:01:58.200Z"
}
```

### 💡 Best Practices

- **Start Small**: Begin with a low `maxUrlsToCrawl` value to test results
- **Use Stealth for Protected Sites**: Enable `useStealth` and `solveCloudflare` for Cloudflare-protected sites. Stealth auto-enables an Apify datacenter proxy if you do not provide one. The actor rotates datacenter IPs on blocks and only escalates to expensive residential proxies after repeated consecutive blocks on the same domain.
- **Optimize Depth**: Most contact info is found within 1–2 levels of crawl depth
- **Target Specific Contact Types**: Use `mediaType` to focus on emails, phones, or socials
- **Respect Websites**: Use reasonable `maxConcurrency` values to avoid overloading sites

### 📚 Examples

#### Extract emails only

```json
{
  "startUrls": [{ "url": "https://company.com/" }],
  "mediaType": "email",
  "maxCrawlDepth": 2,
  "maxUrlsToCrawl": 50
}
```

#### Extract all contact types

```json
{
  "startUrls": [{ "url": "https://company.com/" }],
  "mediaType": "all",
  "maxCrawlDepth": 2,
  "maxUrlsToCrawl": 100,
  "includeContactText": true
}
```

#### Collect social media profiles

```json
{
  "startUrls": [{ "url": "https://company.com/" }],
  "mediaType": "social",
  "maxCrawlDepth": 1,
  "maxUrlsToCrawl": 50
}
```

### ⚙️ Technical Implementation

The actor uses multiple extraction strategies:

1. **DOM Selectors**: `mailto:`, `tel:`, social links, and structured markup
2. **Text Scanning**: Regex over visible page text
3. **Cloudflare Decode**: Reverses `data-cfemail` obfuscation
4. **Adaptive Escalation**: Rotates datacenter IPs on blocks; only falls back to residential stealth when a domain repeatedly fails with datacenter proxies

### 📈 Performance Considerations

- Processing speed depends on website complexity and response times
- Typical extraction rates: 5–10 pages per second without proxy, 2–5 pages per second with proxy
- Memory usage scales with concurrency and page complexity
- The actor uses datacenter proxies by default and escalates to residential proxies only when necessary, keeping costs low for most contact-extraction tasks

### 🔗 Integration Ideas

- Connect with **Apify Storage** for permanent dataset archiving
- Combine with **Google Sheets** integration for easy team collaboration
- Use with **Zapier** or **Make** to automate outreach workflows

# Actor input Schema

## `startUrls` (type: `array`):

One or more URLs to start crawling from.

## `mediaType` (type: `string`):

Which type of contact information to extract from crawled pages.

## `maxCrawlDepth` (type: `integer`):

How many link-hops deep to follow from the start URLs. 0 = only the start page.

## `maxUrlsToCrawl` (type: `integer`):

Stop after visiting this many pages (not contact items — pages).

## `maxConcurrency` (type: `integer`):

Number of pages to fetch in parallel.

## `maxRequestRetries` (type: `integer`):

How many times to retry a failed HTTP request before giving up.

## `maxBlockedRetries` (type: `integer`):

How many times to retry a blocked request. The crawler rotates datacenter IPs first and only escalates to residential stealth after repeated consecutive blocks on the same domain.

## `stayOnDomain` (type: `boolean`):

Only follow links that point to the same domain as the start URLs.

## `useStealth` (type: `boolean`):

When a page blocks the fast HTTP session the crawler automatically retries using a full stealth browser with anti-bot patches. Stealth mode auto-enables an Apify datacenter proxy if you have not configured one, because stealth without a proxy is often blocked.

## `solveCloudflare` (type: `boolean`):

Automatically solve Cloudflare Turnstile and Interstitial challenges. Requires useStealth=true.

## `includeContactText` (type: `boolean`):

Scan the visible page text for emails, phone numbers, and addresses not wrapped in links.

## `groupByPage` (type: `boolean`):

When enabled, all contacts found on a single page are combined into one dataset item. When disabled, each contact is emitted as a separate flat item.

## `useProxy` (type: `object`):

Use Apify Proxy to avoid IP bans. By default the actor uses datacenter IPs and rotates them on blocks. Residential proxies are used only when a domain repeatedly fails with rotated datacenter IPs, or when you explicitly select a residential proxy group.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com/"
    }
  ],
  "mediaType": "all",
  "maxCrawlDepth": 2,
  "maxUrlsToCrawl": 100,
  "maxConcurrency": 10,
  "maxRequestRetries": 3,
  "maxBlockedRetries": 5,
  "stayOnDomain": true,
  "useStealth": true,
  "solveCloudflare": false,
  "includeContactText": true,
  "groupByPage": true,
  "useProxy": {
    "useApifyProxy": false,
    "apifyProxyGroups": []
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing all extracted contacts

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

// Run the Actor and wait for it to finish
const run = await client.actor("thenetaji/website-email-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 = { "startUrls": [{ "url": "https://example.com/" }] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thenetaji/website-email-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/UwSrlU3InJgbBlD2s/builds/F9BBgvh058rgZVVg3/openapi.json
