# Contact Scraper - Website Emails, Phones & Socials (`wrenfield-data/website-contact-scraper`) Actor

Extract emails, phone numbers and social media profiles (LinkedIn, Facebook, Instagram, X, YouTube, TikTok...) from any list of company websites or domains. One clean row per website, contact-page aware, email provider (MX) detection. Pay only for websites with contacts.

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

## Pricing

from $2.50 / 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 Contact Scraper - Emails, Phones & Social Links

**Turn a list of company websites or domains into contact data.** For every website you get **emails**, **phone numbers** (E.164), **LinkedIn, Facebook, Instagram, X/Twitter, YouTube, TikTok** and other social profiles, the **contact page**, **postal address**, and the domain's **email provider** (Google Workspace, Microsoft 365...). You get **one clean row per website**, ready for your CRM, Clay, n8n, Make, Zapier or an AI agent.

- 🎯 **Contact-page aware**: starts at the homepage, then visits only the pages most likely to list contacts (Contact, About, Team, Locations, Impressum / Mentions légales / Aviso legal...) in 20+ languages. It doesn't crawl the whole site.
- 💸 **Pay only for websites with contacts**: a flat price per website, no matter how many pages were checked. Dead domains, blocked sites and sites with no contacts return a **free** row that says why.
- ✅ **Clean data**: decodes Cloudflare-protected and `name [at] domain [dot] com` emails. It drops junk like `image@2x.png`, Sentry/Wix IDs, `example.com` and template placeholders, and filters out emails whose domain can't receive mail. Phones are validated and normalised to E.164.
- 🏷️ **Email labels**: each email is tagged `role` (info@, sales@), `personal` (jane.doe@) or `free` (gmail.com), with `onDomain` and the page it was found on. One switch keeps only role-based emails.
- 📬 **Email provider detection**: from MX records you get Google Workspace, Microsoft 365, Zoho... plus security gateways (Proofpoint, Mimecast, Barracuda), which is useful for segmenting cold-email lists.
- ⚡ **Fast and light**: plain HTTP with a real-browser TLS fingerprint, no headless browser. In our cloud tests 3-25 websites took 7-20 s, and 500 mixed websites took about 3 minutes (1 GB memory).

💸 **$4 per 1,000 websites with contacts found**. Websites with no emails, phones or social links are free. Try it on Apify's free plan ($5 of monthly credit, no credit card).

#### Sample output

| Domain | Email | Phone | LinkedIn | Instagram |
|---|---|---|---|---|
| levainbakery.com | media@levainbakery.com | +18779322161 | linkedin.com/company/levain-bakery | instagram.com/levainbakery |
| katzsdelicatessen.com | cs@katzsdelicatessen.com | +12122542246 | – | instagram.com/katzsdeli |

<sub>Real rows from the prefilled example run (Oct 19, 2026), 5 of 43+ fields shown. Export as JSON, CSV, Excel or HTML, or read them through the API.</sub>

### What data do you get?

| Field group | Fields |
|---|---|
| Contacts | `primaryEmail`, `emails`, `primaryPhone`, `phones`, `address`, `contactPageUrl`, `hasContactForm` |
| Social | `linkedin`, `facebook`, `instagram`, `twitter`, `youtube`, `tiktok`, `pinterest`, `github`, `threads`, `whatsapp`, `telegram`, `discord`, `yelp`, `trustpilot`, `socialProfiles` (all) |
| Company | `domain`, `url`, `companyName`, `legalName`, `logo`, `title`, `description`, `language` |
| Email infrastructure | `emailProvider`, `emailSecurityGateway`, `mxRecords` |
| Details | `emailDetails` (`email`, `type`, `onDomain`, `source`, `page`, `domainAcceptsMail`), `phoneDetails` (`phone`, `international`, `country`, `source`, `page`) |
| Run info | `status`, `error`, `errorType`, `pagesCrawled`, `crawledUrls`, `pagesFailed`, `proxyUsed`, `redirectedFrom`, `sourceName`, `sourceIndex`, `scrapedAt` |

### How to use

1. Paste **websites or domains** into **Websites / domains**, one per line: `acme.com`, `https://www.acme.com`, `acme.com/contact`. Email addresses also work (their domain is used).
2. **Or enrich a dataset**: put the ID of a dataset from another Actor (for example Google Maps Scraper or [Company Name to Domain Finder](https://apify.com/wrenfield-data/company-name-to-domain)) into **Enrich an existing dataset**. The website field is detected automatically (`website`, `url`, `domain`...).
3. Optionally set **Max pages per website** (default 6) and the email filters.
4. Click **Start**. Download the results as JSON, CSV or Excel. The **Leads** view is flat and CSV-ready. You can also call the Actor through the API or MCP.

### Input example

```json
{
    "urls": ["apify.com", "https://www.katzsdelicatessen.com", "levainbakery.com", "bosch.de"],
    "maxPagesPerDomain": 6,
    "extractPhones": true,
    "extractSocials": true,
    "onlyRoleEmails": false,
    "checkMx": true
}
```

### Output example

A real row from a cloud test run (some fields trimmed):

```json
{
    "input": "https://www.katzsdelicatessen.com",
    "domain": "katzsdelicatessen.com",
    "url": "https://katzsdelicatessen.com/",
    "status": "ok",
    "companyName": "Katz's Delicatessen",
    "primaryEmail": "press@katzsdelicatessen.com",
    "emails": ["press@katzsdelicatessen.com", "cs@katzsdelicatessen.com", "events@katzsdelicatessen.com"],
    "primaryPhone": "+12122542246",
    "phones": ["+12122542246"],
    "facebook": "https://www.facebook.com/katzsdeli",
    "instagram": "https://www.instagram.com/katzsdeli",
    "twitter": "https://x.com/KatzsDeli",
    "linkedin": null,
    "contactPageUrl": "https://katzsdelicatessen.com/contact",
    "hasContactForm": true,
    "emailProvider": "Google Workspace",
    "emailSecurityGateway": null,
    "emailDetails": [
        {"email": "press@katzsdelicatessen.com", "type": "role", "onDomain": true, "source": "mailto", "page": "https://katzsdelicatessen.com/", "domainAcceptsMail": true},
        {"email": "events@katzsdelicatessen.com", "type": "role", "onDomain": true, "source": "text", "page": "https://katzsdelicatessen.com/faqs", "domainAcceptsMail": true}
    ],
    "phoneDetails": [
        {"phone": "+12122542246", "international": "+1 212-254-2246", "country": "US", "source": "tel", "page": "https://katzsdelicatessen.com/"}
    ],
    "pagesCrawled": 6
}
```

A website without contacts, or one that fails, returns a **free** row:

```json
{"input": "notarealdomain-xyz123.com", "domain": "notarealdomain-xyz123.com", "status": "error", "errorType": "dns_not_found", "error": "Domain does not resolve (DNS lookup failed). Not charged.", "emails": [], "phones": [], "socialProfiles": []}
```

| `status` | Meaning | Charged? |
|---|---|---|
| `ok` | At least one email, phone or social profile found | Yes |
| `no_contacts` | The website loaded but showed no contact details on the pages checked | No |
| `error` | DNS failure, timeout, blocked, HTTP error, not an HTML site (`errorType` says which) | No |
| `robots_disallowed` | The site's robots.txt disallows all crawlers (see the Advanced setting) | No |
| `invalid_input` | Not a website (e.g. a Facebook page URL, an IP address) | No |

### How it works

1. **Homepage**: it tries `https://domain`, then `www.` and then `http://`, and follows redirects. If the site moved to another domain, you get `redirectedFrom`.
2. **Page selection**: links are ranked by how likely they hold contacts (contact > impressum/legal > about > team > locations > support), using the URL and the link text in 20+ languages. Blog posts and deep paths are skipped. If there's no contact link, `/contact` and `/contact-us` are tried.
3. **Extraction**:
   - **Emails** come from `mailto:` links, schema.org JSON-LD, Cloudflare email protection, visible text, and obfuscated `[at]`/`(at)` forms.
   - **Phones** come from `tel:` links, JSON-LD, and text next to a phone label. Each is validated with Google's libphonenumber.
   - **Socials** come from profile links and JSON-LD `sameAs`. Share buttons and single posts are ignored.
4. **Clean-up**: junk emails are filtered out, and email domains are checked in DNS. Results are ranked so the best contact comes first: own domain, explicit mailto, found on the contact page.
5. **Blocking**: pages are fetched directly with a real-browser TLS fingerprint. A website that answers with a bot check, 403 or 429 is retried through Apify Proxy. You can also enable a residential fallback.

### Pricing

This Actor uses **pay-per-event** pricing. You pay for **`website-contacts`**: one website that returned at least one email, phone number or social profile. The price is per website, not per page or per email. Proxy and compute are included. Websites with no contacts, errors, invalid inputs and robots-disallowed sites are **free**. Check the **Pricing** tab for the current price.

You can cap spending with **Max websites with contacts** or the run's **maximum cost**. The Actor stops cleanly at the limit and doesn't start websites it can't charge for.

### Related tools

Start from company names, then add contacts, tech stack and traffic to every lead.

- [Company Name to Domain](https://apify.com/wrenfield-data/company-name-to-domain): turn a list of company names into official website domains
- [Tech Stack Detector](https://apify.com/wrenfield-data/website-tech-stack-detector): see which CMS, ecommerce, analytics and CRM tools a site uses
- [Domain Authority & Traffic Checker](https://apify.com/wrenfield-data/domain-authority-traffic-checker): bulk domain authority, backlinks and organic traffic
- [SEC Form D Scraper](https://apify.com/wrenfield-data/sec-form-d-scraper): startups that just raised money, from SEC EDGAR

All of these are maintained by the same developer and export flat JSON, CSV or Excel, so you can feed one tool's dataset into the next.

### FAQ

**Do I need a login, cookies or an API key?**
No. The Actor only reads public company websites.

**Why did a website return no emails?**
Many companies publish only a contact form or a phone number. Others hide the email in an image or load it with JavaScript after the page renders, and this Actor doesn't run a browser. You still get phones, socials, `hasContactForm` and `contactPageUrl` when available. If nothing at all is found, the row is free.

**Does it find personal emails of employees?**
Only if the company publishes them on its own website (for example a team page). It doesn't guess email patterns, query third-party databases, or scrape LinkedIn. Use **Only role-based emails** to keep just info@/sales@/support@-style addresses.

**Can I enrich my Google Maps results?**
Yes. Put the Google Maps run's dataset ID into **Enrich an existing dataset**. The `website` field is read automatically, and each row gets `sourceName` (the place name) and `sourceIndex` so you can join the results back.

**How accurate is the email provider?**
It comes straight from the domain's public MX records. When a security gateway (Proofpoint, Mimecast...) sits in front of the mailbox provider, `emailSecurityGateway` is set and `emailProvider` may be empty.

**Are the emails verified?**
Each email's domain is checked to accept mail (MX record), which removes typos and dead domains. Individual mailboxes are not SMTP-probed. Run a dedicated email verifier before large cold-email sends.

**What about websites behind Cloudflare or other bot protection?**
Requests use a real-browser TLS fingerprint, and blocked requests are retried through Apify Proxy. Sites that only show content after a JavaScript challenge may still fail. They return a free `error` row with `errorType: blocked`.

**Can I use it from an AI agent, n8n, Make or Zapier?**
Yes. Call it through the Apify API, the MCP server or any integration with a single domain and read the first dataset row. Rows are small and flat.

**Is it legal to scrape contact details from websites?**
The Actor collects information that businesses publish on their own public websites, respects robots.txt by default, and doesn't log in. Business contact data can still be personal data under GDPR, CCPA and similar laws, especially named emails like jane.doe@company.com. You are responsible for having a lawful basis for processing it and for following anti-spam laws (CAN-SPAM, PECR, CASL) when you contact people. If you are unsure, consult a lawyer.

**Something didn't work?**
Open an issue on the **Issues** tab with the run ID and the website. We usually fix things within a day.

# Actor input Schema

## `urls` (type: `array`):

Company websites or bare domains, one per line: <code>acme.com</code>, <code>https://www.acme.com</code> or a specific page like <code>acme.com/contact</code>. The Actor always starts at the homepage and then visits the pages most likely to list contacts (Contact, About, Team, Impressum, Locations...). Duplicates are skipped.

## `datasetId` (type: `string`):

ID of an Apify dataset to enrich, e.g. from Google Maps Scraper or Company Name to Domain Finder. The Actor reads each item's website from the field below and adds <code>sourceName</code> / <code>sourceIndex</code> so you can join the results back.

## `datasetUrlField` (type: `string`):

Field holding the website in that dataset. Leave empty to auto-detect (<code>website</code>, <code>url</code>, <code>domain</code>, <code>companyWebsite</code>...).

## `maxPagesPerDomain` (type: `integer`):

Homepage plus the highest-ranked contact/about/team/legal pages. 4-8 finds most contacts; more pages = slower, never more expensive per website.

## `extractPhones` (type: `boolean`):

Phone numbers from <code>tel:</code> links, structured data and text next to a phone label, validated and normalised to E.164 (+14155550132).

## `extractSocials` (type: `boolean`):

LinkedIn, Facebook, Instagram, X/Twitter, YouTube, TikTok, Pinterest, GitHub, Threads, WhatsApp, Telegram, Discord, Yelp and Trustpilot profile links. Share buttons and post links are ignored.

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

2-letter country used to read local phone numbers (without +country code) on sites with a generic TLD like .com. Country TLDs (.de, .co.uk...) and the page language (en-GB) override it.

## `onlyRoleEmails` (type: `boolean`):

Drop personal addresses (jane.doe@...) and free-mail addresses. Useful for GDPR-sensitive outreach.

## `onlyOwnDomainEmails` (type: `boolean`):

Drop addresses on other domains (agencies, parent companies, gmail.com...).

## `checkMx` (type: `boolean`):

DNS check: drops emails whose domain cannot receive mail, and adds the website's email provider (Google Workspace, Microsoft 365, Zoho...) and security gateway (Proofpoint, Mimecast...). No emails are sent.

## `respectRobotsTxt` (type: `boolean`):

Skip pages the website's robots.txt disallows for all crawlers. Websites that disallow everything return a free <code>robots_disallowed</code> row.

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

How many websites are crawled at the same time.

## `maxResults` (type: `integer`):

Stop after this many paid results (websites with at least one contact). Leave empty for no limit. You can also cap spending with the run's maximum cost.

## `proxyMode` (type: `string`):

Most company websites don't block, so pages are fetched directly and only blocked requests are retried through the proxy.

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

Proxy used for websites that block direct requests. Apify datacenter proxy is included in the price.

## `residentialFallback` (type: `boolean`):

Retry still-blocked websites through residential IPs. Slightly higher success on heavily protected sites.

## Actor input object example

```json
{
  "urls": [
    "apify.com",
    "https://www.katzsdelicatessen.com",
    "levainbakery.com"
  ],
  "maxPagesPerDomain": 6,
  "extractPhones": true,
  "extractSocials": true,
  "defaultCountry": "US",
  "onlyRoleEmails": false,
  "onlyOwnDomainEmails": false,
  "checkMx": true,
  "respectRobotsTxt": true,
  "maxConcurrency": 30,
  "proxyMode": "fallback",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "residentialFallback": false
}
```

# Actor output Schema

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

No description

## `leads` (type: `string`):

No description

## `summary` (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 = {
    "urls": [
        "apify.com",
        "https://www.katzsdelicatessen.com",
        "levainbakery.com"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("wrenfield-data/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 = {
    "urls": [
        "apify.com",
        "https://www.katzsdelicatessen.com",
        "levainbakery.com",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("wrenfield-data/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 '{
  "urls": [
    "apify.com",
    "https://www.katzsdelicatessen.com",
    "levainbakery.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call wrenfield-data/website-contact-scraper --silent --output-dataset

```

## MCP server setup

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