# Website Contact Extractor (`mighty_monk/website-contact-extractor`) Actor

Extract contact information (emails, phone numbers, social profiles, addresses) from websites by crawling homepage, contact, about, and footer links. One structured result per domain with CSV and Markdown export.

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

## Pricing

from $3.00 / 1,000 domain 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/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

## Website Contact Extractor

**Extract emails, phone numbers, social profiles, and addresses from one or more websites.**

Website Contact Extractor is an Apify Actor built with **TypeScript** and **Crawlee CheerioCrawler**. Point it at company homepages — it crawls the landing page plus contact/about/footer links, deduplicates contacts per domain, and writes a structured dataset plus CSV and Markdown summaries.

### Features

- **Email extraction** — `mailto:` links, page text regex, schema.org; filters noise (`example.com`, Sentry, Wixpress, noreply, image extensions)
- **Phone extraction** — `tel:` links, international patterns, `itemprop="telephone"`
- **Social profiles** — LinkedIn, Twitter/X, Facebook, Instagram, YouTube, TikTok, GitHub
- **Physical addresses** — schema.org `PostalAddress`, microdata, footer `<address>` blocks
- **Contact page discovery** — follows contact, about, team, support, locations, impressum links
- **Company name** — from `og:site_name`, page title, or JSON-LD Organization
- **Per-domain deduplication** — one dataset item per domain with merged contacts
- **Rate limiting & retries** — configurable concurrency, delay, and request retries
- **Proxy-ready** — optional Apify Proxy configuration
- **Exports** — `CONTACTS.csv` and `CONTACTS.md` in the default key-value store

### Installation

```bash
## Clone / open the Actor directory
cd website-contact-extractor

## Install dependencies
npm install

## Run locally (uses storage/key_value_stores/default/INPUT.json or --input)
apify run
```

On the Apify platform, deploy with `apify push` or use the published Actor from the Store.

### Input

| Field                | Type       | Required | Default   | Description                            |
| -------------------- | ---------- | -------- | --------- | -------------------------------------- |
| `startUrls`          | `object[]` | Yes      | —         | Homepage URLs to extract contacts from |
| `maxPagesPerDomain`  | `integer`  | No       | `10`      | Max pages crawled per domain           |
| `maxConcurrency`     | `integer`  | No       | `5`       | Parallel request limit                 |
| `maxRequestRetries`  | `integer`  | No       | `3`       | Retries for failed requests            |
| `requestDelayMs`     | `integer`  | No       | `200`     | Delay hint for rate limiting           |
| `followContactLinks` | `boolean`  | No       | `true`    | Crawl discovered contact/about links   |
| `includeEmails`      | `boolean`  | No       | `true`    | Extract emails                         |
| `includePhones`      | `boolean`  | No       | `true`    | Extract phones                         |
| `includeSocials`     | `boolean`  | No       | `true`    | Extract social profiles                |
| `includeAddresses`   | `boolean`  | No       | `true`    | Extract physical addresses             |
| `proxyConfiguration` | `object`   | No       | Proxy off | Apify Proxy settings                   |

#### Example input

```json
{
    "startUrls": [
        { "url": "https://www.apify.com" },
        { "url": "https://www.shopify.com" },
        { "url": "https://stripe.com" }
    ],
    "maxPagesPerDomain": 5,
    "maxConcurrency": 5,
    "requestDelayMs": 200,
    "followContactLinks": true,
    "includeEmails": true,
    "includePhones": true,
    "includeSocials": true,
    "includeAddresses": true
}
```

See also: [`examples/input.json`](examples/input.json)

### Output

Each dataset item is one domain:

| Field             | Type             | Description                           |
| ----------------- | ---------------- | ------------------------------------- |
| `domain`          | `string`         | Normalized host without `www`         |
| `url`             | `string`         | Start URL for the domain              |
| `companyName`     | `string \| null` | Detected company/site name            |
| `emails`          | `string[]`       | Deduplicated emails                   |
| `phones`          | `string[]`       | Deduplicated phones                   |
| `socials`         | `object`         | Map of platform → profile URL         |
| `addresses`       | `string[]`       | Physical addresses when found         |
| `contactPageUrls` | `string[]`       | Discovered contact/about URLs         |
| `pagesScraped`    | `integer`        | Pages successfully scraped            |
| `scrapedAt`       | `string`         | ISO timestamp                         |
| `error`           | `string \| null` | Present if the domain failed entirely |

#### Example output

```json
{
    "domain": "apify.com",
    "url": "https://www.apify.com",
    "companyName": "Apify",
    "emails": ["hello@apify.com"],
    "phones": [],
    "socials": {
        "linkedin": "https://www.linkedin.com/company/apifytech",
        "twitter": "https://twitter.com/apify",
        "github": "https://github.com/apify"
    },
    "addresses": [],
    "contactPageUrls": ["https://apify.com/contact"],
    "pagesScraped": 3,
    "scrapedAt": "2026-07-13T12:00:00.000Z"
}
```

#### Key-value store exports

| Key            | Content type       | Description                     |
| -------------- | ------------------ | ------------------------------- |
| `CONTACTS.csv` | `text/csv`         | Flat CSV summary of all domains |
| `CONTACTS.md`  | `text/markdown`    | Human-readable Markdown report  |
| `OUTPUT`       | `application/json` | Run summary stats               |

See also: [`examples/output.json`](examples/output.json)

### How it works

1. **Start URLs** — Each unique domain is queued once (first URL wins as the primary).
2. **Homepage scrape** — Cheerio parses the page for emails, phones, socials, addresses, and company name.
3. **Contact link discovery** — Same-domain links matching contact/about/team/support/locations patterns are enqueued (up to `maxPagesPerDomain`).
4. **Merge & dedupe** — Contacts from all pages are merged per domain.
5. **Export** — One dataset item per domain, plus `CONTACTS.csv` and `CONTACTS.md`.

### Pricing

**$0.003 per domain result** using Apify pay-per-event (`apify-default-dataset-item`). Charged once per domain written to the dataset.

### Run locally

```bash
npm install
npm run build
npm test
apify run -p -i examples/input.json
```

### Limitations

- **Static HTML only** — Uses Cheerio (no browser). Contacts injected purely via client-side JavaScript may be missed.
- **Obfuscated emails** — Cloud-encoded or image-only emails are not decoded.
- **Address detection** — Best with schema.org / microdata; free-form footer text is heuristic.
- **Phone false positives** — Aggressive phone regex can occasionally match non-phone number sequences; noise filters reduce this.
- **Same-domain only** — Contact links on other domains are not followed.
- **Rate limits** — Aggressive settings may trigger site WAF/bot protection; enable Apify Proxy for production.

### FAQ

**How many pages are crawled per site?**\
Up to `maxPagesPerDomain` (default 10): the start URL plus discovered contact/about links.

**Can I extract only emails?**\
Yes. Set `includePhones`, `includeSocials`, and `includeAddresses` to `false`.

**Does it work without a proxy?**\
Yes for small local tests. For bulk production runs, enable `proxyConfiguration.useApifyProxy`.

**Where is the CSV?**\
In the run’s default key-value store under key `CONTACTS.csv`.

**One result or many?**\
One dataset item **per unique domain**, regardless of how many pages were scraped on that domain.

### Changelog

#### 1.0.0

- Initial release
- Email, phone, social, address extraction with CheerioCrawler
- Contact/about link following and per-domain deduplication
- CSV and Markdown exports
- Input flags for selective extraction
- LEAD\_GENERATION + DEVELOPER\_TOOLS categories

### License

Apache-2.0

# Actor input Schema

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

Homepage or landing page URLs to extract contacts from. One result is produced per unique domain.

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

Maximum number of pages to crawl per domain (homepage + contact/about links).

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

Maximum number of pages processed in parallel.

## `maxRequestRetries` (type: `integer`):

How many times to retry failed HTTP requests before giving up.

## `requestDelayMs` (type: `integer`):

Approximate delay between requests used for rate limiting (converted to maxRequestsPerMinute).

## `followContactLinks` (type: `boolean`):

When enabled, discovers and crawls contact, about, team, support, and location pages linked from the homepage.

## `includeEmails` (type: `boolean`):

Extract email addresses from mailto links, page text, and structured data.

## `includePhones` (type: `boolean`):

Extract phone numbers from tel: links, schema.org, and text patterns.

## `includeSocials` (type: `boolean`):

Extract LinkedIn, Twitter/X, Facebook, Instagram, YouTube, TikTok, and GitHub profile URLs.

## `includeAddresses` (type: `boolean`):

Extract physical addresses from schema.org PostalAddress, microdata, and footer blocks when detectable.

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

Optional Apify Proxy settings for production runs.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.apify.com"
    },
    {
      "url": "https://www.shopify.com"
    },
    {
      "url": "https://stripe.com"
    }
  ],
  "maxPagesPerDomain": 10,
  "maxConcurrency": 5,
  "maxRequestRetries": 3,
  "requestDelayMs": 200,
  "followContactLinks": true,
  "includeEmails": true,
  "includePhones": true,
  "includeSocials": true,
  "includeAddresses": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

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

No description

## `markdown` (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://www.apify.com"
        },
        {
            "url": "https://www.shopify.com"
        },
        {
            "url": "https://stripe.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mighty_monk/website-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 = { "startUrls": [
        { "url": "https://www.apify.com" },
        { "url": "https://www.shopify.com" },
        { "url": "https://stripe.com" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("mighty_monk/website-contact-extractor").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://www.apify.com"
    },
    {
      "url": "https://www.shopify.com"
    },
    {
      "url": "https://stripe.com"
    }
  ]
}' |
apify call mighty_monk/website-contact-extractor --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/hjHwTS2ah7FvhuJNI/builds/fRsOtcPKxbDASz7rc/openapi.json
