# Website Contact Finder (`oski/website-contact-finder`) Actor

Turn a list of domains into contacts: emails (even obfuscated), phone numbers, social profiles, and addresses. Crawls the contact and about pages. No API key.

- **URL**: https://apify.com/oski/website-contact-finder.md
- **Developed by:** [Oski](https://apify.com/oski) (community)
- **Categories:** Developer tools, Lead generation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 results

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 Finder

Turn a list of websites into contacts. Give it domains, get back **emails, phone numbers, social profiles, and postal addresses** for each one, pulled from the homepage and the pages that actually hold contact details (contact, about, team, imprint). **No API key, no third-party data source, no setup.**

One clean record per website, ready for a CRM, an outreach list, or enrichment.

### Why this one

Most contact scrapers either wrap a third-party API that breaks when the upstream throttles, or spray a page with a naive email regex and hand you `logo@2x.png`. This one is built for the two things that actually matter: **finding the contact, and being right about it.**

- **Finds obfuscated emails.** `sales [at] acme [dot] co [dot] uk` and `info(at)example(dot)com` are decoded, not missed. `mailto:` links are read directly.
- **Real phone numbers, not noise.** Every number is validated by Google's libphonenumber against the site's country, so dates, prices and order IDs are never returned as phones. Output is clean E.164 (`+441134960000`).
- **Junk filtered out.** Asset filenames (`logo@2x.png`), analytics/telemetry (`sentry`, `wix`), and template placeholders (`your@email.com`) are dropped.
- **Social profiles, de-shared.** LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok, GitHub, Pinterest, one profile per platform, with share/intent links removed.
- **Provenance on every item.** Each email and phone records the exact page it was found on.
- **Never hangs.** A hard page and time budget per site means one slow or hostile website can't stall your run.
- **Honest nulls.** A site with no discoverable contacts is reported as `no_contacts`, not padded with guesses, and you are not charged for it.

### Input

| Field | What it does |
|---|---|
| `domains` | Websites to process: bare domain (`example.com`), `www` host, or full URL. One record per site. |
| `maxPagesPerDomain` | Pages to crawl per site: homepage + the most contact-relevant internal pages (default 5, max 20). |
| `defaultRegion` | Country code (e.g. `GB`, `US`, `DE`) for validating phone numbers with no country code. The site's own ccTLD (`.co.uk`, `.de`) overrides it automatically. |
| `maxSecondsPerDomain` | Hard time stop per site (default 45s). |
| `proxyConfiguration` | Rotates IPs if a site blocks datacenter traffic. Datacenter by default; switch to Residential for tough sites. |

```json
{
  "domains": ["apify.com", "https://www.monzo.com"],
  "maxPagesPerDomain": 5,
  "defaultRegion": "GB"
}
```

### Output

One record per website:

```json
{
  "domain": "monzo.com",
  "input_url": "https://www.monzo.com",
  "final_url": "https://monzo.com/",
  "status": "ok",
  "http_status": 200,
  "company_name": "Monzo Bank",
  "primary_email": "help@monzo.com",
  "emails": ["help@monzo.com", "bereavements@monzo.com"],
  "primary_phone": "+441722580180",
  "phones": ["+441722580180", "+443444111444"],
  "socials": {
    "instagram": "https://instagram.com/monzo",
    "facebook": "https://facebook.com/monzobank",
    "linkedin": "https://linkedin.com/company/monzo-bank"
  },
  "contact_form_url": "https://monzo.com/contact",
  "addresses": ["Broadwalk House, 5 Appold St, London, EC2A 2AG"],
  "emails_detail": [
    {"value": "help@monzo.com", "on_domain": true, "obfuscated": false, "source_url": "https://monzo.com/money-worries"}
  ],
  "phones_detail": [
    {"value": "+441722580180", "source": "text", "source_url": "https://monzo.com/money-worries"}
  ],
  "pages_crawled": ["https://monzo.com/", "https://monzo.com/money-worries"],
  "pages_count": 2,
  "error": null,
  "scraped_at": "2026-07-21T18:30:00+00:00"
}
```

`status` is one of `ok` (contacts found), `no_contacts` (reachable, nothing found), `blocked` (site refused the request), `unreachable` (DNS/connection failed), or `invalid` (not a usable domain).

### Pricing

Pay per event: a tiny charge to start the run, then a per-website charge **only for websites that return at least one contact**. Empty, blocked and unreachable sites are still reported for your records, but you are never charged for them.

### Good to know

- Reads only **public** pages. No login, no private data, polite rate limits and a small page budget per site.
- Sites rendered entirely in JavaScript with no server-side HTML are best-effort; if a homepage ships no readable content, the site is reported honestly rather than guessed at.
- Phone region defaults to `defaultRegion` but is overridden by the site's country domain when it has one.

# Actor input Schema

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

Websites to find contacts for. Accepts a bare domain (example.com), a www host (www.example.com), or a full URL (https://example.com/about). One record is returned per website.

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

How many pages to crawl per website: the homepage plus the most contact-relevant internal pages (contact, about, team, imprint). Higher finds more but costs more compute. You are only charged per website that returns a contact, never per page.

## `defaultRegion` (type: `string`):

Two-letter country code (ISO 3166, e.g. US, GB, DE) used to validate phone numbers written without a country code. The site's own country domain (.co.uk, .de) overrides this automatically.

## `maxSecondsPerDomain` (type: `integer`):

Hard stop per website so one slow site can never hang the run.

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

Rotates IPs if a site blocks datacenter traffic. Datacenter is used by default (cheap and works on most business sites); switch to Residential for the hostile few.

## Actor input object example

```json
{
  "domains": [
    "apify.com",
    "https://www.stripe.com"
  ],
  "maxPagesPerDomain": 5,
  "defaultRegion": "GB",
  "maxSecondsPerDomain": 45,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("oski/website-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"] }

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

```

## MCP server setup

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

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/EydawCMLv3W3qiOzd/builds/YOIeqZz5LLM6Y4nsi/openapi.json
