# Lead List Enricher: Website Emails, Phones & Socials (`rationalistic_canoe/website-contact-finder`) Actor

Enrich a lead list or any dataset with website contacts: ranked emails with mail-server check, validated phones, 15 social networks, contact form, site platform and AI-crawler flags. One row per company. Pay only for companies where contacts are found.

- **URL**: https://apify.com/rationalistic_canoe/website-contact-finder.md
- **Developed by:** [VISHAL RAWAT](https://apify.com/rationalistic_canoe) (community)
- **Categories:** Lead generation, Automation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 company with 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/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

## Lead List Enricher: website emails, phones and socials, one row per company

Give it a list of websites, or the ID of a dataset you already have (a Google Maps, directory or trade-show scrape). You get back one clean row per company with the best email, every email we found (ranked, with a mail-server check), validated phone numbers in international format, social profiles on 15 networks, the street address and opening hours when the site publishes them. New in 1.1: the contact-form URL, the site platform (WordPress, Shopify, Wix, Webflow and more) and which AI crawlers the site blocks in robots.txt.

**Try it free:** Apify's free plan gives $5/month of usage, no card needed. That's about 1,250 companies with contacts a month at $4 per 1,000.

**$4 per 1,000 companies with contacts. Sites where nothing is found cost nothing.** Unreachable, timed-out, invalid and duplicate sites are free too. No add-ons, no per-page fees, no surprise charges.

### Why people switch to it

- **Pay for results, not pages.** You're charged only when a company row has at least one email, phone number or social profile. Other contact scrapers on the Store charge per page crawled, whether or not anything is found.

- **Enrich the list you already have.** Point it at a dataset ID: it finds the website column itself (or use `websiteField`), enriches every row and copies the fields you choose into `source`, so results match back to your list.

- **Timeouts that can't stall a run.** Every page has a time limit, every site has a time budget, and the run stops starting new sites before its own timeout. Sites it didn't reach are listed in `OUTPUT.notProcessedSites` and cost nothing.

- **One row per company, not one row per page.** Duplicate domains in your list are merged before the run starts, and emails and phones are deduplicated across every page we visit. Paste the row straight into your CRM.

- **The best email is picked for you.** Emails on the company's own domain come first, then ones found on the Contact or Impressum page. Addresses like `noreply@`, `privacy@` or the web agency's email sink to the bottom. Each email is tagged `role` (info@, sales@) or `personal` (priya@).

- **Mail-server check included.** Every email's domain is checked for an MX record, free. Domains that can't receive mail are dropped (or flagged, if you prefer).

- **Finds what others miss.** Cloudflare-protected emails, `name [at] domain [dot] com` style addresses, schema.org business data, `tel:` and WhatsApp links.

- **Clean phone numbers.** Parsed and validated with Google's libphonenumber rules, returned as `+91 98765 43210` and E.164. Set a default country to also catch local-format numbers.

- **Fast and light.** Plain HTTP, no browser, so 1,000 sites finish in minutes. It goes straight to the pages that matter (Contact, About, Team, Impressum, footer links) instead of crawling the whole site.

- **Polite by default.** Respects robots.txt and visits a handful of pages per site.

### Use cases

- **Sales prospecting:** turn a list of target company websites into emails and phone numbers you can load into your CRM.
- **Enriching scraped lists:** add emails and socials to the website column from Google Maps, directory or trade-show exports.
- **Agencies and freelancers:** build verified lead lists for clients in minutes instead of opening hundreds of tabs.
- **Recruiters and partnerships:** reach the right inbox at companies that are hiring or launching.
- **Market research:** see which companies in a niche are active on LinkedIn, X, Instagram or TikTok.
- **Data hygiene:** re-check an old list and drop emails whose domain can no longer receive mail.

### Input

| Field | What it does | Default |
|---|---|---|
| `websites` | Domains or URLs, one per line | either this or `datasetId` |
| `datasetId` | Enrich rows of an existing Apify dataset | empty |
| `websiteField` | Column holding the website (auto-detects `website`, `url`, `domain`, `companyWebsite`, `homepage`) | auto |
| `keepFields` | Source fields copied into each row's `source` (empty = all text and number fields) | all |
| `maxPagesPerSite` | Homepage plus the likeliest contact pages | 6 |
| `defaultCountry` | e.g. `US`, `GB`, `IN`: also catch local-format phone numbers | empty |
| `verifyEmailDomains` | MX check for every email domain | on |
| `dropUndeliverable` | Remove emails whose domain can't receive mail | on |
| `onlySameDomainEmails` | Hide third-party addresses | off |
| `stopWhenFound` | Stop a site early once contact page, email and phone are found | on |
| `respectRobotsTxt` | Skip pages disallowed by robots.txt | on |
| `includeEmptyRows` | Keep a free row for sites with no contacts, with the reason | on |
| `pageTimeoutSecs` / `siteTimeoutSecs` | Time limit per page / time budget per site | 10 / 45 |

```json
{ "websites": ["plausible.io", "fsf.org", "https://www.nobroker.in"], "defaultCountry": "IN" }
```

Enrich a dataset from another actor:

```json
{ "datasetId": "<your dataset ID>", "keepFields": ["title", "phone", "address"] }
```

### Output

Real row from a test run on 31 Oct 2026 (trimmed):

```json
{
  "companyName": "Free Software Foundation",
  "domain": "fsf.org",
  "url": "https://www.fsf.org/",
  "bestEmail": "info@fsf.org",
  "bestPhone": "+1 617 542 5942",
  "emails": [
    { "email": "info@fsf.org", "type": "role", "sameDomain": true, "mx": true, "foundOn": ["https://www.fsf.org/about/contact/"] },
    { "email": "campaigns@fsf.org", "type": "role", "sameDomain": true, "mx": true, "foundOn": ["https://www.fsf.org/"] }
  ],
  "phones": [
    { "phone": "+1 617 542 5942", "e164": "+16175425942", "country": "US", "type": "FIXED_LINE_OR_MOBILE", "source": "schema.org" }
  ],
  "socials": { "bluesky": ["https://bsky.app/profile/fsf.org"] },
  "address": "31 Milk Street # 960789, Boston, MA, 02196, US",
  "contactPageUrl": "https://www.fsf.org/about/contact/",
  "contactFormUrl": null,
  "sitePlatform": null,
  "aiCrawlersBlocked": [],
  "status": "ok",
  "pagesCrawled": 5
}
```

Social networks: LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok, GitHub, Pinterest, Threads, Telegram, WhatsApp, Discord, Bluesky, Medium, Crunchbase.

Extra fields in 1.1: `contactFormUrl` (first page with a contact form or a known form embed such as HubSpot, Typeform or Contact Form 7), `sitePlatform` and `sitePlatforms` (from the homepage HTML), `aiCrawlersBlocked` (GPTBot, ClaudeBot, PerplexityBot, Google-Extended and others fully disallowed in robots.txt; `null` if robots.txt couldn't be read), and `source` (your fields, when you enrich a dataset).

Real checks on 31 Oct 2026: allbirds.com returned `sitePlatform: "shopify"` and its `/pages/contact-us` form; wordpress.org returned `"wordpress"`.

`status` is `ok`, `no-contacts-found`, `unreachable`, `timed-out` or `invalid-input`. Only `ok` rows are charged.

### Pricing

Pay per event:

- **Company with contacts:** $0.004 (one row with at least one email, phone or social profile)
- Sites with no contacts, unreachable sites and duplicates: **free**

A list of 1,000 websites where 700 have contacts costs $2.80. The other 300 cost nothing. Set a max charge on the run and the actor stops cleanly when it's reached.

### Good to know

- It reads public pages only. No logins, no paywalls, no CAPTCHAs.
- Sites that build their contact page entirely in JavaScript may show fewer results. Most business sites render contacts in plain HTML.
- The MX check confirms the domain accepts mail. It does not send anything to the mailbox.

### Compliance

- **Public business contact data only.** It reads pages a company publishes on its own website. It never logs in, never gets past paywalls or CAPTCHAs, and never reads private profiles.
- **Respects robots.txt** by default and visits only a handful of pages per site.
- **You're responsible for how you use the data.** If you contact people in the EU, UK, US, India or anywhere else with privacy and anti-spam laws (GDPR, PECR, CAN-SPAM, India's DPDP Act), make sure you have a lawful basis, identify yourself and offer an easy opt-out. Business role addresses (info@, sales@) are the safest place to start.
- The mail-server check only confirms that a domain accepts email. Nothing is ever sent.

### Works well with

- Google Maps or directory scrapers: feed their website column in here to add emails and socials.
- Our [ATS Jobs Feed](https://apify.com/rationalistic_canoe/ats-jobs-feed): find companies that are hiring, then get their contact details.
- n8n, Make and Zapier via the Apify integration, or the Apify API for scheduled enrichment.

Questions or a site it misses? Open an issue on the actor's Issues tab and we'll look at it.

# Actor input Schema

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

Domains or URLs, one per line (e.g. stripe.com). Or leave empty and enrich a dataset below.

## `datasetId` (type: `string`):

ID of an Apify dataset to enrich, e.g. the output of a Google Maps or directory scraper. Each row with a website gets one enriched row back.

## `websiteField` (type: `string`):

Column holding the website. Leave empty to auto-detect (website, url, domain, companyWebsite, homepage). Dot paths work, e.g. company.website.

## `keepFields` (type: `array`):

Copied into each output row under "source" so you can match results back. Leave empty to keep every top-level text or number field.

## `keepSourceFields` (type: `boolean`):

Turn off to return only the enrichment.

## `maxDatasetRows` (type: `integer`):

Upper limit on rows read from the dataset.

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

Homepage plus the most likely contact pages (Contact, About, Team, Impressum, footer links). 6 finds contacts on most small-business sites. Raise it for big corporate sites.

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

Two-letter code such as US, GB, IN or DE. When set, local-format numbers in page text (e.g. 020 7946 0958) are recognised and returned in international format. Leave empty to keep only numbers written with a + prefix or a tel: link.

## `verifyEmailDomains` (type: `boolean`):

Looks up the MX record of every email's domain. Emails whose domain cannot receive mail are flagged (mx: false). Free, no emails are sent.

## `dropUndeliverable` (type: `boolean`):

Removes emails whose domain has no mail server. Turn off to keep them, flagged with mx: false.

## `onlySameDomainEmails` (type: `boolean`):

Hides third-party addresses such as the web agency or a Gmail address. Off by default; same-domain emails are always ranked first anyway.

## `maxEmailsPerSite` (type: `integer`):

Most companies publish 1–5. Extra ones are usually staff or third parties.

## `stopWhenFound` (type: `boolean`):

Saves time on large lists. Turn off to always crawl up to the page limit.

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

Skip pages the site owner asked crawlers not to visit.

## `includeEmptyRows` (type: `boolean`):

Adds a row with the reason (unreachable, no contacts) so your list stays complete. These rows are never charged.

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

How many websites are processed at the same time. Pages within one site are always fetched one by one.

## `pageTimeoutSecs` (type: `integer`):

Give up on a single page after this many seconds (3–60). Slow pages never block the run.

## `siteTimeoutSecs` (type: `integer`):

Time budget per website (10–300 s). The site is closed with what was found so far; a site with nothing found is free.

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

Not needed for most sites. Turn on Apify Proxy if many sites block you.

## Actor input object example

```json
{
  "websites": [
    "plausible.io",
    "fsf.org",
    "nobroker.in"
  ],
  "keepFields": [],
  "keepSourceFields": true,
  "maxDatasetRows": 10000,
  "maxPagesPerSite": 6,
  "verifyEmailDomains": true,
  "dropUndeliverable": true,
  "onlySameDomainEmails": false,
  "maxEmailsPerSite": 10,
  "stopWhenFound": true,
  "respectRobotsTxt": true,
  "includeEmptyRows": true,
  "maxConcurrency": 10,
  "pageTimeoutSecs": 10,
  "siteTimeoutSecs": 45,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "websites": [
        "plausible.io",
        "fsf.org",
        "nobroker.in"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("rationalistic_canoe/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 = { "websites": [
        "plausible.io",
        "fsf.org",
        "nobroker.in",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("rationalistic_canoe/website-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 '{
  "websites": [
    "plausible.io",
    "fsf.org",
    "nobroker.in"
  ]
}' |
apify call rationalistic_canoe/website-contact-finder --silent --output-dataset

```

## MCP server setup

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