# Website Contact Scraper: Emails, Phones & Social Links (`precious_bathmat/website-contact-scraper`) Actor

Find emails, phone numbers and social media profiles (LinkedIn, Facebook, Instagram, X, YouTube, TikTok) on any list of websites. Reads the home page plus contact, about and imprint pages. Decodes protected emails. Only charged when contacts are found.

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

## Pricing

$3.00 / 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 Scraper: Emails, Phones & Social Links

Turn a list of websites into a **contact list**. For every site you get **emails**, **phone numbers** and **social media profiles** (LinkedIn, Facebook, Instagram, X, YouTube, TikTok, Pinterest and more) in one clean row, plus the address, the contact page and whether the site has a contact form.

- **Reads more than the home page**: it follows the site's own **contact, imprint (Impressum), about and support pages**, where contact details usually live
- **Finds hidden emails**: `mailto:` links, plain text, **Cloudflare-protected addresses** and "name \[at] domain \[dot] com"
- **Clean results**: no image file names, placeholder addresses (`your@email.com`), error-tracker addresses or share buttons
- **Phones you can call**: taken from click-to-call links, the site's structured data and WhatsApp links, never guessed from random numbers in the page code
- **The company's own social profiles**: share buttons and accounts of embedded widgets are left out
- **Pay only for results**: websites where nothing is found are **free**

### What does this website contact scraper do?

```
rotorooter.com
 Phone 800-768-6911 · contact page /contact-us · contact form yes
 LinkedIn /company/roto-rooter · Facebook /rotorooter · Instagram /rotorooterplumbing
 X /rotorooter · YouTube /user/RotoRooterTV · Pinterest /RotoRooter1
 Address 255 East 5th St, #2500, Cincinnati, OH, 45202, US
```

1. Paste **websites**: domains (`acme.com`) or full addresses, thousands at a time.
2. For each site, the scraper reads the **home page** and up to four of the **most likely contact pages**.
3. You get **one row per website** with every email, phone and social profile found, and a CSV ready for your CRM.

```mermaid
flowchart LR
    A[Website list] --> B[Home page]
    B --> C[Contact, imprint,<br/>about, support pages]
    B --> D[Emails, phones,<br/>social profiles]
    C --> D
    D --> E[One row per website<br/>+ CSV]
```

### Who is it for?

- **Sales and lead generation**: add emails, phones and LinkedIn pages to a list of company websites, for example the websites from a **Google Maps** export
- **Agencies and freelancers** prospecting local businesses: plumbers, dentists, lawyers, restaurants
- **Marketers** building outreach lists for partnerships, PR and link building
- **Recruiters** looking for a company's careers or HR mailbox
- **Data teams** enriching and checking company records in a CRM

### What data do you get?

**Contacts** (dataset, one row per website with at least one contact)

| Field | What it tells you |
|---|---|
| `domain`, `website`, `companyName`, `description`, `language` | Which site, as it names itself |
| `emails`, `primaryEmail` | Every email found; the first is on the site's own domain when there is one |
| `phones`, `primaryPhone`, `whatsapp` | Phone numbers as written, and a WhatsApp number when the site links one |
| `linkedin`, `facebook`, `instagram`, `twitter`, `youtube`, `tiktok`, `pinterest`, `github`, `threads`, `telegram` | The company's profile on each network |
| `address`, `businessType` | From the site's structured data, when it has it |
| `contactPageUrl`, `hasContactForm` | Where to get in touch when there is no email |
| `emailDetails`, `phoneDetails` | Each email and phone with the page it was found on |
| `allSocialProfiles` | Every profile found per network, best match first |
| `pagesRead`, `personalEmailsLeftOut` | What was read, and how many personal mailboxes were skipped |

`CONTACTS_CSV` holds the same rows as flat columns. `RUN_SUMMARY` counts what was found and gives the reason for every website with no contacts (no contacts published, bot check, site down, domain does not exist).

### Example: 7 October 2026

**152 websites in 2 minutes 7 seconds**: 112 small businesses from a Google Maps search for plumbers around Chicago, and 40 company sites.

| Found | Websites | Share |
|---|---|---|
| Any contact detail | 118 | 78% |
| Phone number | 86 | 57% |
| Social profile | 90 | 59% |
| Email | 71 | 47% |
| Contact form | 55 | 36% |
| LinkedIn company page | 44 | 29% |

- Small businesses publish **Gmail, Yahoo and Comcast addresses** as often as addresses on their own domain, so both are kept (switch on **Only emails on the website's own domain** to change that).
- The 34 sites without results: 13 published nothing on the pages read, 11 had a bot check or refused automated visitors, 5 did not answer, 4 returned an error page and 1 domain no longer exists. None of them was charged.

### Pricing

**$0.003 per website with contact details.** Websites where nothing is found cost nothing.

- 100 websites: **at most $0.30**
- 1,000 websites: **at most $3**
- The run above: 118 websites with contacts, **$0.35**

### Good to know

- **Personal data**: by default only **role and brand mailboxes** are saved (info@, sales@, support@, smithplumbing@gmail.com). Mailboxes named after a person (jane@, j.smith@) are **left out and counted** in `personalEmailsLeftOut`. Switch on **Include emails named after people** only if you have a lawful basis to process them. Names of people are never collected.
- **What a site publishes is what you get**: many sites show only a contact form. `contactPageUrl` and `hasContactForm` tell you where to reach them.
- **Bot checks**: a few sites (about 7% in the run above) show a Cloudflare or similar check to automated visitors. They are listed in `RUN_SUMMARY`, not charged.
- **Pages per website**: 5 by default (home page plus up to four contact pages). Raise it for large sites with several office pages.
- **Speed**: 10 websites at once by default, about 1 second per website in the run above.
- **Same site twice**: inputs on the same site (`acme.com` and `www.acme.com/about`) are read once.
- No login, no API key and no proxies needed.

### Input

| Field | Meaning |
|---|---|
| **Websites** | Domains or full addresses, one per line |
| **Pages per website** | 1 to 20 (default 5) |
| **Include emails named after people** | Off by default |
| **Only emails on the website's own domain** | Off by default |
| **Websites at once** | 1 to 25 (default 10) |

### Integrations

Export to JSON, CSV, Excel or Google Sheets, or connect through the Apify API, webhooks, Make, Zapier and n8n. Feed it the `website` column of a **Google Maps** or directory export to turn a list of businesses into a contact list in one step.

# Actor input Schema

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

One per line: a domain (acme.com) or a full address (https://www.acme.com). Each site is read once, even if listed twice.

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

The home page plus the most likely contact pages (contact, impressum/imprint, about, support), in that order. 5 finds almost everything; raise it for big sites.

## `includePersonalEmails` (type: `boolean`):

Off by default: only role and brand mailboxes (info@, sales@, support@, acmeplumbing@gmail.com) are saved, and mailboxes named after a person (jane@, j.smith@) are left out and counted. Turn on only if you have a lawful basis to process personal data.

## `sameDomainEmailsOnly` (type: `boolean`):

Keep only emails ending in the site's domain (info@acme.com for acme.com). Off by default, because many small businesses use Gmail or Outlook addresses.

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

How many websites are read in parallel. Each website's pages are read one after another.

## Actor input object example

```json
{
  "websites": [
    "rotorooter.com",
    "kingarthurbaking.com",
    "pipedrive.com"
  ],
  "maxPagesPerSite": 5,
  "includePersonalEmails": false,
  "sameDomainEmailsOnly": false,
  "concurrency": 10
}
```

# Actor output Schema

## `contacts` (type: `string`):

One row per website: emails, phones, LinkedIn, Facebook, Instagram, X, YouTube, TikTok and more, address, contact page, contact form.

## `csv` (type: `string`):

The same rows as flat CSV.

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

Counts, and the reason for every website without contacts.

# 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": [
        "rotorooter.com",
        "kingarthurbaking.com",
        "pipedrive.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("precious_bathmat/website-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 = { "websites": [
        "rotorooter.com",
        "kingarthurbaking.com",
        "pipedrive.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("precious_bathmat/website-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 '{
  "websites": [
    "rotorooter.com",
    "kingarthurbaking.com",
    "pipedrive.com"
  ]
}' |
apify call precious_bathmat/website-contact-scraper --silent --output-dataset

```

## MCP server setup

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