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

Scrape emails, phone numbers and 16 kinds of social profiles (LinkedIn, Facebook, Instagram, X, YouTube, TikTok...) from any list of websites. Contact and imprint pages first, one row per website with company name, address and contact form. Pay only for websites with contacts.

- **URL**: https://apify.com/eiv/contact-details-scraper.md
- **Developed by:** [Eimantas V](https://apify.com/eiv) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $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.
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

**Contact Details Scraper** finds the **emails, phone numbers and social media profiles** of any list of websites, plus the company name, address, contact page and contact form. Paste domains or URLs, get **one clean row per website**, ready for your CRM or outreach tool. No login, no API key, no browser, from **$3.00 per 1,000 websites**, and you **pay only for websites where contacts were found**.

```
joesstonecrab.com  ->  Joe's Stone Crab · info@joesstonecrab.com · +1 305-673-0365 · instagram.com/joesstonecrab · Miami Beach, FL
dishoom.com        ->  Dishoom · hello@dishoom.com (+5 more) · +44 20 7420 9327 (+12 more) · LinkedIn, Instagram, X, YouTube, TikTok · contact form
```

**Benchmark: 300 business websites taken from Google Maps, read in 3 min 33 s. Contacts on 250 of them; phones on 86 % and emails on 62 % of the websites that loaded.**

### What does Contact Details Scraper do?

It reads each website the way a person looking for contact details would: the home page first, then the contact, imprint, about, team and location pages, in 15 languages, before anything else. Everything found is merged into one row per website, with each email and phone number ranked and checked.

- **Emails** from mailto: links, page text, Cloudflare-protected addresses and spellings like "info \[at] example \[dot] com". Placeholders, image file names and tracking ids are refused. Each address is flagged as a shared mailbox (info@, sales@) or a person's, on the website's own domain or not, free provider (Gmail) or not, and its domain's mail servers are checked.
- **Phone numbers** from tel: and WhatsApp links, the site's structured data and the page text, validated with Google's numbering-plan data and returned in international format (+44 20 7420 9327) with the national format, country and line type (mobile or landline).
- **16 social networks**: LinkedIn, Facebook, Instagram, X (Twitter), YouTube, TikTok, Pinterest, Threads, Snapchat, Telegram, WhatsApp, Discord, Reddit, GitHub, Bluesky and Medium. Share buttons, single posts and theme placeholders are left out.
- **Company details**: name, postal address, country, language, description, logo, contact page and whether the site has a contact form.
- **Only pay for real results.** A website with no email, phone or profile on it, a site that is down, parked or refuses the connection: never charged.
- **Built for lead lists.** Paste thousands of domains, upload a CSV or link a Google Sheet. Run it on a schedule, call it from the API, or connect it to Make, Zapier, n8n, Google Sheets or an AI agent through Apify's MCP server.

### Why it is fast and cheap

Pages are read over plain HTTP, the way a browser downloads them, without starting a browser: one website takes 2-10 seconds and a fraction of a cent of computing. The crawl goes to the pages that hold contact details first, so 10 pages per website find what most sites publish. A page that does not answer is retried from another address automatically.

### What data can you extract from websites?

| Field | Example | What it is |
|---|---|---|
| `companyName` | `Joe's Stone Crab` | The business's name, from its structured data, site name or page title |
| `primaryEmail` | `info@joesstonecrab.com` | The best address for outreach: own domain first, shared inboxes next, no-reply and privacy mailboxes last |
| `primaryPhone` | `+13056730365` | The best phone number, international format |
| `emails` | `["hello@dishoom.com", "reservations@dishoom.com"]` | Every email found, best first |
| `phones` | `["+442074209327", "+441135171712"]` | Every phone number found, international format |
| `phonesUncertain` | `["+493089004333"]` | Valid-looking numbers in the text with no phone label next to them: check before use |
| `linkedIns`, `facebooks`, `instagrams`, `twitters`, `youtubes`, `tiktoks`, ... | `["https://www.instagram.com/joesstonecrab"]` | Profiles on 16 networks, the one shown on the most pages first |
| `contactPageUrl` | `https://www.joesstonecrab.com/contact/` | The website's contact page |
| `hasContactForm`, `contactFormUrl` | `true`, `https://www.dishoom.com/say-hello/` | Whether a page has a message form, and which |
| `address`, `country`, `language` | `11 Washington Ave, 33139, Miami Beach, FL`, `US`, `en-US` | Where the business is |
| `description`, `logoUrl` | | The home page's description and the logo |
| `emailDetails` | `{ "email": "info@...", "type": "role", "onWebsiteDomain": true, "freeProvider": false, "mxFound": true, "foundOn": [...] }` | Per email: type, own domain, free provider, mail servers, pages found on |
| `phoneDetails` | `{ "phone": "+44...", "national": "020 7420 9327", "country": "GB", "lineType": "fixed_line", "source": "link" }` | Per number: national format, country, line type, where it came from |
| `contactsFound`, `pagesScraped`, `scrapedUrls` | `9`, `10` | How many contacts, how many pages read, which pages |

### How to scrape contact details from websites

1. Click **Try for free** and paste your websites into **Websites**, one per line: `example.com`, `https://www.example.com/contact`, or a line copied from a spreadsheet. For a long list, upload a file or link a Google Sheet under **Website list**.
2. Leave **Max pages per website** at 10, or raise it for staff directories with many personal emails.
3. Click **Start**. Expect about 1,000 websites in 12 minutes.
4. Open the **Output** tab: one row per website, or the **Emails** and **Phones** views with one contact per row. Download as CSV, Excel or JSON.

### How much does it cost to scrape contact details?

You pay per website **where at least one email, phone number or social profile was found**, whatever the number of pages read. The price drops on higher Apify plans:

| Apify plan | Per 1,000 websites with contacts |
|---|---|
| Free | $5.00 |
| Starter | $4.00 |
| Scale | $3.50 |
| Business | $3.00 |

- 1,000 websites from a Google Maps search on the Starter plan: about 830 have contacts, so about **$3.30**.
- 10,000 company domains on the Business plan, 8,000 with contacts: **$24.00**.
- Scrapers that charge per page read cost $20-40 per 1,000 websites at 10-20 pages each. Here the price is per website, and only when it has contacts.
- A run also costs **$0.00005** to start. Websites with nothing found, websites that fail and duplicates cost nothing.
- The Free plan's $5 monthly credit covers about **1,000 websites with contacts**.

### Input

```json
{
    "websites": ["joesstonecrab.com", "https://www.dishoom.com/", "info@lilyandloaf.com"],
    "maxPagesPerWebsite": 10,
    "excludeUrlPatterns": ["/blog"]
}
```

| Field | Default | Notes |
|---|---|---|
| Websites | `apify.com` (example) | Domains, URLs, lines with a URL in them, or a business email (its domain is read). Duplicates are skipped |
| Website list | | Apify's URL list: add URLs, upload a text/CSV file, or link a Google Sheet. Takes `startUrls` input from other contact scrapers unchanged |
| Max pages per website | 10 | Contact, imprint and about pages are read first; up to 50 |
| Max link depth | 2 | 0 = the start page only |
| Stay on the website | on | Subdomains included. Link-in-bio pages (Linktree, Beacons...) always lead to the sites they list |
| Stop at the first email | off | Fastest way to get one email per website |
| Only pages matching / Skip pages matching | | Words or regular expressions, e.g. `contact`, `/blog` |
| Country for local phone numbers | auto | e.g. `US`; worked out per website when empty |
| Check email domains | on | MX lookup per email domain, free |
| Only websites with contacts | off | Leaves empty websites out of the dataset (they are never charged either way) |
| Max websites | 100,000 | Cap for the run |
| Proxy | off | Pages load from the Actor's own address and retry through Apify datacenter proxy automatically. Residential proxy is not used |

### Output

Each website is one row. The **Overview** view shows the key columns, **Social profiles** all 16 networks, **Emails** and **Phones** one contact per row, **Company details** the firmographics.

```json
{
    "recordType": "website",
    "input": "dishoom.com",
    "url": "https://www.dishoom.com/",
    "domain": "dishoom.com",
    "companyName": "Dishoom Indian Restaurants",
    "primaryEmail": "hello@dishoom.com",
    "primaryPhone": "+442074209327",
    "emails": ["hello@dishoom.com", "reservations@dishoom.com", "team@dishoom.com"],
    "phones": ["+442074209327", "+441135171712", "+442030067013"],
    "linkedIns": ["https://www.linkedin.com/company/dishoom"],
    "instagrams": ["https://www.instagram.com/dishoom"],
    "twitters": ["https://x.com/dishoom"],
    "youtubes": ["https://www.youtube.com/user/DishoomLondon"],
    "tiktoks": ["https://www.tiktok.com/@dishoom_uk"],
    "contactPageUrl": "https://www.dishoom.com/say-hello/",
    "hasContactForm": true,
    "contactFormUrl": "https://www.dishoom.com/say-hello/",
    "country": "GB",
    "language": "en",
    "emailDetails": [
        { "email": "hello@dishoom.com", "onWebsiteDomain": true, "type": "role", "freeProvider": false, "mxFound": true, "foundOn": ["https://www.dishoom.com/frequently-asked-questions-faq/"] }
    ],
    "phoneDetails": [
        { "phone": "+442074209327", "national": "020 7420 9327", "country": "GB", "lineType": "fixed_line", "source": "link", "foundOn": ["https://www.dishoom.com/say-hello/", "https://www.dishoom.com/locations/"] }
    ],
    "contactsFound": 24,
    "pagesScraped": 10,
    "pagesFailed": 0,
    "scrapedAt": "2026-11-03T12:55:37.007Z"
}
```

A website that cannot be read becomes a row with `"recordType": "error"` and an `errorCode`, never charged:

| `errorCode` | Meaning |
|---|---|
| `NOT_FOUND` | The domain does not exist, or the page and its home page are gone |
| `PARKED_DOMAIN` | The domain is parked, for sale, suspended or shows a server's default page |
| `BLOCKED` | The site refuses automated visitors (strict bot protection) |
| `TIMEOUT` / `NETWORK` / `HTTP_ERROR` | The site is down, too slow or answered with an error |
| `NOT_A_WEBSITE` | A social media profile was given instead of a website |
| `UNSUPPORTED_CONTENT` | The address is a PDF, image or other file, not a web page |

### Tips and limits

- **Google Maps lists**: feed the `website` column of a Google Maps export straight in; tracking parameters are ignored.
- **One email per company, fast**: turn on **Stop at the first email** and set **Max pages per website** to 5.
- **No JavaScript rendering.** Contacts that only appear after scripts run (some single-page apps) can be missed; most sites, including Wix, Squarespace, Shopify and WordPress, publish them in the page itself.
- **Sites behind strict bot protection** (about 2 % of business websites) return `BLOCKED` and are not charged.
- **Contact forms hide emails.** Many small businesses publish only a form; the row then shows the form page, the phone and the profiles.
- **No people search.** It returns what the websites publish, not employee lists from a database. For follower counts and profile details, run the profiles through the scrapers below.
- **Hit your spending limit?** The run stops cleanly at your maximum cost per run and keeps everything it collected. Raise the limit in the run options to read more websites.

### FAQ

#### Is it legal to scrape contact details from websites?

This Actor reads only what websites show publicly, without logging in. Business contact details are generally fine to collect, but results can include personal data (a person's email or phone), which laws such as the GDPR and CAN-SPAM protect. Collect and contact people only with a legitimate purpose, honour opt-outs, and ask a lawyer if you are unsure.

#### Can I use it through the API or with Make, Zapier and n8n?

Yes. Every run can be started through the [Apify API](https://docs.apify.com/api/v2) and returns the dataset as JSON, CSV or Excel. Schedules, webhooks and integrations for Make, Zapier, n8n and Google Sheets are on the **Integrations** tab.

#### Can AI agents use it?

Yes. Agents connected to [Apify's MCP server](https://mcp.apify.com) can find and run this Actor as a tool ("find the contact email of these companies"), with the same per-website pricing.

#### Why did a website return fewer contacts than I can see in my browser?

Either the contacts are on a page beyond your page limit (raise **Max pages per website**), they are drawn by JavaScript after the page loads, or they sit in an image. The `scrapedUrls` field shows which pages were read.

#### Why did I get fewer rows than websites?

Duplicates (two spellings of one site) are merged, values that are not website addresses are skipped with a warning, and the run stops at your maximum cost per run or **Max websites**. The log says which.

### Related scrapers

- [Instagram Profile Scraper](https://apify.com/eiv/instagram-profile-scraper): followers, bio, emails and phones for the Instagram profiles found here.
- [Facebook Pages Scraper](https://apify.com/eiv/facebook-pages-scraper): emails, phones, followers and ratings of the Facebook Pages found here.
- [YouTube Channel & Video Scraper](https://apify.com/eiv/youtube-channel-scraper): subscribers and videos of the YouTube channels found here.
- [Website Tech Stack & Lead Enrichment Scraper](https://apify.com/eiv/website-intelligence-scraper): the technologies, ad pixels and email security of the same domains.

### Support

Missing a contact you can see on the site, or found a website that does not work? Open an issue on the **Issues** tab with the website and the run link, and it will be looked at quickly.

# Actor input Schema

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

One website per line: a domain (example.com), a full URL (https://www.example.com/contact), or a line copied from a spreadsheet with a URL in it. A business email address works too: its domain is read. Duplicates are skipped.

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

The same websites in Apify's URL-list format: add URLs one by one, upload a text or CSV file, or link a Google Sheet shared as "Anyone with the link" (the first column that holds a website is read). Also takes the startUrls of other contact scrapers, so their inputs run unchanged.

## `maxPagesPerWebsite` (type: `integer`):

How many pages to read on each website, contact and imprint pages first. 10 finds what most sites publish; raise it for staff directories with many personal emails. The price is per website, whatever this is set to.

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

How many links away from the start page to go. 0 = the start page only, 1 = pages it links to, 2 (default) = one step further.

## `sameDomain` (type: `boolean`):

Read only pages of the same website (subdomains included). Turn off to also follow links to other websites. Link-in-bio pages (Linktree, Beacons...) always lead to the sites they list.

## `stopWhenEmailFound` (type: `boolean`):

Stop reading a website as soon as an email address is found on it. Fastest way to get one email per website for large lists; phones and profiles on later pages are then missed.

## `includeUrlPatterns` (type: `array`):

Optional. Read only pages whose address contains one of these words or matches one of these regular expressions, e.g. contact, team, /about|kontakt/. The start page is always read.

## `excludeUrlPatterns` (type: `array`):

Optional. Never read pages whose address contains one of these words or matches one of these regular expressions, e.g. blog, /products?/, careers.

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

Optional two-letter country code (US, GB, DE...) used to read numbers written without +country code. Leave empty to work it out per website from its address, domain ending, language and phone links.

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

Look up each email domain's mail servers (MX records) and report mxFound per address. Free and fast; turn off only to skip DNS lookups.

## `onlyWebsitesWithContacts` (type: `boolean`):

Leave out websites where no email, phone or social profile was found. Those rows are never charged either way; this only keeps them out of the dataset.

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

Read at most this many websites from the list. Only websites with contacts are charged; your maximum charge per run in the run options also caps the cost.

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

Off by default: pages load from the Actor's own address, and a refused or stalled page is retried through Apify datacenter proxy automatically. Turn on to send every page through datacenter proxy or your own proxy URLs. Residential proxy is not used.

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

How many websites are read at the same time (each reads up to 3 pages at once). Above about 20 a run only gets busier, not faster.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for one page. A page with no answer after 4 seconds is also requested from a second address, and the first answer wins.

## `maxRetries` (type: `integer`):

Extra attempts for a page that is refused, fails to connect or stalls, each from another address.

## Actor input object example

```json
{
  "websites": [
    "apify.com"
  ],
  "maxPagesPerWebsite": 10,
  "maxDepth": 2,
  "sameDomain": true,
  "stopWhenEmailFound": false,
  "checkEmailDomains": true,
  "onlyWebsitesWithContacts": false,
  "maxItems": 100000,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxConcurrency": 10,
  "requestTimeoutSecs": 15,
  "maxRetries": 2
}
```

# Actor output Schema

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

One row per website (recordType "website"), plus one uncharged row per website that could not be read (recordType "error").

## `emails` (type: `string`):

Every email with its type (shared mailbox or person), own-domain and free-provider flags, mail-server check and the pages it was found on.

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

Totals for the run: websites read, with and without contacts, failures, page requests and why the run stopped.

# 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": [
        "apify.com"
    ]
};

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

# Run the Actor and wait for it to finish
run = client.actor("eiv/contact-details-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": [
    "apify.com"
  ]
}' |
apify call eiv/contact-details-scraper --silent --output-dataset

```

## MCP server setup

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