# Website Email & Contact Extractor (`darknezz/website-contact-extractor`) Actor

Extract emails, phone numbers and social profile links from any list of websites. Crawls contact, about and team pages, decodes obfuscated and Cloudflare-protected emails. Perfect for B2B lead generation and CRM enrichment.

- **URL**: https://apify.com/darknezz/website-contact-extractor.md
- **Developed by:** [Oaida Adrian](https://apify.com/darknezz) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 website processeds

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?

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

Extract **emails, phone numbers and social profile links** from any list of websites — built for B2B lead generation, sales prospecting and CRM enrichment. Handles redirects, prioritises contact pages, decodes obfuscated and Cloudflare-protected emails, and returns one clean JSON row per website.

### Why this Actor

A spreadsheet full of company domains is worth little until you know who to call and where to write. This Actor turns a raw domain list into a ready-to-import contact sheet: verified inboxes, phone numbers, and the LinkedIn/Facebook/Twitter profiles of the companies you want to reach. No proxy configuration, no API keys, no login — give it URLs, get structured contacts back.

### What it does

For every website you provide, the Actor:

1. Crawls the homepage, then **prioritises contact, about, team, support, impressum and legal pages** (up to `maxPagesPerSite` pages, same domain only).
2. Extracts **email addresses** from plain text, `mailto:` links, common obfuscations (`name [at] domain [dot] com`) and **Cloudflare-protected emails** (`data-cfemail` decoding).
3. Extracts **phone numbers** from `tel:` links plus international (`+40 …`) and US (`(555) 123-4567`) text formats.
4. Extracts **social profile links** — Facebook, Twitter/X, Instagram, LinkedIn, YouTube, TikTok, GitHub — with per-platform columns for easy spreadsheet export.
5. Outputs **one structured JSON item per website**, with junk emails (image filenames, placeholders, no-reply) filtered out.

### Input

```json
{
  "websites": ["https://example.com/", "another-site.com"],
  "maxPagesPerSite": 10,
  "includeSocialLinks": true
}
```

| Option | Description |
|--------|-------------|
| `websites` | List of website URLs or bare domains (required). |
| `maxPagesPerSite` | Max pages to crawl per site (default 10, max 50). Contact/about/team pages are visited first. |
| `includeSocialLinks` | Whether to extract social profile links (default true). |

### Output (one item per website)

```json
{
  "url": "https://example.com/",
  "domain": "example.com",
  "pagesCrawled": 10,
  "emails": ["hello@example.com", "sales@example.com"],
  "phones": ["+1 (555) 123-4567"],
  "contactPages": ["https://example.com/contact"],
  "linkedin": "https://linkedin.com/company/example",
  "facebook": "https://facebook.com/example",
  "socialLinks": ["https://facebook.com/example", "https://linkedin.com/company/example"]
}
```

Export to JSON, CSV or Excel in the Apify console, or pull the dataset with the API. One row per website — the per-platform social columns (`linkedin`, `facebook`, `twitter`, `instagram`, `youtube`, `tiktok`, `github`) drop straight into a CRM import template.

### Use cases in detail

- **B2B lead generation** — turn a list of company domains (from a directory, a competitor's customer page, or a Google Sheets export) into a ready-to-import contact list.
- **CRM enrichment** — fill missing email/phone/social columns for accounts you already track; run it nightly on new sign-ups.
- **Outreach prep** — find the correct contact/impressum page and the right inbox before a cold campaign, instead of guessing `info@`.
- **Directory & dataset building** — enrich business listings, marketplace vendors or conference exhibitor lists with verified contact channels.
- **Recruiting & partnerships** — pull team-page and social links to reach the right person directly.

### Run on a schedule or via API

Point an Apify **Schedule** at a fresh batch of domains each day, or trigger the Actor from your own backend and read the results straight into your CRM:

```bash
curl -X POST "https://api.apify.com/v2/acts/darknezz~website-contact-extractor/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"websites":["https://astral.sh/","https://www.python.org/"],"maxPagesPerSite":10}'
```

From Python:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("darknezz~website-contact-extractor").call(
    run_input={"websites": ["https://astral.sh/", "https://www.python.org/"], "maxPagesPerSite": 10},
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["domain"], item["emails"])
```

Each run's dataset exports cleanly to JSON, CSV or Excel — one row per website with per-platform social columns for spreadsheet use.

### Worked example (real output)

Running on `https://www.python.org` and `https://astral.sh` (default 10 pages/site) returns:

```json
[
  {
    "url": "https://www.python.org/",
    "domain": "python.org",
    "pagesCrawled": 10,
    "emails": ["jobs@python.org", "legal@python.org", "psf@python.org", "python-announce@python.org", "webmaster@python.org"],
    "phones": null,
    "contactPages": ["https://www.python.org/about/help/", "https://www.python.org/about/legal/", "https://www.python.org/jobs/"],
    "twitter": "https://twitter.com/ThePSF",
    "linkedin": "https://www.linkedin.com/company/python-software-foundation",
    "github": "https://github.com/python"
  },
  {
    "url": "https://astral.sh/",
    "domain": "astral.sh",
    "pagesCrawled": 10,
    "emails": ["hey@astral.sh"],
    "phones": null,
    "contactPages": ["https://astral.sh/legal/privacy"],
    "twitter": "https://twitter.com/astral_sh",
    "github": "https://github.com/astral-sh"
  }
]
```

The first run found five public Python.org inboxes plus its social profiles; the second surfaced Astral's single contact address and GitHub — enough to enrich a CRM row or start an outreach thread.

### Pricing

Pay per event: you are charged once per **website processed**, regardless of how many pages are crawled or contacts found — so cost scales with your input list, not the site's size.

### FAQ

**Does it work on JavaScript-heavy sites?** Contacts rendered server-side (the vast majority) are found, including Cloudflare-obfuscated emails. Contacts injected purely client-side after render are not.

**How many pages per site does it crawl?** Up to `maxPagesPerSite` (default 10, max 50), and it visits contact/about/team/support/impressum/legal pages first so the useful data is found early.

**Does it need a proxy or API key?** No — it crawls the sites you provide directly, no proxy or credentials required.

**How do you avoid junk emails?** Image filenames, placeholder addresses (`example.com`, `yourdomain`) and common `no-reply` inboxes are filtered out before output.

**What if a site is down or times out?** The item is still returned with an `error` field describing what happened, so a partial run never silently drops rows.

**Can I crawl more than 50 pages per site?** Not currently — contact data virtually always lives on the first few pages, so the cap keeps runs fast and cheap. Raise a GitHub issue if you need a higher ceiling.

**Is scraping contact data legal?** The Actor only reads publicly displayed information. You are responsible for complying with GDPR/CAN-SPAM when using extracted data for outreach.

### Limitations

- **No JavaScript rendering** — contacts injected purely client-side after page load are not found. Server-rendered contacts (the vast majority, including Cloudflare-obfuscated emails) are.
- **Same-domain crawl only** — the Actor stays on the site's own domain; it will not follow links to external sites to chase a contact.
- **Best-effort dedup** — the same address can appear on several crawled pages; results are de-duplicated per website, but obfuscated and plain variants of the same address are treated as separate strings.
- **No phone validation** — phones come from `tel:` links and text patterns, so malformed or placeholder numbers may pass through; use the output as a starting point, not a verified directory.

# Actor input Schema

## `websites` (type: `array`):

List of website URLs to extract contacts from (e.g. https://example.com). Bare domains work too.

## `maxPagesPerSite` (type: `integer`):

How many pages to crawl per website. Contact, about and team pages are visited first.

## `includeSocialLinks` (type: `boolean`):

Also extract Facebook, Twitter/X, Instagram, LinkedIn, YouTube, TikTok and GitHub profile links.

## `timeoutSecs` (type: `integer`):

Per-page request timeout in seconds.

## Actor input object example

```json
{
  "websites": [
    "https://www.python.org/",
    "https://astral.sh/"
  ],
  "maxPagesPerSite": 10,
  "includeSocialLinks": true,
  "timeoutSecs": 30
}
```

# Actor output Schema

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

No description

## `url` (type: `string`):

No description

## `finalUrl` (type: `string`):

No description

## `domain` (type: `string`):

No description

## `pagesCrawled` (type: `string`):

No description

## `emails` (type: `string`):

No description

## `phones` (type: `string`):

No description

## `facebook` (type: `string`):

No description

## `twitter` (type: `string`):

No description

## `instagram` (type: `string`):

No description

## `linkedin` (type: `string`):

No description

## `youtube` (type: `string`):

No description

## `github` (type: `string`):

No description

## `error` (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 = {
    "websites": [
        "https://www.python.org/",
        "https://astral.sh/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("darknezz/website-contact-extractor").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 = { "websites": [
        "https://www.python.org/",
        "https://astral.sh/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("darknezz/website-contact-extractor").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 '{
  "websites": [
    "https://www.python.org/",
    "https://astral.sh/"
  ]
}' |
apify call darknezz/website-contact-extractor --silent --output-dataset

```

## MCP server setup

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

```

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/29s7J7qFPbL5dWe69/builds/r3ByPhTgDB8vIoTxN/openapi.json
