# Website Contact Scraper — Emails, Phones & Company Enrichment (`saahir-job-data/website-contact-lead-enricher`) Actor

Enrich company websites with public emails, phones, social profiles, contact forms, tech-stack evidence, and hiring links. One company row with source URLs. Paste domains, CSV, or a Google Maps dataset. No API keys. Failed and no-contact sites have no result fee.

- **URL**: https://apify.com/saahir-job-data/website-contact-lead-enricher.md
- **Developed by:** [Saahir Sultan](https://apify.com/saahir-job-data) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 enriched company websites

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

## Website Contact Scraper & Lead Enrichment

Turn a list of company websites into structured contact records for your CRM, lead research, or sales workflow. Find public email addresses, phone numbers, company social profiles, contact forms, website technologies, and careers links. Every email and phone includes the page where it was found.

**$5 per 1,000 enriched websites, plus $0.001 per run start.** An enriched website has at least one public email, phone, supported social link, or contact form. Up to six pages can be included in one website result. Invalid inputs, duplicate domains within a run, inaccessible websites, and websites with no contacts produce no result charge. The start fee still applies. Platform usage is included in the listed customer price.

### Start in under a minute

1. Paste company domains or website URLs into **Websites**.
2. Set **Maximum websites to attempt** and, if needed, the phone country.
3. Run the Actor. Open the **Company contacts** dataset view or download **LEADS\_CSV**.
4. Review **UNRESOLVED** for websites that did not produce a billable record and **REPORT** for completion details.

You do not need an external API key, proxy subscription, browser cookies, or an email account. This Actor reads publicly accessible web pages. It does not send emails, submit forms, log in to websites, or guess unpublished email addresses.

### Why use company-level enrichment?

Many lead lists already contain a website but lack a usable contact route. A generic homepage scraper can miss the contact or imprint page, return the same address repeatedly, or separate one company's information across many rows. This Actor prioritizes contact, about, imprint, support, team, and careers links, then combines the collected information into one company record.

The unit you pay for is an enriched website, rather than every visited page or every email. By default the crawler attempts four pages per website. You can increase that to six without changing the result price. A record can contain multiple emails, multiple phones, several social profiles, and technology evidence. The output preserves source row numbers so it can be joined back to an existing list.

### Input options

Provide exactly one input source. Clear example URLs before selecting CSV or dataset input.

#### Paste websites

```json
{
  "websites": ["https://www.python.org/", "https://www.scrapy.org/"],
  "maxWebsites": 100,
  "maxPagesPerWebsite": 4,
  "phoneRegion": "US",
  "checkEmailDns": true
}
```

Both bare domains and complete HTTP or HTTPS URLs are accepted. The initial path is preserved, which is useful when you already know a contact page. URLs for the same registered company domain are combined within the run. For example, a homepage and its www contact page produce one record. Separately hosted businesses on supported private suffixes, such as different github.io sites, remain separate.

#### CSV input

Paste comma, semicolon, or tab-separated CSV into **CSV text**. Choose the exact top-level column name with **Website column / field**. The default is `website`. CSV input is limited to 4 MB. The output includes `sourceRow`, a one-based data-row index excluding the CSV header. Duplicate rows appear in `duplicateSourceRows` on the company's record.

```json
{
  "csvText": "website,company\nhttps://www.python.org/,Python\nhttps://www.scrapy.org/,Scrapy",
  "websiteField": "website"
}
```

#### Existing Apify dataset

Select an accessible source dataset containing website URLs. For example, a Google Maps business dataset commonly provides a `website` field. This Actor enriches the company websites in that dataset; it does not scrape Google Maps itself. Select the resource in the input form to grant read-only access to that dataset. The source is never modified.

Use `websiteField` to select a different top-level field, such as `url` or `domain`. Nested property paths are not supported. Output source row numbers refer to dataset item order. Keep the original dataset if you need to join other columns later.

### Output you can use immediately

Each result includes:

| Field | Meaning |
| --- | --- |
| `recordId` | Stable identifier derived from the company domain |
| `domain`, `website` | Company domain and fetched entry URL |
| `companyName`, `companyNameSource` | Name from business structured data, published site name, or a page-title fallback |
| `description` | Published page description, when present |
| `bestEmail` | A selected published address on the company domain, prioritizing sales and general contact mailboxes |
| `emails` | Addresses, source pages, role flags, same-company-domain flags, and DNS status |
| `phone`, `phones` | Parsed phone numbers in international format with source pages |
| `socials`, `linkedinUrl` | Social links published by the website; LinkedIn output is restricted to company, school, and showcase pages |
| `contactPages`, `contactForms` | Discovered contact pages and forms; forms are never submitted |
| `technologies` | Recognized markers, categories, source URLs, and matching evidence |
| `hiringLinks` | Published careers or supported ATS links |
| `addresses` | Business addresses explicitly present in structured data |
| `pagesScraped`, `sourceUrls`, `warnings` | Coverage and any failed subpages |
| `sourceRow`, `duplicateSourceRows` | References back to the original input |
| `partial`, `checkedAt` | Incomplete subpage coverage and extraction timestamp |

The **Company contacts** view is convenient for CRM imports. **Technology & hiring** helps segment websites by observed software and careers links. **Coverage & evidence** helps audit where the information came from. Full nested JSON remains available through the dataset API. A flat, formula-protected CSV is saved as `LEADS_CSV` in the run's key-value store.

### Email quality and what DNS means

Addresses are extracted from published text, mailto links, and business structured data. Common `[at]` and `[dot]` text obfuscation is normalized. Obvious placeholders and asset-like strings are excluded. Emails inside arbitrary script code are not harvested. Addresses from outside the company's domain remain clearly marked.

Optional DNS checks examine MX records for up to five distinct email domains per website. DNS requests send domain names to Google Public DNS; full email addresses are not sent. `mx_present` means a mail route exists. It does **not** prove that a particular mailbox exists or will accept a message. `no_mx_record` is also not a definitive invalid-mailbox result because some domains use address-record fallback. `unknown` means the DNS lookup could not establish a result.

`mailboxStatus` is always `not_checked`. This Actor does not perform SMTP verification, catch-all testing, deliverability guarantees, or message sending. `bestEmail` only selects addresses on the same registered company domain and is null when none qualifies. It is a deterministic ordering of published addresses, not a verified decision-maker identity or a promised conversion.

### Technology and hiring evidence

The Actor recognizes a curated set of common CMS, ecommerce, analytics, marketing, support, payment, and framework markers. Examples include WordPress, Shopify, WooCommerce, Webflow, HubSpot, Klaviyo, Intercom, Google Analytics, Stripe, and Next.js. This is a focused detector, not an exhaustive technology census. Missing a marker does not establish that a company does not use that product.

Each detection includes a source page and matching marker. A linked provider or embedded script is evidence of that web integration, not proof of an active commercial subscription. Careers links recognize providers including Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Workday, and BambooHR. A careers link does not establish that the company is currently hiring. No job counts or buying-intent scores are invented.

### Limits, completion, and predictable charges

The defaults attempt up to 100 unique websites, four pages each, with eight websites processed concurrently. Maximum configurable limits are 2,000 websites, six pages per website, 10,000 input rows, and 480 seconds of processing. Large lists can therefore require multiple runs. The overall Apify run timeout should exceed the processing budget to allow exports to finish.

The Actor checks the customer's remaining charge allowance before starting another batch. Set Apify's maximum run charge to control spending. `maxWebsites` limits attempted websites, including unsuccessful attempts; it is not a promise of that many delivered results. Large pages, long response times, restrictive crawl delays, and unreachable sites are stopped within bounded resource limits.

`REPORT.complete` and `REPORT.stopped` describe whether the input was finished or processing stopped because of a limit. `UNRESOLVED` lists invalid inputs, inaccessible websites, and no-contact outcomes without charging a result fee. A partial record with useful contacts is billable and includes warnings about subpages that failed. Social-only and contact-form-only records are billable even when no email is found. Input duplicates are free within a run; running the same company again in a later run performs a fresh enrichment and is billed normally.

### Coverage and responsible use

The crawler reads HTML returned by public websites and respects robots.txt. It does not execute JavaScript, bypass login walls, solve CAPTCHA challenges, rotate proxies to evade blocks, or crawl unrelated external domains. JavaScript-only contact pages and protected websites may be unresolved. Redirects are restricted to the same registered company domain. IP-address destinations and private networks are rejected.

Phone parsing uses the configured country for numbers without an international prefix. Choose the correct country for local business lists; use separate runs for mixed regions when local number formats are ambiguous. A published social link does not by itself prove ownership. Website contact details can become outdated, and automatically selected company names may require review.

Only collect and use information you are authorized to process. The Actor provides data collection and enrichment; your downstream use, retention, and outreach decisions remain yours. There is no built-in bulk messaging or external contact-data upload. Results are stored in your Apify run storage.

### Automation and support

Run through the Apify API, use a task for a saved configuration, or connect the resulting dataset to your existing n8n, Make, or CRM workflow. Apify schedules can repeat enrichment when you need fresher information. Scheduling does not discover new companies automatically: supply the website list or dataset you want processed.

If an expected published contact is missing, open an Actor issue with the public website URL, the relevant source page, and the field you expected. Avoid sharing credentials or private customer lists in public issues. Reports that identify a reproducible parsing problem are the most useful way to improve coverage.

# Actor input Schema

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

Company website URLs or domains. Clear these examples before using CSV or a dataset. Duplicate company domains are combined within the run.

## `csvText` (type: `string`):

CSV up to 4 MB with a website column. Comma, semicolon, or tab delimiters. Clear Websites first.

## `sourceDatasetId` (type: `string`):

Choose a dataset with company websites, such as a Google Maps results dataset. Only the selected dataset is read. Clear Websites first.

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

Exact top-level CSV column or dataset field containing website URLs. Nested fields are not supported.

## `maxWebsites` (type: `integer`):

Includes successful and unsuccessful websites. Only enriched company records incur a result fee.

## `maxPagesPerWebsite` (type: `integer`):

Contact, about, imprint, support, and careers links are prioritized. The per-website result price stays the same.

## `phoneRegion` (type: `string`):

Two-letter country code used for local numbers without an international prefix, for example US, CA, GB, DE.

## `checkEmailDns` (type: `boolean`):

Checks up to five email domains per website through Google Public DNS. Domain names only are sent. Does not verify mailbox existence or send email.

## `concurrency` (type: `integer`):

Different company websites processed concurrently. Requests within a company remain sequential.

## `maxInputRows` (type: `integer`):

Includes invalid and duplicate rows. REPORT records any input truncation.

## `maxSeconds` (type: `integer`):

Website processing time. Set the Apify run timeout higher to leave time for exports.

## Actor input object example

```json
{
  "websites": [
    "https://www.python.org/",
    "https://www.scrapy.org/"
  ],
  "websiteField": "website",
  "maxWebsites": 100,
  "maxPagesPerWebsite": 4,
  "phoneRegion": "US",
  "checkEmailDns": true,
  "concurrency": 8,
  "maxInputRows": 5000,
  "maxSeconds": 240
}
```

# Actor output Schema

## `companies` (type: `string`):

No description

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

No description

## `report` (type: `string`):

No description

## `unresolved` (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": [
        "https://www.python.org/",
        "https://www.scrapy.org/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("saahir-job-data/website-contact-lead-enricher").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": [
        "https://www.python.org/",
        "https://www.scrapy.org/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("saahir-job-data/website-contact-lead-enricher").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": [
    "https://www.python.org/",
    "https://www.scrapy.org/"
  ]
}' |
apify call saahir-job-data/website-contact-lead-enricher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,saahir-job-data/website-contact-lead-enricher"
        }
    }
}
```

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/Mu1iXofitx01g4JZC/builds/zzqeJE6VgsNFGQmzP/openapi.json
