# Website Contact Scraper – Emails, Phones & Socials (`dedicated_crest/website-contact-enrichment`) Actor

Bulk-enrich company websites or domains: emails (MX-checked, same-domain first), phone numbers, LinkedIn/Facebook/Instagram/X/YouTube/TikTok and 7 more socials, contact page URLs, company name and address. robots.txt respected. $2 per 1,000 websites, only when contacts are found.

- **URL**: https://apify.com/dedicated_crest/website-contact-enrichment.md
- **Developed by:** [Ben Barazandeh](https://apify.com/dedicated_crest) (community)
- **Categories:** Lead generation, Automation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 website with contacts founds

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?

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 Contact Scraper – Emails, Phones & Socials

Paste a list of **company websites or domains** and get **one clean row per company** back: **emails** (same-domain first, MX-checked), **phone numbers**, **LinkedIn, Facebook, Instagram, X/Twitter, YouTube, TikTok, Pinterest, GitHub, Threads, Telegram, WhatsApp, Discord and Yelp** links, **contact-page URLs**, plus the company name, address, page title, description and language.

Made for enriching lead lists, such as businesses from [Local Business Leads](https://apify.com/dedicated_crest/osm-local-business-leads), a CRM export or a spreadsheet of domains. It reads only **public pages on each company's own website**, follows **robots.txt** and crawls politely.

**$2 per 1,000 websites, and only for websites where at least one email, phone or social profile was found.** Websites that are unreachable, blocked by robots.txt or have nothing to extract are free.

### Who is it for?

- **Sales and lead-gen teams**: add emails, phones and socials to lists of domains before outreach
- **Agencies**: enrich local-business lists (restaurants, dentists, trades) with a contact email and Instagram/Facebook pages
- **CRM hygiene**: fill missing company phone numbers, LinkedIn pages and contact pages
- **Market research**: see which companies are active on TikTok, Instagram or YouTube

### Features

- 📧 Emails from `mailto:` links, page text, **schema.org JSON-LD**, **Cloudflare-protected** addresses and "name \[at] domain \[dot] com" obfuscation
- ✅ Each email is tagged **sameDomain**, **role address** (info@, sales@…) and **domainAcceptsMail** (DNS MX check). Placeholder and tracking addresses (example.com, sentry, wixpress…) are dropped.
- 📞 Phones from `tel:` links and schema.org data, de-duplicated. Phone-like numbers found only in page text are kept separately in `phonesUncertain`.
- 🌐 13 social networks, plus `contactPageUrls` for contact forms
- 🏢 Company name and postal address from schema.org/OpenGraph when the site publishes them
- 🔎 Crawls the homepage plus up to 19 contact/about/impressum/team pages on the same domain. If no contact link exists, it tries `/contact`, `/contact-us` and `/about`.
- 🤝 **Polite**: robots.txt (including Crawl-delay) respected, 1 request per second per website by default, an identifiable User-Agent (`ContactEnrichmentBot`), and a time budget per site. Many websites are processed in parallel, but each site is crawled one page at a time.
- ♻️ Retries with backoff on 429/5xx, http/https fallback, duplicate domains removed
- 💵 Stops cleanly at your **Maximum cost per run**

### Input example

```json
{
  "websites": ["stripe.com", "https://www.defenseplumbing.com/", "griffon.paris", "info@metrocityroofing.com"],
  "maxPagesPerSite": 5
}
```

### Output example

Real output from live run MudyC1mCEnClL4X3J (one website):

```json
{
  "input": "https://www.aloha-dental.com/",
  "domain": "aloha-dental.com",
  "url": "https://www.aloha-dental.com/",
  "finalUrl": "https://www.aloha-dental.com/",
  "finalDomain": "aloha-dental.com",
  "status": "ok",
  "httpStatus": 200,
  "companyName": "Aloha Dental",
  "title": "Dentist in Austin, TX | Aloha Dental | Dr. Blake Mulgrew DMD, Dr. Holly Langendorfer DMD, Dr. Mark Whitney Jumper DDS",
  "description": "Home. Aloha Dental offers Invisalign, Extractions, Root Canals, Botox for TMJ and more in Austin, TX 78741 Call:512-707-7300",
  "language": "en-US",
  "emails": [
    "riverside@aloha-dental.com"
  ],
  "emailsSameDomain": [
    "riverside@aloha-dental.com"
  ],
  "primaryEmail": "riverside@aloha-dental.com",
  "phones": [
    "(512)707-7300"
  ],
  "phonesUncertain": [
    "(512) 494-5655"
  ],
  "address": "1614 E. Riverside DR., Austin, Texas, 78741, US",
  "contactPageUrls": [
    "https://www.aloha-dental.com/contact-us/"
  ],
  "linkedin": [],
  "facebook": [
    "https://www.facebook.com/people/Aloha-Dental-Texas/61557771617859"
  ],
  "instagram": [
    "https://www.instagram.com/alohadentaltx"
  ],
  "twitter": [],
  "youtube": [],
  "tiktok": [],
  "pinterest": [],
  "github": [],
  "threads": [],
  "telegram": [],
  "whatsapp": [],
  "discord": [],
  "yelp": [
    "https://www.yelp.com/biz/aloha-dental-austin"
  ],
  "emailDetails": [
    {
      "email": "riverside@aloha-dental.com",
      "foundOn": "https://www.aloha-dental.com/",
      "method": "cloudflare-protected",
      "domainAcceptsMail": true,
      "sameDomain": true,
      "role": false
    }
  ],
  "phoneDetails": [
    {
      "phone": "(512)707-7300",
      "foundOn": "https://www.aloha-dental.com/",
      "method": "tel-link"
    }
  ],
  "pagesCrawled": 5,
  "crawledUrls": [
    "https://www.aloha-dental.com/",
    "https://www.aloha-dental.com/contact-us/",
    "https://www.aloha-dental.com/about-us/",
    "https://www.aloha-dental.com/our-team/",
    "https://www.aloha-dental.com/location/"
  ],
  "error": null,
  "contactsFound": true,
  "charged": true,
  "scrapedAt": "2026-10-17T21:20:00.473Z"
}
```

Every input website gets a row. Websites where nothing was extracted have `status` = `blocked_by_robots_txt`, `dns_not_found`, `http_404`, `timeout` and so on, and `charged: false`. A `SUMMARY` record shows the counts by status.

### Real test results

Live runs on Apify, 17 Oct 2026. Fill rates are measured over the websites that were reached (`status: ok`).

| Test | Websites | Reached | Charged | Email | Same-domain email | Phone | Facebook | Instagram | LinkedIn | X | Contact page | Company name | Avg pages |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 100 Manchester restaurant/café URLs (90 unique) | 90 | 72 | 65 | 68% | 59% | 62% | 65% | 76% | 11% | 33% | 70% | 80% | 2.8 |
| 57 mixed URLs: US roofers/plumbers/dentists, Paris cafés, 12 SaaS companies (55 unique) | 55 | 49 | 47 | 55% | 48% | 57% | 77% | 69% | 59% | 34% | 89% | 85% | 3.8 |
| Maximum cost per run = $0.01 | 90 | – | **5 ($0.010)** | | | | | | | | | | |

Websites that weren't reached: blocked by robots.txt (7 and 4, never crawled), HTTP 403/404, DNS not found, timeouts. None of them were charged. Platform cost was about $0.002–0.008 per 55–90 websites.

### Pricing

| Event | Price |
|---|---|
| Website with contacts found (up to *Max pages per website* crawled) | **$0.002** ($2 per 1,000 websites) |
| Unreachable, robots.txt-blocked or empty website | free |

Other contact scrapers on the Store charge **per page crawled**: $2 per 1,000 pages, even when nothing is found. Here a full website visit (homepage plus contact and about pages, 2.8 pages on average in our tests) costs the same as one page there, and you pay nothing for websites with no contact details.

### Compliance note

- **Public data only.** The Actor never logs in, never fills forms or solves CAPTCHAs, and never sends email or probes mailboxes. The MX check is a DNS lookup on the email's domain.
- **robots.txt is honoured.** If a site disallows crawling for generic bots (`User-agent: *`) or for `ContactEnrichmentBot`, that site gets `blocked_by_robots_txt` and is not crawled.
- **Personal data.** Websites sometimes list named employees' emails or phone numbers. These count as personal data under the GDPR, UK GDPR, CCPA and similar laws. **You are the data controller.** Have a lawful basis (e.g. legitimate interest for B2B outreach), honour opt-outs, and follow anti-spam rules (CAN-SPAM, CASL, PECR/ePrivacy). Role addresses (info@, sales@) are flagged `role: true`. If you need to minimise personal data, keep only those.
- Use the results in line with each website's terms. Don't use the Actor to build spam lists.

### FAQ

**Can I pass domains, URLs or emails?** Yes, all three. For an email address, its domain is used.

**Why was a site `blocked_by_robots_txt`?** Its owner disallows automated crawling. We respect that, and you aren't charged.

**Some sites block data-centre IPs. What can I do?** Set a proxy (Apify Proxy) in the input. Proxy traffic is billed by Apify.

**Does it run JavaScript?** No. It reads the HTML the server sends, which is fast and cheap. Sites that only render contacts with JavaScript (rare for contact details) may return fewer results.

# Actor input Schema

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

One per line: domains (acme.com), full URLs, or even email addresses (the domain is used). Duplicates are removed. Paste up to 100,000.

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

Homepage + contact / about / impressum / team pages on the same domain. 3–5 finds almost everything.

## `tryCommonContactPaths` (type: `boolean`):

If the homepage has no visible contact link, also try /contact, /contact-us and /about (only if allowed by robots.txt).

## `checkEmailDomains` (type: `boolean`):

DNS check that each email's domain has a mail server; emails on dead domains are dropped. No email is sent and no mailbox is probed.

## `concurrency` (type: `integer`):

Different websites processed at the same time. Each single website is always crawled sequentially.

## `perSiteDelayMs` (type: `integer`):

Politeness delay per website. A robots.txt Crawl-delay (up to 10 s) is honoured if larger.

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

Usually not needed. Some sites block data-centre IPs; Apify Proxy may help (proxy usage billed by Apify).

## `maxItems` (type: `integer`):

Stop after this many websites with contacts found. The run also stops cleanly at your Maximum cost per run.

## Actor input object example

```json
{
  "websites": [
    "stripe.com",
    "https://www.defenseplumbing.com/",
    "griffon.paris"
  ],
  "maxPagesPerSite": 5,
  "tryCommonContactPaths": true,
  "checkEmailDomains": true,
  "concurrency": 10,
  "perSiteDelayMs": 1000,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxItems": 100
}
```

# Actor output Schema

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

One row per website

## `summary` (type: `string`):

Counts by status

# 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": [
        "stripe.com",
        "https://www.defenseplumbing.com/",
        "griffon.paris"
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("dedicated_crest/website-contact-enrichment").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": [
        "stripe.com",
        "https://www.defenseplumbing.com/",
        "griffon.paris",
    ],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("dedicated_crest/website-contact-enrichment").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": [
    "stripe.com",
    "https://www.defenseplumbing.com/",
    "griffon.paris"
  ],
  "maxItems": 100
}' |
apify call dedicated_crest/website-contact-enrichment --silent --output-dataset

```

## MCP server setup

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

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/qPFqWrhmM6FRkPYvd/builds/sz93oOCdV1a2IMM9j/openapi.json
