# Website Email Extractor & Contact Finder: Email, Phone, Social (`datadesk-io/website-contact-finder`) Actor

Enrich domains with emails (free MX/syntax validity check), phones, LinkedIn/X/Facebook/Instagram, company name, description, address and tech stack. One deduplicated row per company. $2 per 1,000 sites with contacts; empty sites free.

- **URL**: https://apify.com/datadesk-io/website-contact-finder.md
- **Developed by:** [DataDesk](https://apify.com/datadesk-io) (community)
- **Categories:** Lead generation, AI, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 website with contacts

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?

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 Extractor & Contact Finder: Emails, Phones, Social Links (+ Company Data)

**Enrich a list of domains with contact data.** Paste company domains and get **one clean, deduplicated row per company**: emails (each with a free validity check), phone numbers, LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok, GitHub and WhatsApp, plus **company name, description, address and tech hints** (CMS, e-commerce, CRM, analytics, chat). Good for B2B lead generation, CRM enrichment, sales prospecting and AI agents.

*Email finder and extractor from websites · extraer correos y teléfonos de sitios web · extrair e-mails e telefones de sites.*

### Quick start: paste domains, click Run (all fields optional)

```json
{
  "websites": ["stripe.com", "https://www.python.org", "basecamp.com"],
  "maxPagesPerWebsite": 6,
  "validateEmails": true
}
```

With an empty input it enriches 5 sample domains in a few seconds.

### Output example (real run, 2026-10-21, `calendly.com`, shortened)

```json
{
  "domain": "calendly.com",
  "companyName": "Calendly",
  "companyDescription": "Calendly connects and simplifies all of the work around meetings, from scheduling, payments, and meeting prep to notetaking, contact management, and follow-up.",
  "primaryEmail": "andrija.djuric@calendly.com",
  "emails": [
    "andrija.djuric@calendly.com",
    "callie@calendly.com",
    "jennifer.garcia@calendly.com",
    "thomas.nguyen@calendly.com"
  ],
  "emailChecks": [
    {
      "email": "andrija.djuric@calendly.com",
      "status": "deliverable_domain",
      "syntaxValid": true,
      "domainHasMx": true,
      "mxHost": "aspmx.l.google.com",
      "isRoleBased": false,
      "isDisposable": false,
      "matchesWebsiteDomain": true,
      "isFreeProvider": false
    },
    "... one entry per email"
  ],
  "phones": [],
  "linkedin": [
    "https://www.linkedin.com/company/calendly",
    "https://www.linkedin.com/company/calendly/life/lifeatcalendly"
  ],
  "twitter": [
    "https://x.com/calendly"
  ],
  "facebook": [
    "https://www.facebook.com/Calendly"
  ],
  "instagram": [
    "https://www.instagram.com/calendly"
  ],
  "youtube": [
    "https://www.youtube.com/c/Calendly"
  ],
  "techHints": [
    "Cloudflare",
    "Google Tag Manager"
  ],
  "language": "en",
  "pagesCount": 6
}
```

### What does it extract?

| Field | Description |
|---|---|
| `companyName`, `companyDescription`, `address`, `logo`, `language`, `foundingDate`, `employeesHint` | From schema.org Organization data, Open Graph and meta tags |
| `emails`, `primaryEmail` | Company-domain emails first. The primary email prefers a valid contact inbox (info@, contact@, hello@, sales@). No-reply addresses are excluded |
| `emailChecks` | Per email: `status` (`deliverable_domain` = domain has MX servers, `no_mx_a_record_only`), `mxHost`, `isRoleBased`, `isDisposable`, `isFreeProvider`, `matchesWebsiteDomain` |
| `phones`, `primaryPhone` | Normalized to digits (E.164 style when the site gives the international prefix) |
| `linkedin`, `twitter`, `facebook`, `instagram`, `youtube`, `tiktok`, `github`, `whatsapp` | Profile URLs (share buttons ignored) |
| `techHints` | e.g. WordPress, Shopify, HubSpot, Intercom, Google Analytics, Stripe, Next.js, Cloudflare (about 45 signatures) |
| `pagesVisited`, `pagesCount`, `error` | Audit trail |

The email check is a **signal, not a guarantee**: it confirms the address is well formed and that its domain accepts mail. It does not open an SMTP connection to the mailbox, because that is unreliable and gets IPs blocked.

### Honest, simple pricing

- **$2.00 per 1,000 websites with contacts** ($0.002 per website), whatever the number of pages, emails or socials.
- **Websites where nothing is found are free.** Dead domains, blocked sites, timeouts and duplicates cost $0.
- **No start fee, no per-page fee, no per-email fee, no proxy or browser surcharges.** 10,000 enriched websites cost $20, and you know the maximum before you run.

### Built to fix what users complain about elsewhere

| Common complaint about other contact scrapers | What this Actor does |
|---|---|
| "Says $2 per 1,000 but charged me $71" / "$20 per 1,000 links, not $2" | One flat event per website with contacts. No per-page, proxy or browser events. |
| "It just keeps running" / "crawling takes much longer" | Page cap per site (default 6) **and** a per-site time limit (default 60 s). |
| "A single website crawl yielded 1,800 results" / messy output | Exactly **one row per company** with flat fields and deduplicated lists. |
| "Duplicate originalStartUrl" / paying twice | Inputs are deduplicated by domain (www/http/https), and sites that redirect to the same domain are only charged once. |
| "I can't get company phone numbers" | Phones from `tel:` links, page text **and schema.org structured data**. |
| Emails of unknown quality | Free **syntax + MX/DNS check** for every email, role-based/disposable/free-provider flags. Emails on non-existent domains are removed. |

### Ready-made presets

| Preset | Input |
|---|---|
| SaaS lead enrichment (default depth, email validation on) | `{"websites": ["hubspot.com", "intercom.com", "calendly.com"], "maxPagesPerWebsite": 6, "validateEmails": true}` |
| Fast scan (homepage + 1 page, no email check) | `{"websites": [...], "maxPagesPerWebsite": 2, "validateEmails": false}` |
| Deep crawl with audit rows (free rows for sites with nothing found) | `{"websites": [...], "maxPagesPerWebsite": 15, "includeWebsitesWithoutContacts": true}` |

### Workflow: domain → company + contacts (with Company Data Enrichment)

[Website Contact Finder](https://apify.com/datadesk-io/website-contact-finder) and [Company Data Enrichment](https://apify.com/datadesk-io/company-data-enrichment) are built to be used together on the same list of domains. Join their outputs on `domain`:

| Step | Actor | Gives you | Price |
|---|---|---|---|
| 1 | Company Data Enrichment | Who the company is: industry, HQ, employees, revenue, CEO, legal entity (LEI), SEC data | $3 / 1,000 companies found |
| 2 | Website Contact Finder | How to reach them: emails with MX check, phones, social profiles, tech stack | $2 / 1,000 sites with contacts |

Real output for `hubspot.com` from both Actors (runs on 2026-10-21, shortened):

Contacts (Website Contact Finder):

```json
{
  "domain": "hubspot.com",
  "companyName": "HubSpot",
  "primaryEmail": null,
  "primaryPhone": "+16178125820",
  "phonesTotal": 58,
  "linkedin": [
    "https://www.linkedin.com/company/hubspot",
    "... 17 more (company + people pages)"
  ],
  "twitter": [
    "https://x.com/HubSpot",
    "..."
  ],
  "facebook": [
    "https://www.facebook.com/hubspot"
  ],
  "techHints": [
    "Cloudflare",
    "Google Analytics",
    "HubSpot"
  ]
}
```

Company (Company Data Enrichment):

```json
{
  "name": "HubSpot",
  "industries": [
    "software industry",
    "software as a service",
    "..."
  ],
  "foundedDate": "2006-01-01",
  "headquarters": [
    "Cambridge"
  ],
  "employees": 8246,
  "revenue": 2630000000,
  "revenueCurrency": "United States dollar",
  "ceo": [
    "Yamini Rangan"
  ],
  "tickers": [
    "HUBS"
  ],
  "registry": {
    "lei": "529900BLNVEW0MVKJB37",
    "legalName": "HUBSPOT, INC."
  },
  "sec": {
    "cik": "0001404655",
    "sicDescription": "Services-Prepackaged Software"
  }
}
```

**No-code:** run both on the same domain list (or chain them with an Apify integration / webhook on "run succeeded"), export both datasets and merge on `domain`.

**API (Python):**

```python
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
domains = ["hubspot.com", "calendly.com", "stripe.com"]
companies = client.actor("datadesk-io/company-data-enrichment").call(run_input={"companies": domains})
contacts = client.actor("datadesk-io/website-contact-finder").call(run_input={"websites": domains})
by_domain = {r["domain"]: r for r in client.dataset(contacts["defaultDatasetId"]).iterate_items()}
for c in client.dataset(companies["defaultDatasetId"]).iterate_items():
    lead = {**c, "contacts": by_domain.get(c.get("domain"))}
    print(lead["name"], lead.get("employees"), (lead["contacts"] or {}).get("primaryEmail"))
```

**AI agents (MCP):** load both tools in one MCP server:

`https://mcp.apify.com/?tools=datadesk-io/company-data-enrichment,datadesk-io/website-contact-finder`

Then ask, for example: *"For hubspot.com, calendly.com and stripe.com, get industry, employee count and CEO, then the best contact email (deliverable domains only) and LinkedIn page, and give me one table."* The agent calls Company Data Enrichment with `{"companies": [...]}` and Website Contact Finder with `{"websites": [...]}`, then joins the results on `domain`. Use `maxCompanies` / `maxWebsites` to cap the cost of each call.

### How it works

For each domain it loads the homepage and then the most relevant pages on the same site: **contact, about, imprint/Impressum, team, support, legal** (English, Spanish, Portuguese and German). It uses fast HTTP with no browser, so 1,000 domains take minutes.

### Limits

- Sites that show contacts only through JavaScript, or that block bots (Cloudflare challenges), may return fewer contacts. They are free when nothing is found.
- It only collects data that the company publishes on its own website. No guessing of email patterns and no third-party databases.

### Use with AI agents (MCP)

This Actor works as a tool for AI agents through the [Apify MCP server](https://mcp.apify.com). Add it to Claude (Desktop/Code), ChatGPT, Cursor, VS Code or any MCP client:

`https://mcp.apify.com/?tools=datadesk-io/website-contact-finder`

Sign in with your Apify account (OAuth) or pass `Authorization: Bearer <APIFY_TOKEN>`. Local stdio config:

```json
{"mcpServers": {"leads": {"command": "npx", "args": ["-y", "@apify/actors-mcp-server", "--tools", "datadesk-io/website-contact-finder"], "env": {"APIFY_TOKEN": "<token>"}}}}
```

The agent sees the input schema, runs the Actor and reads the dataset. Every input is optional and every output field is documented. Tip: set `maxWebsites` to cap the cost of a single tool call.

**Example prompt:**

> Enrich basecamp.com, notion.so and linear.app: give me the best contact email (only deliverable domains), phone, LinkedIn page and the tech they use.

Plain REST: `POST https://api.apify.com/v2/acts/datadesk-io~website-contact-finder/run-sync-get-dataset-items?token=...` with the JSON input as the body.

### FAQ

**Is this legal?** It collects contact details that companies publish on their own websites. You are responsible for using personal data (for example named employee emails) in line with the GDPR, CAN-SPAM and other rules that apply to you.
**Make, Zapier, n8n?** Yes, through the Apify integrations and API.
**Missing a field or a site that fails?** Open an issue in the Issues tab.

# Actor input Schema

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

Company domains or website URLs (e.g. stripe.com, https://www.python.org). Duplicates (www/http/https/same domain) are processed and charged once. One result per company.

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

Homepage plus the most relevant pages (contact, about, imprint, team, support). Price does not depend on pages.

## `validateEmails` (type: `boolean`):

Check each email syntax, whether its domain has MX mail servers, role-based (info@, sales@), disposable and free-provider flags. Emails on non-existent domains are dropped. No paid API, no SMTP probing.

## `extractPhonesFromText` (type: `boolean`):

Besides tel: links, detect phone numbers written in the text (international or 10+ digit formats).

## `includeWebsitesWithoutContacts` (type: `boolean`):

Also output websites where nothing was found (with the error, if any). These rows are never charged.

## `maxWebsites` (type: `integer`):

Hard cap on how many unique websites are processed in this run.

## `perWebsiteTimeoutSecs` (type: `integer`):

A slow site never blocks the run: it is stopped after this time.

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

Websites processed in parallel.

## Actor input object example

```json
{
  "websites": [
    "python.org",
    "djangoproject.com",
    "basecamp.com",
    "mozilla.org",
    "stripe.com"
  ],
  "maxPagesPerWebsite": 6,
  "validateEmails": true,
  "extractPhonesFromText": true,
  "includeWebsitesWithoutContacts": false,
  "perWebsiteTimeoutSecs": 60,
  "maxConcurrency": 10
}
```

# Actor output Schema

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

All websites with contacts. Each item: domain, companyName, companyDescription, emails, primaryEmail, emailChecks, phones, socials, address, techHints (see dataset schema for field descriptions).

# 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": [
        "python.org",
        "djangoproject.com",
        "basecamp.com",
        "mozilla.org",
        "stripe.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datadesk-io/website-contact-finder").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": [
        "python.org",
        "djangoproject.com",
        "basecamp.com",
        "mozilla.org",
        "stripe.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("datadesk-io/website-contact-finder").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": [
    "python.org",
    "djangoproject.com",
    "basecamp.com",
    "mozilla.org",
    "stripe.com"
  ]
}' |
apify call datadesk-io/website-contact-finder --silent --output-dataset

```

## MCP server setup

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

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/pHak0d6O7IF6aUvos/builds/rDRspGkyIgPyPMbR5/openapi.json
