# Website Contact Extractor: Emails, Phones & Socials (`waags/website-contact-extractor`) Actor

Extract emails, phone numbers and social profiles (LinkedIn, Facebook, Instagram, X, YouTube, TikTok) from any list of company websites. Crawls the homepage and contact/about pages, decodes protected emails, and returns one row per site. Sites without contacts are free.

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

## Pricing

$2.40 / 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.

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 Extractor: Bulk Emails, Phone Numbers & Social Profiles

Paste a list of company websites and get back **one clean row per website** with every **email address**, **phone number (validated, E.164)** and **social profile** (LinkedIn, Facebook, Instagram, X/Twitter, YouTube, TikTok, Pinterest, GitHub, Yelp) found on the homepage and its **contact, about and team pages**. It decodes **Cloudflare-protected emails** and `name [at] domain [dot] com` obfuscation, and picks a **primary email and phone** for you.

**You only pay for websites where contacts were found.** Sites with nothing, or that fail to load, are free. No login, no API key, no browser.

### Why this contact scraper?

- 🧾 **One row per website, not per email.** Ready for CRM import with `emails[]`, `phones[]` and one column per social network.
- 🔎 **Finds the right pages:** homepage plus up to N same-site pages whose path looks like contact, about, team, staff, leadership, locations or support
- 🛡️ **Protected emails decoded:** Cloudflare `data-cfemail`, HTML entities (`&#64;`), `[at]`/`(dot)` obfuscation
- ☎️ **Real phone numbers only:** `tel:` links plus text numbers validated with Google's libphonenumber (no order IDs or dates), normalized to E.164
- 🚫 **Junk filtered:** image filenames (`logo@2x.png`), placeholder addresses (`you@example.com`), tracking and Sentry addresses
- ⭐ **Primary contact picked:** prefers an inbox on the site's own domain, then `info@`, `contact@`, `hello@`, `sales@`…
- 💸 **No contacts, no charge**, and duplicate domains are merged before crawling
- ⚡ **Lightweight and memory-safe:** streaming downloads capped per page, no headless browser, so it is cheap and fast

### Use cases

- **Lead enrichment:** turn a list of company domains (from a CRM, a spreadsheet, Google Maps, a directory or a trade-show list) into emails and phones
- **Local business outreach:** contractors, agencies, clinics, restaurants, dealers
- **Partner & supplier research:** find the right contact channel for any website
- **Data hygiene:** pair with our validators to keep only deliverable emails and valid phones

### Works great with

1. **[Bulk Email Validator](https://apify.com/waags/bulk-email-validator):** pick this run's dataset as input, field `emails`. It removes bad, disposable and role addresses.
2. **[US Phone Number Validator](https://apify.com/waags/us-phone-number-validator):** dataset input, field `phones`. Adds line type, state, time zone and calling hours.
3. **[Domain WHOIS Lookup](https://apify.com/waags/domain-whois-lookup)** and **[Website Tech Stack Detector](https://apify.com/waags/website-tech-stack-detector):** dataset input, field `domain`. Adds domain age and the site's technologies.

### How to use it

1. Click **Try for free**. A few sample websites are prefilled, so the first run works in one click.
2. Paste your websites or bare domains (one per line), **or** pick an **Input dataset** and set the **Website field** (e.g. `website` from a Google Maps scrape).
3. Click **Start** and export to **CSV, Excel or JSON**, or use the API, Zapier, Make or n8n.

#### Input example

```json
{
    "websites": ["acmeplumbing.com", "https://www.sunrun.com", "www.servicemaster.com"],
    "maxPagesPerSite": 4,
    "defaultCountry": "US",
    "includeSitesWithoutContacts": false,
    "maxItems": 1000
}
```

| Field | Description |
| --- | --- |
| `websites` | URLs or bare domains. One row per domain. |
| `inputDatasetId` + `websiteField` | Read websites from another dataset (dot paths and arrays supported) |
| `maxPagesPerSite` | Homepage + up to N−1 contact/about/team pages (default 4) |
| `defaultCountry` | Country for validating phone numbers without a `+` prefix (default `US`) |
| `includeSitesWithoutContacts` | Also output free rows for sites with nothing found or that failed |
| `maxItems` | Maximum websites to process |

#### Output example

```json
{
    "website": "sunrun.com",
    "domain": "sunrun.com",
    "finalUrl": "https://www.sunrun.com/",
    "title": "Sunrun | America's #1 Home Storage & Solar Power Company",
    "primaryEmail": "press@sunrun.com",
    "emails": ["press@sunrun.com"],
    "primaryPhone": "+18333245886",
    "phones": ["+18333245886", "+18333862912", "+18554786786"],
    "facebook": "https://www.facebook.com/sunrun",
    "instagram": "https://www.instagram.com/sunrunsolar",
    "linkedin": "https://www.linkedin.com/company/sunrun",
    "twitter": "https://twitter.com/Sunrun",
    "youtube": null,
    "contactPageUrl": "https://www.sunrun.com/contact-us",
    "pagesCrawled": 3,
    "error": null
}
```

The **Contacts** view shows domain, title, primary email, all emails, primary phone, all phones and the main social links.

### Pricing

Pay-per-event: a fixed price per website **where at least one contact was found**, currently **about $2.40 per 1,000 websites** (see the **Pricing** tab). Websites with no contacts, unreachable sites and duplicates are free. There is no start fee and no compute charge on top.

### FAQ

**Why were no contacts found for some sites?** Some websites hide contacts behind forms or JavaScript widgets, or block automated requests (large enterprise sites behind Cloudflare/Akamai). Those runs are free. Small and mid-size business sites, the usual lead-gen targets, work best.

**Does it use a browser?** No. It downloads HTML directly (streamed and capped), which makes it fast, cheap and light. Sites that render contacts only with JavaScript won't show them.

**Are the emails verified?** They are extracted exactly as published. Run them through our [Bulk Email Validator](https://apify.com/waags/bulk-email-validator) to check domains, MX records, disposable and role addresses.

**Is it legal?** It only reads publicly available pages. You are responsible for using the contacts lawfully (GDPR, CAN-SPAM, TCPA).

**Problems?** Report them on the **Issues** tab. The actor is monitored daily.

### Related actors by WAAGS

- [Bulk Email Validator](https://apify.com/waags/bulk-email-validator) · [US Phone Number Validator](https://apify.com/waags/us-phone-number-validator)
- [Domain WHOIS Lookup](https://apify.com/waags/domain-whois-lookup) · [Website Tech Stack Detector](https://apify.com/waags/website-tech-stack-detector)
- Lead sources: [Building Permits](https://apify.com/waags/building-permits-leads) · [Contractor Licenses](https://apify.com/waags/contractor-license-leads) · [SEC Form D](https://apify.com/waags/sec-form-d-funding-leads) · [FMCSA Carriers](https://apify.com/waags/fmcsa-trucking-carrier-leads)

# Actor input Schema

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

Company websites or bare domains, one per line (e.g. acme.com or https://www.acme.com/). Duplicates are merged per domain.

## `inputDatasetId` (type: `string`):

Pick another Apify dataset, e.g. a Google Maps or business-directory scrape, whose websites should be processed. Set the field name in "Website field".

## `websiteField` (type: `string`):

Name of the field that holds the website URL in the input dataset. Dot notation is supported (e.g. contact.website).

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

Homepage plus up to N-1 contact/about/team pages found on it. More pages find more contacts but take longer.

## `defaultCountry` (type: `string`):

ISO country used to validate phone numbers written without a +country code.

## `includeSitesWithoutContacts` (type: `boolean`):

Add a free row for websites where nothing was found or that failed to load, so you can see every input in the output. These rows are never charged.

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

Maximum number of websites to process. You are charged only for websites where contacts were found, so this also caps your cost.

## Actor input object example

```json
{
  "websites": [
    "https://apify.com",
    "https://www.sunrun.com",
    "https://www.servicemaster.com",
    "https://www.mrrooter.com",
    "https://www.basecamp.com"
  ],
  "websiteField": "website",
  "maxPagesPerSite": 4,
  "defaultCountry": "US",
  "includeSitesWithoutContacts": false,
  "maxItems": 20
}
```

# Actor output Schema

## `results` (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://apify.com",
        "https://www.sunrun.com",
        "https://www.servicemaster.com",
        "https://www.mrrooter.com",
        "https://www.basecamp.com"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("waags/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://apify.com",
        "https://www.sunrun.com",
        "https://www.servicemaster.com",
        "https://www.mrrooter.com",
        "https://www.basecamp.com",
    ],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("waags/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://apify.com",
    "https://www.sunrun.com",
    "https://www.servicemaster.com",
    "https://www.mrrooter.com",
    "https://www.basecamp.com"
  ],
  "maxItems": 20
}' |
apify call waags/website-contact-extractor --silent --output-dataset

```

## MCP server setup

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