# Domain Contact Finder — Emails, Phones & Socials from Websites (`gary_sells/domain-contact-finder`) Actor

Turn a list of company domains into usable contacts. Extracts emails, phone numbers, and social profile links from websites — structured output for CRM, outreach, and agent workflows. Pay only when a contact is found ($0.002 per contact-found).

- **URL**: https://apify.com/gary\_sells/domain-contact-finder.md
- **Developed by:** [Industrial AI](https://apify.com/gary_sells) (community)
- **Categories:** Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 contact founds

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?

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

## Find Company Emails, Phones & Socials from Domains

### When to call this Actor

Use when a human or agent needs **public contact details from company websites**:

- "Get emails from these company websites"
- "Find phone numbers and LinkedIn for these domains"
- "Enrich this domain list with public emails/socials"

**Do not use** for: inbox scraping, CRM exports, LinkedIn Sales Nav, CAPTCHA/login bypass, or people-search APIs.

### Agent quickstart

**Input (prefer):** `{ "domains": ["acme.com", "https://example.com"] }`\
**Also accepted:** `startUrls` (RequestList sources)\
**Output:** one dataset item per domain → `emails`, `externalEmails`, `phones`, `socials`, `status`

**Example prompts → input**

| User says | Pass |
| --- | --- |
| Get emails from acme.com and globex.io | `domains: ["acme.com","globex.io"]` |
| Contacts from https://www.acme.com/about | `domains: ["https://www.acme.com/about"]` |
| First 25 of this list only | `domains: [...], maxDomains: 25` |

**Pricing (PPE):** `domain-processed` per domain; `contact-found` when emails/phones > 0 (socials alone do not bill `contact-found`).

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-orange)](https://apify.com)
[![PPE](https://img.shields.io/badge/Pricing-Pay%20per%20event-blue)](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event)

***

### What this Actor does

Given one or more company **domains** or **website URLs**, the Actor:

1. Normalizes each domain to `https://`
2. Crawls high-signal pages: `/`, `/contact`, `/contact-us`, `/about`, `/about-us`, `/team`, `/impressum`, `/legal`, `/support`
3. Extracts **emails** (including obfuscated `[at]` / `(dot)` / `mailto:` / HTML entities)
4. Extracts **phone numbers** (`tel:` links + patterns)
5. Extracts **social profiles**: LinkedIn, X/Twitter, Facebook, Instagram, YouTube, GitHub, TikTok
6. Filters junk / placeholder emails and splits **same-domain emails** vs **externalEmails**
7. Optionally verifies emails with **format + DNS MX/A** checks (no third-party API)

**Output:** one dataset item per domain.

Uses **CheerioCrawler** (HTTP + HTML parse) instead of a full browser — lower compute cost and faster runs.

***

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `domains` | `string[]` | — | Domains or URLs (`acme.com`, `https://www.acme.com`) |
| `startUrls` | `RequestListSources` | — | Alternative URL list |
| `maxPagesPerDomain` | `integer` | `8` | Contact-ish paths to fetch per domain (1–20) |
| `maxDomains` | `integer` | — | Optional cap on domains processed |
| `includeEmailVerification` | `boolean` | `false` | Format + MX/A DNS verification |
| `proxyConfiguration` | `object` | Apify Proxy on | Recommended for larger batches |

#### Example input

```json
{
  "domains": ["apify.com", "crawlee.dev"],
  "maxPagesPerDomain": 8,
  "includeEmailVerification": false,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

***

### Output

One item per domain:

```json
{
  "domain": "apify.com",
  "urls": ["https://apify.com/", "https://apify.com/contact"],
  "emails": ["hello@apify.com"],
  "externalEmails": [],
  "phones": ["+420123456789"],
  "socials": {
    "linkedin": ["https://www.linkedin.com/company/apifytech"],
    "twitter": ["https://twitter.com/apify"],
    "facebook": [],
    "instagram": [],
    "youtube": [],
    "github": ["https://github.com/apify"],
    "tiktok": []
  },
  "pagesScanned": 6,
  "status": "ok",
  "error": null
}
```

- **`emails`** — addresses on the same domain (or subdomain)
- **`externalEmails`** — other addresses found on the pages (partners, tools, etc.)
- **`status`** — `ok` | `partial` | `failed`

***

### Pricing (Pay-per-event)

Suggested Store pricing (configure in **Apify Console → Actor → Monetization**):

| Event | When charged | Suggested price |
| --- | --- | --- |
| **`contact-found`** (primary) | Domain finished with ≥1 email **or** phone | **~$2.00 / 1,000 events** ($0.002 each) |
| **`domain-processed`** | Every finished domain | ~$0.50 / 1,000 events ($0.0005 each) |

**Do not** enable pay-per-event **+ usage pass-through** — keep PPE-only so agent budgets stay predictable.

Synthetic platform events (e.g. actor start) may appear separately; leave defaults unless you have a reason to change them.

#### Cost example

- 1,000 domains with contacts → ~**$2.00** on `contact-found` (+ small `domain-processed` fee)
- Domains with no email/phone still emit `domain-processed` only

***

### For AI agents / MCP

- **Actor name:** `gary_sells/domain-contact-finder` (ID `cvhnTR9Q5TvW2qWRH`)
- **Prefer input field:** `domains` (string list of domains or URLs)
- **PPE events:** `domain-processed` (every finished domain), `contact-found` (emails or phones found; socials alone do **not** bill)
- **Constraints:** public HTML only — no inbox access, no login/CAPTCHA bypass
- Discoverable via Apify Store / MCP `search-actors` once published with PPE-only + Limited permissions + Standby off (`allowsAgenticUsers`)

### API & agent usage

```bash
curl "https://api.apify.com/v2/acts/USERNAME~domain-contact-finder/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domains":["example.com"],"maxPagesPerDomain":8}'
```

Or use the [Apify client](https://docs.apify.com/api/client/js) / Store “Try for free” / AI agent tool integrations. Charge events map cleanly to tool-call budgets.

Local PPE dry-run:

```bash
ACTOR_TEST_PAY_PER_EVENT=true apify run -p
```

Inspect `storage/datasets/charging_log/` for charged events (local default price $1/event).

***

### Legal & ethics disclaimer

This Actor collects **publicly available information** from websites you specify. It does **not** bypass logins, CAPTCHAs, or access private inboxes.

You are responsible for complying with applicable laws (including GDPR/CCPA where relevant), website terms, and anti-spam rules (e.g. CAN-SPAM). Use contact data only for lawful purposes. The authors provide no warranty and accept no liability for misuse.

***

### How it works (technical)

- **Runtime:** Node.js + [Crawlee](https://crawlee.dev) `CheerioCrawler` + Apify SDK
- **PPE:** `Actor.charge({ eventName: 'domain-processed' | 'contact-found' })`
- **Permissions:** Designed for **limited** Actor permissions (default). No Standby mode.
- **Email decode:** `[at]` / `(at)` / `[dot]` / `mailto:` / HTML entities
- **Junk filters:** noreply, example.com placeholders, asset false-positives, etc.

***

### Local development

```bash
cd domain-contact-finder
npm install
## dry-run on sample domains
npm run dry-run
## or
apify run -p
```

Deploy (after `apify login`):

```bash
apify push
```

Then set PPE prices and keep **Limited permissions** in Apify Console.

***

### Changelog

- **1.0.3** — Agent-findability: richer input descriptions, Store/MCP title & description, README when-to-call + AI/MCP blurb.
- **1.0.2** — README charging\_log path fix; dataset schema `emailVerification`.
- **1.0** — Initial Store-ready release: Cheerio crawl, PPE events, MX optional verify, social extractors.

# Actor input Schema

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

Primary input. Pass bare domains OR full URLs — both work. Examples: "acme.com", "www.acme.com", "https://acme.com/about". Each entry is normalized to https and crawled for public contact pages. Prefer this over startUrls for agent tool calls.

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

Alternative to domains for Apify RequestList-style URL objects. Use only if the caller already has requestListSources. For plain domain strings, use domains instead. May be combined with domains.

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

How many high-signal paths to fetch per domain (/, /contact, /about, /team, …). Lower = cheaper/faster; raise only if contacts are buried. Range 1–20, default 8.

## `maxDomains` (type: `integer`):

Hard cap on domains processed from the input list. Use when the user pastes a long list but only wants the first N. Omit to process all.

## `includeEmailVerification` (type: `boolean`):

If true, attach format + DNS MX/A checks per email (no SMTP send, no third-party verifier). Default false for speed/cost; enable when the agent must drop obviously invalid addresses.

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

Apify Proxy. Keep useApifyProxy true for batches > a few domains to reduce blocks. Agents can leave the default.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "crawlee.dev",
    "example.com"
  ],
  "maxPagesPerDomain": 8,
  "includeEmailVerification": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One dataset item per domain with emails, phones, and socials.

# 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 = {
    "domains": [
        "apify.com",
        "crawlee.dev",
        "example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gary_sells/domain-contact-finder").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 = { "domains": [
        "apify.com",
        "crawlee.dev",
        "example.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("gary_sells/domain-contact-finder").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 '{
  "domains": [
    "apify.com",
    "crawlee.dev",
    "example.com"
  ]
}' |
apify call gary_sells/domain-contact-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gary_sells/domain-contact-finder"
        }
    }
}
```

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/cvhnTR9Q5TvW2qWRH/builds/1dsOeHMzEJIKdRaPD/openapi.json
