# Bulk Website Contact Extractor — Emails & Phones API (`nexgensignal/bulk-website-contact-extractor`) Actor

Bulk contact extractor: feed website URLs or domains and get one clean record per site with its public business emails (role and personal), phones and social links, plus a DNS mail-server check. Keyless, residential-proxy hardened. B2B use; honour outreach/privacy law.

- **URL**: https://apify.com/nexgensignal/bulk-website-contact-extractor.md
- **Developed by:** [NexGen Signal](https://apify.com/nexgensignal) (community)
- **Categories:** Business, Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $26.80 / 1,000 contact records

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?

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

## Bulk Website Contact Extractor — Emails & Phones API

**Pay per site. Feed a list of company websites and get back their public business contact details — emails, phones, and social links — as clean JSON, hardened for bulk runs.**

Give it one URL or ten thousand and, for each, it fetches the site (and its contact/about pages), pulls out every published business email, phone number, and social profile, checks whether the domain can actually receive mail, and returns one tidy record per site. It is a keyless, code-friendly contact extractor built for B2B prospecting at scale.

### What this is, in one paragraph

Company websites publish the contact details a sales team needs — a `sales@`, a switchboard number, a LinkedIn page — but they're scattered across landing pages, contact pages, and footers. This actor gathers them for you: for each URL it reads the page and a few likely contact/about/imprint pages, extracts business emails (including simple obfuscated `name [at] domain [dot] com` forms), phone numbers, and social links, classifies emails into role-based (`info@`, `sales@`) versus named-person business addresses, and confirms via a DNS lookup whether the domain can receive mail. The bulk path is hardened: requests run over a residential proxy with automatic session re-rolls so a transient block on one site doesn't sink the run. The output is a B2B prospecting dataset assembled entirely from publicly published sources.

### Who buys this and for what job

- **Sales and RevOps teams** turning a list of target-company domains into reachable contact records.
- **Lead-generation and agency teams** enriching prospect lists with a verified point of contact per account.
- **Data teams** building a company-contact table keyed by domain.
- **Builders and agents** who need a keyless contact-extraction endpoint they can call over a big URL list.

The job it does: take the websites you already care about and return the business contact details published on them — at scale, with mail-server verification, and without an account or key.

### Pricing

Pay per event. One event: a contact record delivered (one per URL processed). Tiered by your Apify plan; the record is delivered before it is charged. No subscription. A URL that yields no contacts still returns a record and counts as one processed site, exactly as a contact API would meter a lookup.

| Event | Free plan | Bronze | Silver | Gold / Platinum / Diamond |
|-------|-----------|--------|--------|---------------------------|
| Contact record | $0.04 | $0.036 | $0.032 | $0.0268 |

About 25 sites per dollar at the base rate.

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `urls` | array | *(required)* | Website URLs or domains to extract from. Aliases `domains`, `websites`, `startUrls`, `targetUrls`, `siteUrls`, `url` are all accepted. |
| `crawl_contact_pages` | boolean | true | Also follow likely contact / about / imprint pages for more complete data. |
| `max_pages_per_site` | integer | 3 | Maximum pages fetched per site (landing page plus contact/about pages). |
| `verifyMx` | boolean | true | Check via DNS whether each domain can receive mail — a DNS lookup only, never a mailbox probe. |
| `request_timeout_secs` | integer | 20 | Per-request timeout. |
| `proxyConfiguration` | object | Residential | Proxy for site requests; residential is recommended for reliable bulk extraction. |

### Output

One JSON object per URL. Real example:

```json
{
  "record_id": "apify.com",
  "source_url": "https://apify.com",
  "domain": "apify.com",
  "page_title": "Apify",
  "emails": ["hello@apify.com", "support@apify.com"],
  "role_emails": ["hello@apify.com", "support@apify.com"],
  "personal_emails": [],
  "phones": [],
  "social_links": ["https://github.com/apify", "https://linkedin.com/company/apify"],
  "email_count": 2,
  "phone_count": 0,
  "pages_crawled": 3,
  "has_mx": true,
  "mx_provider": "Google Workspace",
  "success": true,
  "error": null,
  "observed_at": "2026-08-25T00:00:00Z"
}
```

An unbilled `RUN_RECEIPT` records how many URLs went in, how many records were delivered, and how many yielded at least one contact.

### Field reference

Each record carries `record_id` and `domain`, the `source_url`, the `page_title`, and the extracted contact data: `emails` (all of them), split into `role_emails` and `personal_emails`, plus `phones` and `social_links`. `email_count` and `phone_count` are convenience counts, `pages_crawled` says how deep it went, and `has_mx` / `mx_provider` report the domain's mail capability from a DNS check. `success` and `error` let your pipeline branch. Provenance travels on every record: `source`, `licence`, `attribution`, `disclaimer`, and `observed_at`.

### How a run works

For each URL the actor fetches the landing page over a residential proxy, and — if crawling is on — a few likely contact/about/imprint pages on the same domain. It extracts emails (dropping asset filenames and placeholder addresses), phones, and social links across those pages, de-duplicates them, classifies each email, and optionally confirms the domain's mail server via a DNS MX lookup. Every URL produces exactly one delivered, billed record, whether or not contacts were found, so your cost is predictable and every input is accounted for. Transient blocks are retried with a fresh residential session before the URL is marked unreachable.

### Bulk hardening

This actor is built for large URL lists. Each fetch is retried up to three times, each retry on a fresh residential proxy session, so an intermittent block or timeout on one site is transparently re-rolled rather than lost. Contact-page crawling is bounded by `max_pages_per_site` so cost and time stay predictable across a big batch, and every site resolves to exactly one billed record — number of URLs in equals number of records out equals number of events billed.

### Use with AI agents

Point Claude, an OpenAI Agents SDK tool, an n8n or Make flow, or any MCP-aware client at this actor and hand it a list of company URLs. Because the output is flat JSON keyed by `domain`, an agent can enrich an account list with a point of contact per company in one pass and route the misses to a human. A typical prompt: *pull the business contact emails and phone for each of these company websites.*

### Why one record per site

Keeping the output at one record per URL — rather than one row per email — is what makes this actor easy to join and easy to budget. Every site you feed in comes back as exactly one row keyed by its domain, with its emails, phones, and social links held together in that row, so the result slots straight into an account list without a second grouping step. It also makes cost trivial to predict: the number of URLs you submit is the number of records you get back and the number of events you are billed, full stop. When you want the emails broken out individually — for example to load them into a sequencing tool — a single flatten step on the `emails` array does it, while the account-level view stays intact for everything else.

### Lawful use and limitations

- **Business contacts, public sources.** Every field comes from the public web pages at the URLs you supply. This is a B2B prospecting dataset for contacting businesses and their published business contacts — not a tool for compiling data on private individuals.
- **Use it lawfully.** Respect applicable outreach and data-protection law (for example GDPR legitimate-interest and CAN-SPAM requirements) and honour opt-outs and unsubscribe requests.
- **MX is a DNS check.** Mail-server verification is a DNS lookup that confirms a domain *can* receive mail; it is never an SMTP mailbox probe and does not confirm a specific address is active.
- **Coverage varies.** Some sites publish no contact details, obfuscate them, or load them via scripts; those return an empty but honest record. No data beyond published business contact details is collected.

***

*Business contact details extracted from the public web pages at the supplied URLs, for B2B outreach. Intended for contacting businesses and their published business contacts, not private individuals. Use in line with applicable outreach and data-protection law, and honour opt-outs.*

# Actor input Schema

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

Website URLs or domains to extract business contact details from. One record is returned per URL.

## `crawl_contact_pages` (type: `boolean`):

Follow likely contact / about / imprint pages on each site for more complete contact data.

## `max_pages_per_site` (type: `integer`):

Maximum pages fetched per site (the landing page plus contact/about pages).

## `verifyMx` (type: `boolean`):

Check via DNS whether each domain can receive mail (a DNS lookup only, never a mailbox probe).

## `request_timeout_secs` (type: `integer`):

Per-request timeout.

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

Proxy for site requests. Residential is recommended for reliable bulk extraction.

## Actor input object example

```json
{
  "urls": [
    "stripe.com",
    "apify.com"
  ],
  "crawl_contact_pages": true,
  "max_pages_per_site": 3,
  "verifyMx": true,
  "request_timeout_secs": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Delivered records.

# 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": [
        "stripe.com",
        "apify.com"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nexgensignal/bulk-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 = {
    "urls": [
        "stripe.com",
        "apify.com",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("nexgensignal/bulk-website-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 '{
  "urls": [
    "stripe.com",
    "apify.com"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call nexgensignal/bulk-website-contact-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nexgensignal/bulk-website-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/cHFigqdFlSWT3qFTM/builds/IycHyZFjf3PKdSPqA/openapi.json
