# Contact Details Scraper - Emails, Phones & Socials (`solidscrape/contact-details-scraper`) Actor

Crawl any website or domain and extract clean business contact details: emails (including Cloudflare-protected and \[at]/\[dot]-obfuscated), validated phone numbers (E.164), and social profiles. High recall, low junk. Export JSON/CSV/Excel.

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

## Pricing

from $1.25 / 1,000 results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Contact Details Scraper – Email, Phone & Social Media Scraper

A fast **contact details scraper** that crawls any list of **websites or domains** and extracts clean **business contact details**: **email addresses**, **validated phone numbers**, and **social-media profiles** — one tidy row per domain, ready to **export to JSON, CSV, or Excel**.

Built for **lead generation, sales prospecting, recruiting, market research, and CRM enrichment**. Use it as a no-code **contact scraper** in the Apify Console, or call it programmatically through the **Apify API** — **no target-site API key required**, because it reads the public web directly. It runs purely over HTTP (no headless browser) so it's **fast and cheap**, with extraction logic tuned for **high recall and low junk**.

> Pairs perfectly with the [Google Maps Scraper](https://apify.com/compass/crawler-google-places) for end-to-end local lead generation: pull business websites from Google Maps, then feed them here to harvest emails, phones, and socials.

### What it does

- 📧 **Emails** – from `mailto:` links, page text, **Cloudflare-protected** elements (`data-cfemail` / `/cdn-cgi/l/email-protection`), HTML-entity-encoded addresses, and **`[at]` / `[dot]`-obfuscated** text.
- 📞 **Phones** – from `tel:` links and page text, **validated with Google's libphonenumber**. Invalid numbers are dropped, so you don't get dates, prices, or zip codes masquerading as phones. Output keeps both **E.164** and the **raw** on-page format.
- 🔗 **Socials** – LinkedIn, Twitter/X, Facebook, Instagram, TikTok, and YouTube profile URLs.
- 🎯 **Smart crawl** – fetches each homepage, then **prioritizes contact/about/imprint/kontakt/team/legal pages** before anything else, within your page and depth caps.
- 🧹 **Junk filter** – removes asset filenames (`logo@2x.png`), tracking/CDN/Sentry/Wix addresses, `noreply@`, and `example.com` placeholders.
- 🧱 **One clean row per domain** – emails and phones de-duplicated across all crawled pages.

### Why this scraper

Most contact scrapers miss the addresses that matter most and pad your output with noise. This Actor specifically fixes the common complaints:

| Common pain point | How this Actor handles it |
|---|---|
| Cloudflare-obfuscated emails come back as `[email protected]` | **Decodes `data-cfemail` / email-protection** XOR payloads natively — no browser needed. |
| `[at]` / `[dot]` / entity-encoded emails are missed | **De-obfuscated** before matching. |
| Phone numbers are full of false positives | **Validated with libphonenumber**; invalid numbers are discarded. |
| Output is full of `noreply@`, asset files, `example.com` | **Aggressive junk filter** drops them. |
| German/EU sites hide contacts on **Impressum/Kontakt** | Those paths are **first-priority** in the crawl. |

> **Honest limitation:** this is an **HTTP-first** scraper (no JavaScript rendering in v1). Contact details that are injected **only by client-side JavaScript** after page load (e.g. a phone number rendered by a React widget with nothing in the HTML) may not be captured. Emails hidden behind Cloudflare's static obfuscation **are** captured, because that data is present in the HTML.

### Example input

```json
{
  "startUrls": [{ "url": "https://apify.com" }],
  "domains": ["stripe.com", "vercel.com"],
  "maxPagesPerDomain": 10,
  "maxDepth": 2,
  "sameDomainOnly": true,
  "extractEmails": true,
  "extractPhones": true,
  "extractSocials": true,
  "defaultPhoneCountry": "US",
  "proxyConfiguration": { "useApifyProxy": true }
}
```

### Example output

One item per input domain:

```json
{
  "type": "contact",
  "domain": "example.com",
  "url": "https://example.com/contact",
  "emails": ["hello@example.com", "sales@example.com"],
  "phones": [
    { "e164": "+14155552671", "raw": "(415) 555-2671" }
  ],
  "socials": {
    "linkedin": "https://www.linkedin.com/company/example",
    "twitter": "https://twitter.com/example",
    "facebook": "https://www.facebook.com/example",
    "instagram": "https://www.instagram.com/example",
    "tiktok": null,
    "youtube": "https://www.youtube.com/@example"
  },
  "pagesScraped": 6,
  "scrapedAt": "2026-06-26T10:00:00.000Z"
}
```

### Output fields

Every item has `type: "contact"`.

| Field | Type | Description |
|---|---|---|
| `type` | string | Always `"contact"`. |
| `domain` | string | The registrable domain crawled (leading `www.` stripped). |
| `url` | string | The most relevant source page (first contact/about page found, else the homepage). |
| `emails` | string\[] | De-duplicated, junk-filtered, lower-cased email addresses found across all crawled pages, sorted alphabetically. Empty `[]` if `extractEmails` is off. |
| `phones` | object\[] | Validated phone numbers, each `{ "e164": "+1...", "raw": "as shown on page" }`. Empty `[]` if `extractPhones` is off. |
| `socials` | object | Profile URLs keyed by network: `linkedin`, `twitter`, `facebook`, `instagram`, `tiktok`, `youtube` (each a URL or `null`). All `null` if `extractSocials` is off. |
| `pagesScraped` | integer | How many pages were successfully fetched and parsed for this domain. |
| `scrapedAt` | string | ISO 8601 timestamp of when the item was produced. |

Domains that could not be reached still produce a row (with empty results), so your output always has one item per input. Download the full dataset as **JSON, CSV, Excel, or HTML** from the Apify Console, or pull it via the **Apify API**.

### Input reference

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array | — | Website URLs to crawl (full URLs or homepages). |
| `domains` | array | — | Bare domains to crawl (e.g. `stripe.com`); merged with `startUrls`. |
| `maxPagesPerDomain` | integer | `10` | Max pages to fetch per domain (contact pages first). |
| `maxDepth` | integer | `2` | Link hops from the homepage (`0` = homepage only). |
| `sameDomainOnly` | boolean | `true` | Only follow links on the input domain and its subdomains. |
| `extractEmails` | boolean | `true` | Extract email addresses (incl. Cloudflare + obfuscated). |
| `extractPhones` | boolean | `true` | Extract and validate phone numbers. |
| `extractSocials` | boolean | `true` | Extract social-profile URLs. |
| `defaultPhoneCountry` | string | — | ISO-2 country (e.g. `US`) for validating national-format numbers. Blank = only `+CC` numbers. |
| `proxyConfiguration` | object | Apify proxy | Proxy settings. Datacenter is default; use residential for protected sites. |

### Common use cases

- **Lead generation & sales prospecting** – turn a list of company domains into emails, phones, and socials.
- **CRM / dataset enrichment** – fill missing contact fields for accounts you already have.
- **Recruiting & outreach** – find company contact points at scale.
- **Market research** – build directories of businesses in a niche.

### FAQ

**Does this contact scraper need an API key?** No. There is no target-site API key to obtain — the Actor crawls public web pages directly through the Apify proxy. The only credential involved is your own Apify token if you call the Actor through the **Apify API**.

**Can I run it from the API instead of the UI?** Yes. Start runs and fetch results programmatically via the [Apify API](https://docs.apify.com/api/v2) or the Apify client libraries (JavaScript/Python). The input is the same JSON shown above, and the dataset can be exported as JSON, CSV, or Excel.

**How does it decode Cloudflare-protected emails?** Cloudflare's email obfuscation is a reversible single-byte XOR cipher embedded in the page (`data-cfemail` / `email-protection#<hex>`). The first byte is the key; each following byte XORed with the key is one character of the address. The Actor decodes this directly from the HTML — no browser required.

**Why are some phone numbers missing?** Numbers are validated with libphonenumber and **dropped if invalid**. National-format numbers without a country code can only be validated if you set `defaultPhoneCountry`. This is intentional — it trades a little recall for far fewer false positives.

**Will it find JavaScript-rendered contacts?** Not in v1. This is an HTTP-only scraper for speed and cost. Statically present data (including Cloudflare-obfuscated emails) is captured; data injected only by client-side JavaScript may be missed.

**Which proxy should I use?** The default datacenter proxy works for most sites. For Cloudflare-challenged or geo-restricted sites, enable residential proxies in `proxyConfiguration`.

**How many results do I get?** Exactly one aggregated item per input domain, with emails/phones de-duplicated across every page crawled for that domain.

**Which social networks are detected?** LinkedIn, Twitter/X, Facebook, Instagram, TikTok, and YouTube profile URLs. Share, intent, and login endpoints are skipped so you only get real profile links.

**Can I limit cost on large lists?** Yes. Lower `maxPagesPerDomain` (e.g. `5`) and `maxDepth` (e.g. `1`) — contact, about, and imprint pages are crawled first, so even small caps usually find what matters while keeping runs fast and cheap.

### Responsible use & GDPR

This Actor collects contact information (including personal data such as names within email addresses) from publicly available web pages. You are responsible for using the output lawfully. In particular:

- Have a **valid legal basis** (e.g. legitimate interest) before processing personal data, and honour data-subject rights such as access and erasure under the **GDPR**, **CCPA**, and similar laws.
- Respect **marketing/opt-out rules** (GDPR, ePrivacy, CAN-SPAM, PECR) before sending outreach.
- Only scrape sites you are **permitted** to access, and follow each website's terms.

The Actor itself extracts only what is already published publicly and applies a junk filter (dropping `noreply@`, placeholders, and tracking addresses); it does not bypass logins or paywalls.

# Actor input Schema

## `startUrls` (type: `array`):

Website URLs to crawl for contact details. You can paste full URLs (e.g. https://example.com/contact) or homepage URLs — the scraper automatically discovers and prioritizes contact, about, and imprint pages.

## `domains` (type: `array`):

Bare domains to crawl, e.g. "stripe.com". Equivalent to entering https://stripe.com under Start URLs — handy for pasting a plain list.

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

How many pages to crawl per domain. The homepage plus the highest-priority contact/about/imprint pages are visited first, so a small value (5–10) usually finds everything.

## `maxDepth` (type: `integer`):

How many link hops from the homepage to follow. 0 = homepage only, 1 = homepage + linked pages, 2 = two hops (recommended). Contact-style links are always followed first.

## `sameDomainOnly` (type: `boolean`):

Only follow links that stay on the input domain (and its subdomains). Strongly recommended to avoid wandering off to third-party sites.

## `extractEmails` (type: `boolean`):

Find email addresses from mailto: links, page text, Cloudflare-protected (data-cfemail) elements, and \[at]/\[dot]-obfuscated text. Junk addresses (asset filenames, noreply, example.com, tracking) are filtered out.

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

Find phone numbers from tel: links and page text, then validate each with Google's libphonenumber so invalid/false-positive numbers are dropped. Output includes both E.164 and the raw on-page format.

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

Collect LinkedIn, Twitter/X, Facebook, Instagram, TikTok, and YouTube profile URLs found in page links.

## `defaultPhoneCountry` (type: `string`):

Optional two-letter country code (e.g. "US", "GB", "DE") used to validate national-format phone numbers that have no country code. Leave blank to only accept internationally-formatted (+CC) numbers — the safest, lowest-false-positive option.

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

Most sites are reachable on the default datacenter Apify proxy. For Cloudflare-protected or geo-restricted sites, switch on residential proxies.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://apify.com"
    }
  ],
  "domains": [
    "stripe.com",
    "vercel.com"
  ],
  "maxPagesPerDomain": 10,
  "maxDepth": 2,
  "sameDomainOnly": true,
  "extractEmails": true,
  "extractPhones": true,
  "extractSocials": true,
  "defaultPhoneCountry": "US",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `contacts` (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 = {
    "startUrls": [
        {
            "url": "https://apify.com"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidscrape/contact-details-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 = {
    "startUrls": [{ "url": "https://apify.com" }],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("solidscrape/contact-details-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://apify.com"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call solidscrape/contact-details-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=solidscrape/contact-details-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/QnWnq6V7jPAkZqHR9/builds/uTcBNySeqJhf5panq/openapi.json
