# Website Email & Contact Scraper (`avoca_code/website-email-contact-scraper`) Actor

Crawl any list of websites and get their emails, phone numbers and social profiles, sorted by inbox type (editorial, general, person, support) with the page each one was found on.

- **URL**: https://apify.com/avoca_code/website-email-contact-scraper.md
- **Developed by:** [Avocado Code](https://apify.com/avoca_code) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 website with emails founds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## Website Email & Contact Scraper

Give it a list of websites and get back every email address, phone number and social profile on them. Each email comes with the exact page it was found on and a label telling you what kind of inbox it is.

Built for people who need to reach the *right* inbox at a company, like link builders, PR teams, partnership managers and sales teams. A plain list of addresses isn't enough for that.

### What you get for each website

- **Emails**, sorted so the most useful inboxes come first:
  - `editorial_partnerships_ads`: editor@, press@, partnerships@, advertise@, marketing@, writeforus@ …
  - `general`: info@, contact@, admin@
  - `person`: named people such as john@, john.smith@, j.smith@
  - `support`: support@
  - `other`: everything else (hello@, sales@, jobs@ …), returned only in "All emails" mode
- **The page each email was found on**, so you can check it in one click
- **`sameDomain` flag**, which separates the site's own inboxes from agency, parent-company or partner addresses shown on the same page
- **Phone numbers**, validated with Google's libphonenumber and normalised to international format, so you don't get random digits from URLs or article IDs
- **Social profiles**: LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok, GitHub, Bluesky, Threads and more
- **Tech stack** (optional): CMS, analytics, CDN and other technologies detected on the homepage
- **Site title and meta description**

### Why it finds more than a homepage scan

- **It crawls the pages where inboxes live.** It doesn't guess fixed URLs. It follows the site's real links and visits contact, imprint, advertise, write-for-us, press, media-kit, partners, about and team pages first.
- **It decodes hidden emails.** It reads `mailto:` links, Cloudflare's email protection, and "name \[at] domain \[dot] com" style obfuscation.
- **It handles JavaScript sites and bot checks.** When a site renders with JavaScript or shows a challenge page, it switches to a real Chrome browser for that site only. Simple sites stay on fast HTTP requests.
- **It cleans up glued-on text.** Addresses like `info@example.comContact` are trimmed against the real domain.

### Input

| Field | What it does | Default |
|---|---|---|
| Websites / Domains | URLs or bare domains (`example.com`). Duplicates are removed. | n/a |
| Which emails to return | `All emails` or `Outreach inboxes only` (drops jobs@, team@, noreply-style and unclassified addresses) | All emails |
| Include emails on other domains | Keep addresses whose domain differs from the website | On |
| Phone numbers / Social profiles / Tech stack | Turn each extractor on or off | On / On / Off |
| Max pages per website | Pages crawled per site. Contact-type pages go first. | 20 |
| Time limit per website | Hard time budget per site | 120 s |
| Use a real browser when needed | Chrome fallback for JavaScript sites and bot checks | On |
| Websites in parallel | Concurrency | 5 |
| Proxy | Apify Proxy for sites behind Cloudflare or other bot checks. Use the RESIDENTIAL group for those. | Off |

Example input:

```json
{
  "domains": ["example.com", "https://another-site.io"],
  "emailFilter": "outreach",
  "maxPagesPerWebsite": 15
}
```

### Output

You get one dataset item per website. The **Overview** table shows the domain, the best email, the email count, all emails, phones and any error. Export to CSV, Excel or JSON from the dataset tab. `emailsList` and `phonesList` are plain text, so they stay readable in a spreadsheet.

```json
{
  "inputUrl": "https://acme-test.com",
  "domain": "acme-test.com",
  "title": "Acme",
  "emailCount": 3,
  "primaryEmail": "press@acme-test.com",
  "emailsList": "press@acme-test.com; info@acme-test.com; john.smith@acme-test.com",
  "emails": [
    {
      "email": "press@acme-test.com",
      "category": "editorial_partnerships_ads",
      "tier": 1,
      "matchedKeyword": "press",
      "sameDomain": true,
      "isLikelyOfficial": true,
      "sources": ["https://acme-test.com/contact"]
    }
  ],
  "phones": [{ "phone": "+14155550123", "isLikelyOfficial": true, "sources": ["https://acme-test.com/contact"] }],
  "phonesList": "+14155550123",
  "socials": { "linkedin": ["https://www.linkedin.com/company/acme-test"] },
  "error": null,
  "scrapedAt": "2026-10-08T10:18:34+00:00"
}
```

When a site can't be reached, its item is still returned with `error` set, for example `dns: no such host`. Your input list always lines up with the output.

### Tips

- **For speed on large lists**, turn off the browser fallback and set max pages to 10. Most small business sites list their inbox on the contact page or in the footer.
- **For publishers and media sites**, keep the defaults. Advertise, write-for-us and press pages are often several clicks deep.
- **Blocked sites:** collect the rows with a `blocked by bot protection` error and run just those again with Apify Proxy set to RESIDENTIAL.
- **Memory:** 2 GB is enough without the browser. Use 4 GB when the browser fallback is on and you crawl 5 or more sites in parallel.

### Limitations

- It reports what is published on the website. It doesn't guess addresses or check that a mailbox is live.
- Sites behind Cloudflare or a similar bot check return `error: "blocked by bot protection"` and are not charged. Run them again with a residential proxy; some sites block every automated visitor, even then.
- Contact forms without a visible email address can't be turned into one.

### Responsible use

Emails and phone numbers of named people are personal data under GDPR and similar laws. This tool only collects information that websites publish themselves. You are responsible for having a lawful basis to store and contact the people in your results, and for following each website's terms and the anti-spam rules where you operate (CAN-SPAM, PECR, etc.).

### Support

Found a site where it misses an email that is clearly on the page? Open an issue with the URL and it will be checked.

# Actor input Schema

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

Websites to scrape. A homepage URL or a bare domain both work (example.com). The crawl stays on each site's own domain.

## `domains` (type: `array`):

Optional: paste domains one per line instead of using the Websites field. Duplicates across both fields are removed.

## `emailFilter` (type: `string`):

'All emails' returns every address found. 'Outreach inboxes only' keeps editorial / partnership / advertising inboxes, info@ / contact@, named people and support@, and drops things like jobs@, noreply-style and team@ addresses.

## `includeOffDomainEmails` (type: `boolean`):

Keep emails whose domain differs from the website (e.g. a parent company or agency address shown on the contact page). Each email is flagged with sameDomain either way.

## `includePhones` (type: `boolean`):

Extract phone numbers, validated with Google's libphonenumber.

## `includeSocials` (type: `boolean`):

Extract LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok and other profile links.

## `includeTechnologies` (type: `boolean`):

Detect the homepage's technologies (CMS, analytics, CDN...).

## `maxPagesPerWebsite` (type: `integer`):

Contact, about, team, press, advertise and partner pages are crawled first, so 10-20 pages finds most inboxes.

## `maxSecondsPerWebsite` (type: `integer`):

Hard time budget for one website, so a slow site can't stall the run.

## `useBrowserFallback` (type: `boolean`):

Switch to Chrome for sites that render with JavaScript or show a bot check. Turn off for a faster, cheaper run on simple sites.

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

How many websites to crawl at the same time.

## `proxyConfiguration` (type: `object`):

Most sites work without a proxy. For sites behind Cloudflare or other bot checks (they come back with error 'blocked by bot protection'), use Apify Proxy with the RESIDENTIAL group.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://apify.com"
    }
  ],
  "emailFilter": "all",
  "includeOffDomainEmails": true,
  "includePhones": true,
  "includeSocials": true,
  "includeTechnologies": false,
  "maxPagesPerWebsite": 20,
  "maxSecondsPerWebsite": 120,
  "useBrowserFallback": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

All scraped websites. Opens on the Overview view: domain, best email, email count, all emails, phones and error.

# 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://apify.com"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("avoca_code/website-email-contact-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://apify.com" }],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("avoca_code/website-email-contact-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://apify.com"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call avoca_code/website-email-contact-scraper --silent --output-dataset

```

## MCP server setup

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