# Website Email & Phone Contact Extractor (`valev-lab/website-contact-extractor`) Actor

Extract public emails, valid phone numbers, social profiles, contact pages, forms, addresses, and VAT IDs from company websites. Get one CRM-ready row per domain with transparent scoring and source URLs. Use fast HTTP crawling by default, with optional JavaScript fallback and email verification.

- **URL**: https://apify.com/valev-lab/website-contact-extractor.md
- **Developed by:** [Daniel Valev](https://apify.com/valev-lab) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.59 / 1,000 website scanneds

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

**Website Email & Phone Contact Extractor** is a bulk **website contact scraper** for lead enrichment. Paste company domains to extract public emails, valid phone numbers, social profiles, contact and Impressum pages, forms, addresses, VAT IDs, and scored source records. Every deduplicated domain returns one CRM-ready row, including explicit failure, timeout, and budget statuses.

The default HTTP scan costs `$0.005 + $0.0008` per successfully scanned website, so one run can process up to 6,243 HTTP-only websites with the free $5 Apify credit. JavaScript fallback and email verification are optional paid events.

### How to find emails and phone numbers from websites

1. Open the Actor, click **Try for free**, and paste one website per line.
2. Keep the default HTTP-only scan, or enable paid email verification and JavaScript fallback when needed.
3. Click **Start** and export the resulting Dataset to JSON, CSV, Excel, XML, or your integration.

```json
{
  "urls": [
    "konditorei-buchwald.de"
  ],
  "maxPagesPerSite": 10,
  "verifyEmails": false,
  "jsFallback": false,
  "includePersonalNames": false
}
```

Inputs are case-insensitively deduplicated by registrable domain. `www.example.com` and `shop.example.com` therefore share one output row for `example.com`.

### What data can this website contact scraper extract?

| Data | What you receive |
| --- | --- |
| Emails | Primary and deduplicated public emails, 0–100 quality score, source URL, domain-match, role-account, free-provider, and disposable-domain flags |
| Phone numbers | Valid international numbers normalized to E.164, with original text and source page |
| Social profiles | LinkedIn company and people profiles, X, Facebook, Instagram, YouTube, GitHub, TikTok, and Pinterest |
| Contact routes | Contact, legal, support, team, and Impressum pages plus detected contact forms and form fields |
| Company data | Organization names, structured postal addresses, and explicitly published VAT or tax IDs |
| Run metadata | Status, pages crawled, duration, JavaScript fallback usage, timestamp, and actionable failure reason |

### Website contact scraper use cases

- Enrich company domains exported from Google Maps, LinkedIn, directories, or a CRM.
- Build a bulk business email and phone list while preserving one result row per input domain.
- Find German and multilingual Impressum or legal-page contacts.
- Detect contact-form-only companies instead of treating them as failed leads.
- Refresh contact data on a schedule and export it to JSON, CSV, Excel, Google Sheets, Make, Zapier, or n8n.
- Provide structured website contact data to AI agents through the Apify API or MCP server.

### Website contact scraper output example

The default Dataset contains exactly one record per deduplicated input domain. The `overview` view shows the six fields an SDR usually needs first: `websiteUrl`, `primaryEmail`, `allEmails`, `primaryPhone`, `linkedin`, and `status`.

```json
{
  "websiteUrl": "https://www.konditorei-buchwald.de/",
  "primaryEmail": "info@konditorei-buchwald.de",
  "allEmails": [
    "info@konditorei-buchwald.de"
  ],
  "primaryPhone": "+49303915931",
  "linkedin": null,
  "status": "success"
}
```

Email source precision is ordered as JSON-LD, `mailto:`, Cloudflare protection, plain text, and obfuscation. Scores are deterministic: source confidence, domain match, contact-page placement, repetition across pages, role-account status, free-provider status, and disposable-domain status all affect the 0–100 score.

### Website contact scraper input

| Parameter | Type | Default | What it controls |
| --- | --- | --- | --- |
| `urls` | string array | required | 1–10,000 bare domains or full URLs. Tracking parameters are removed and inputs are deduplicated by registrable domain. |
| `maxPagesPerSite` | integer | `20` | Hard cap of 1–200 attempted pages per website. It does not affect the event price. |
| `verifyEmails` | boolean | `false` | Verifies each extracted email. Charged per email that completes verification. |
| `maxConcurrency` | integer | `5` | Parallel page requests within one website; requests to the same host stay sequential. |
| `maxWebsitesConcurrency` | integer | `5` | Number of separate websites processed in parallel. |
| `requestTimeoutSecs` | integer | `15` | Per-request timeout from 5 to 60 seconds. |
| `maxSiteDurationSecs` | integer | `90` | Hard per-site wall-clock limit from 20 to 600 seconds. |
| `respectRobotsTxt` | boolean | `true` | Honors `robots.txt`. Disable only when you have a lawful reason and permission. |
| `jsFallback` | boolean | `false` | If HTTP finds no email, renders only the best contact page and homepage. Charged only after a browser pass completes. |
| `useProxy` | boolean | `false` | Uses the configured proxy. Proxy traffic can add charges from your proxy plan. |
| `proxyConfiguration` | object | Apify Proxy | Proxy groups or custom proxy URLs, used only when `useProxy` is enabled. |
| `verificationLevel` | `format`, `mx`, or `smtp` | `mx` | Format is local; MX checks mail servers; SMTP probes a mailbox and a random catch-all address. |
| `smtpFromAddress` | string | none | Optional own-domain SMTP sender address. Used only for SMTP verification. |
| `includePersonalNames` | boolean | `true` | Associates mailto addresses with nearby names and job titles where possible. |

### How much does website contact scraping cost?

This Actor uses pay per event. A normal HTTP page count does not change the website price.

| Event | When it is charged | Free-tier price |
| --- | --- | ---: |
| `actor-start` | Once when the run starts | $0.0050 |
| `website-scanned` | After a successful `success` or `no_contacts` record is stored | $0.0008 |
| `email-verified` | For each email that completes the selected verification level | $0.0015 |
| `js-fallback-used` | For each website whose opt-in browser fallback completes | $0.0030 |

Without verification or JavaScript fallback, the formula is `$0.005 + $0.0008 × successfully scanned websites`.

| Websites in one run | Worked cost |
| ---: | ---: |
| 1 | $0.0058 |
| 10 | $0.0130 |
| 100 | $0.0850 |
| 1,000 | $0.8050 |

For 100 websites with one verified email each, the cost is `$0.005 + $0.080 + $0.150 = $0.235`. If all 100 also need JavaScript fallback, add `$0.300`, for a total of `$0.535`.

With the free $5 Apify credit, one HTTP-only run can scan up to 6,243 successful websites at Free-tier event prices. Actual capacity is lower when verification, JavaScript fallback, paid proxy traffic, or multiple run starts are used. Bronze, Silver, and Gold tiers discount every event by approximately 5%, 12%, and 26%; higher tiers receive further discounts.

Failed scans are stored as reconciliation rows but are not charged as `website-scanned`. When a maximum run cost or paid-item limit is reached, the remaining domains receive `skipped_budget` rows instead of disappearing.

### Why this website email scraper finds more contacts

- JSON-LD and schema.org microdata for `Organization`, `LocalBusiness`, `Person`, and `ContactPoint`
- Cloudflare `/cdn-cgi/l/email-protection` and `data-cfemail` decoding
- `name [at] domain [dot] com`, entity-encoded, and split inline addresses
- Multilingual contact, legal, team, and impressum pages
- Valid E.164 phone numbers with country inference
- LinkedIn, X, Facebook, Instagram, YouTube, GitHub, TikTok, and Pinterest profiles
- Share-link and social-intent exclusion
- Contact-form detection when no direct email is published
- Optional JavaScript rendering and format, MX, or SMTP verification

### Honest limitations

Expect roughly 20–30% of websites to publish no extractable email. Some companies intentionally offer only a contact form; those results can still return `contactForm`. Emails inserted only after complex interaction, login, consent, or CAPTCHA may remain unavailable even with JavaScript fallback. The Actor does not bypass access controls.

`jsFallback` is opt-in because a browser is slower and has its own paid event. It runs only when the HTTP pass finds zero emails, and only for the homepage and best contact-page candidate.

Google Workspace, Microsoft 365 and Yahoo reject or greylist SMTP probing from cloud IPs. For those providers cap confidence at 45 and never set `isVerified: true`. Many cloud hosts also block outbound port 25 entirely — detect `ECONNREFUSED`/`ETIMEDOUT` on connect and return `smtpAvailable: false` rather than silently reporting everything as invalid. Overstating verification accuracy is the fastest way to earn 1-star reviews.

MX verification proves that a domain accepts email, not that an individual mailbox exists. Catch-all SMTP domains accept random recipients, so their individual addresses remain unverified.

### Integrations

Export directly to Google Sheets or connect the Dataset to Make, Zapier, n8n, and Apify webhooks. Scheduled Actor runs can refresh a CRM enrichment list on a daily, weekly, or monthly cadence. API and webhook consumers should use `domain` as the reconciliation key and inspect `status` before starting outreach.

### API

#### Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('valev-lab/website-contact-extractor').call({
    urls: ['apify.com', 'wordpress.org'],
    verifyEmails: true,
    verificationLevel: 'mx',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("valev-lab/website-contact-extractor").call(
    run_input={
        "urls": ["apify.com", "wordpress.org"],
        "verifyEmails": True,
        "verificationLevel": "mx",
    }
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

#### cURL

```bash
curl --request POST \
  "https://api.apify.com/v2/acts/valev-lab~website-contact-extractor/runs?token=$APIFY_TOKEN&waitForFinish=120" \
  --header "Content-Type: application/json" \
  --data '{
    "urls": ["apify.com", "wordpress.org"],
    "verifyEmails": false
  }'
```

Read the Dataset with `GET https://api.apify.com/v2/datasets/{defaultDatasetId}/items?clean=true`.

### Use from MCP and AI agents

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=valev-lab/website-contact-extractor"
```

Example prompts:

- “Enrich these 200 company domains with their best public email, phone number, and LinkedIn company page.”
- “Find the German Impressum contacts for this domain list and return only domain-matching emails with scores above 50.”
- “Check these prospect websites, verify their emails at MX level, and separate contact-form-only companies.”

### Legality and compliance

This Actor extracts data that websites publish publicly and does not log in, bypass access controls, solve CAPTCHAs, or access private profiles. You are responsible for having a lawful purpose, respecting website terms and `robots.txt`, honoring GDPR and other privacy rights, following CAN-SPAM and similar outreach laws, maintaining suppression lists, and using the output responsibly. Public availability does not automatically grant permission for unsolicited marketing.

### FAQ

#### How do I scrape email addresses from a list of websites?

Paste the domains into `urls` and run the Actor. It prioritizes structured data, mail links, Cloudflare-protected addresses, visible text, and common obfuscation, then returns one row per deduplicated domain.

#### How can I find business phone numbers from company websites?

The Actor checks `tel:` links, JSON-LD telephone fields, and visible text. It validates candidates with `libphonenumber-js` and returns only valid E.164 numbers plus their original text.

#### Can I scrape emails from German Impressum pages?

Yes. German `Impressum` and `Kontakt` paths receive the highest crawl priority, alongside multilingual legal and contact-page terms.

#### Does this website email scraper work on JavaScript websites?

The default path uses fast HTTP requests. Enable `jsFallback` to render the best contact page and homepage when HTTP found zero emails.

#### Can this Actor decode Cloudflare protected email addresses?

Yes. It decodes both Cloudflare email-protection links and `data-cfemail` attributes using Cloudflare's XOR format.

#### How accurate is SMTP email verification?

SMTP is a useful signal, not a guarantee. Catch-all servers, greylisting, cloud-IP blocks, and providers such as Google Workspace, Microsoft 365, and Yahoo limit mailbox-level certainty.

#### Why does a website return no email but a contact form?

Some businesses deliberately publish only a form. The Actor reports a compatible form's URL, method, and fields so your workflow can distinguish “form only” from a failed scan.

#### Will the Actor return a row when a website is down?

Yes. Invalid, failed, timed-out, and budget-skipped inputs still produce explicit rows for reconciliation; only duplicate registrable domains are combined.

#### Does increasing maxPagesPerSite increase the price?

No. Page count does not change the `website-scanned` event price, although larger crawls can run longer.

#### Can I send the extracted contacts to Google Sheets, Zapier, Make, or n8n?

Yes. Use Dataset exports, native Apify integrations, webhooks, or the REST API to send results to those tools and your CRM.

### Changelog

#### 0.1.0 — 2026-08-24

- Initial production release.
- Added HTTP-first crawling, multilingual page scoring, JSON-LD and microdata, Cloudflare and obfuscation decoding, phone and social extraction, forms, optional Playwright fallback, three verification levels, migration-safe state, budget guards, complete schemas, and pay-per-event billing.

# Actor input Schema

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

Required. Add 1–10,000 bare domains or full website URLs, one per line. Inputs are normalized and deduplicated by company domain before scanning.

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

Maximum number of pages to inspect on each website, from 1 to 200. This setting does not affect the price.

## `verifyEmails` (type: `boolean`):

Optionally check each found email at the selected verification level. This is charged once per email actually verified.

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

Maximum number of page requests running at once within a website, from 1 to 20. Requests to any single host remain sequential.

## `maxWebsitesConcurrency` (type: `integer`):

Maximum number of different websites scanned at the same time, from 1 to 20.

## `requestTimeoutSecs` (type: `integer`):

How many seconds to wait for an individual page before treating that request as failed.

## `maxSiteDurationSecs` (type: `integer`):

Hard wall-clock limit for one website. This prevents a slow or broken site from delaying the rest of your lead list.

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

Honor each website's robots.txt rules. Enabled by default; turn it off only when you have a lawful reason and permission to do so.

## `jsFallback` (type: `boolean`):

When the HTTP scan finds no email, retry only the homepage and best contact page in a headless browser. Each website that uses this fallback triggers a separate charged event.

## `useProxy` (type: `boolean`):

Route requests through the selected proxy configuration. Proxy traffic may add charges from your Apify proxy plan.

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

Choose Apify Proxy or your own proxy URLs. This setting is used only when “Use a proxy” is enabled and proxy traffic may add charges.

## `verificationLevel` (type: `string`):

Choose format-only validation, mail-server (MX) lookup, or an SMTP mailbox probe. Higher levels can take longer; every email actually checked is charged once.

## `smtpFromAddress` (type: `string`):

Optional. Use a sender address on a domain you own for SMTP probes; this can improve acceptance and accuracy. Used only with the SMTP verification level.

## `includePersonalNames` (type: `boolean`):

Try to associate each public email with a nearby person's name and job title when the page provides that context.

## Actor input object example

```json
{
  "urls": [
    "konditorei-buchwald.de"
  ],
  "maxPagesPerSite": 20,
  "verifyEmails": false,
  "maxConcurrency": 5,
  "maxWebsitesConcurrency": 5,
  "requestTimeoutSecs": 15,
  "maxSiteDurationSecs": 90,
  "respectRobotsTxt": true,
  "jsFallback": false,
  "useProxy": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "verificationLevel": "mx",
  "smtpFromAddress": "verification@your-company.com",
  "includePersonalNames": true
}
```

# Actor output Schema

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

Complete contact records in the run's default dataset.

## `overview` (type: `string`):

The six-column overview for quick prospecting and CRM reconciliation.

# 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": [
        "konditorei-buchwald.de"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("valev-lab/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": ["konditorei-buchwald.de"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("valev-lab/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": [
    "konditorei-buchwald.de"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call valev-lab/website-contact-extractor --silent --output-dataset

```

## MCP server setup

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