# Website Contact Extractor: Emails, Phones & Social Links (`ensorcelling_melilot/contact-extractor`) Actor

Find emails, phone numbers and social media links on company websites. List of domains in, one row per site out. Fast HTTP crawling, reads Contact/About/Team pages, respects robots.txt. $3 per 1,000 websites.

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

## Pricing

$3.00 / 1,000 website processeds

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: Emails, Phones & Social Links

Give it a list of company websites. Get back **one row per site** with the emails, phone numbers and social media profile links found on it. No browser, no proxies to set up, no per-page math: you pay **per website**.

It reads the home page and then the pages most likely to hold contact details (Contact, About, Team, Impressum/Imprint, Support), so a typical site costs one fixed price however many pages it reads.

### What you get for each website

| Field | Example |
|---|---|
| `domain` | `acme.com` |
| `status` | `ok`, `blocked-by-robots`, `unreachable`, `http-error`, `invalid-url` |
| `emails` | `["hello@acme.com", "sales@acme.com"]` (addresses on the company's own domain come first) |
| `phones` | `["+442083661177"]` (E.164) |
| `phonesFormatted` | `["+44 20 8366 1177"]` |
| `linkedin`, `facebook`, `twitter`, `instagram`, `youtube`, `tiktok`, `github`, `pinterest` | Profile links that appear on the site |
| `pagesCrawled`, `pages` | How many pages were read, and which |
| `title` | The home page title |
| `error` | Why a site gave nothing, in plain words |

### What it finds, and how

- **Emails** from `mailto:` links, plain text, Cloudflare-protected emails, and simple obfuscations like `name [at] domain [dot] com`. Image file names, tracking IDs and placeholder addresses (`you@example.com`) are dropped.
- **Phone numbers** from `tel:` links and page text. Every number is checked against real numbering plans, so order numbers and dates don't slip in. Set a **default country** if the site writes local numbers without `+`.
- **Social links** only if they are linked from the site. Share buttons and tweet intents are ignored. The social networks themselves are never visited.

### Input

- **Websites**: one per line. `acme.com`, `www.acme.com/about` and `https://acme.com` all work. Duplicate domains are checked once.
- **Max pages per website** (default 5): the home page plus the best contact-looking pages.
- **Find emails / phones / social links**: switch off what you don't need.
- **Default country**: two-letter code for local phone numbers.
- **Respect robots.txt** (default on).
- **Websites at once** (default 10).

### Pricing

**$0.003 per website** ($3 per 1,000). Websites that are not readable are **not charged**: unreachable, HTTP errors, invalid addresses, and sites whose robots.txt says no. A readable site that simply has no contact details on it is charged, since it was crawled. No start fee, no per-page fee.

### Good manners and limits

- **Public pages only.** No logins, no forms, no hidden APIs. The Actor reads the same pages a visitor sees.
- **robots.txt is respected** by default.
- **No JavaScript rendering.** Sites that build their pages only in the browser, or that sit behind bot protection (Cloudflare challenges, CAPTCHAs), will often show no contact details or an `http-error`. The Actor tells you; it doesn't guess. If you need those, use a browser-based scraper.
- **No proxy rotation.** Requests come from Apify datacenter IPs, so some large sites refuse them.
- Emails are what the site publishes. They aren't verified; use an email verifier before sending bulk mail. You are responsible for using the data in line with the privacy and anti-spam laws that apply to you (GDPR, CAN-SPAM and similar).

### Use it from code

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('YOUR_USERNAME/contact-extractor').call({
    websites: ['apify.com', 'basecamp.com'],
    maxPagesPerSite: 5,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

# Actor input Schema

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

The company websites to check, one per line. Plain domains (example.com) or full addresses (https://example.com/contact) both work. Each website gives one result row.

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

How many pages to read on each site. The home page is always read first, then the pages most likely to hold contact details (Contact, About, Team, Impressum). You pay per website, not per page, so a higher number finds more but runs longer.

## `findEmails` (type: `boolean`):

Collect email addresses from links and page text, including simple obfuscations like name \[at] domain \[dot] com.

## `findPhones` (type: `boolean`):

Collect phone numbers and return them in international format. Only numbers that validate against real numbering plans are kept.

## `findSocialLinks` (type: `boolean`):

Collect the company's LinkedIn, Facebook, X/Twitter, Instagram, YouTube, TikTok, GitHub and Pinterest profile links. Only links found on the website are returned. The social sites themselves are never visited.

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

Two-letter country code (US, GB, DE...) used to read phone numbers written without a +country code. Leave empty to keep only numbers that already start with a + or a country code.

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

Skip websites whose robots.txt asks crawlers to stay out. Sites skipped this way are not charged. Turn off only for sites you own or have permission to crawl.

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

How many websites to read in parallel. Raise it for big lists.

## Actor input object example

```json
{
  "websites": [
    "apify.com",
    "stripe.com",
    "basecamp.com"
  ],
  "maxPagesPerSite": 5,
  "findEmails": true,
  "findPhones": true,
  "findSocialLinks": true,
  "respectRobotsTxt": true,
  "concurrency": 10
}
```

# Actor output Schema

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

One row per website: emails, phone numbers and social profile links.

## `allFields` (type: `string`):

Every row with all fields, including the pages that were read.

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

How many websites were read, how many had emails, phones or social links, and how many were unreachable or blocked.

# 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",
        "stripe.com",
        "basecamp.com"
    ]
};

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

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

```

## MCP server setup

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