# Website Email & Phone Finder - Checked Before You Send (`salomon.labs/website-email-finder`) Actor

Give it a company website and get every published email and phone number, each with the page it was found on and a 0-100 send-risk score with a plain-English reason.

- **URL**: https://apify.com/salomon.labs/website-email-finder.md
- **Developed by:** [Alan Salomon](https://apify.com/salomon.labs) (community)
- **Categories:** Lead generation, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 website scans

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 Email & Phone Finder — checked before you send

Give it a company website. Get back **every email address and phone number the
company publishes**, each one showing the page it was found on, plus a **0–100
send-risk score and a plain-English reason** for every address.

Most contact scrapers hand you a list of strings and leave you to discover which
ones bounce. This one tells you **which inbox you are actually writing to** — the
enquiries desk, the recruitment desk, or an unattended `no-reply` — and every
address is checked before you see it by the same engine that powers our [Bulk Email
Validator](https://apify.com/salomon.labs/bulk-email-validator).

**Checking is included, not an add-on.** It is not a per-email extra, so your bill
does not grow with how many contacts we find.

***

### What you get on every row

| column | what it is |
|---|---|
| `email` | the address, lowercased and de-duplicated |
| `safe_to_send_score` | 0–100 |
| `recommended_action` | `send`, `send_low_volume`, `manual_review`, `suppress` |
| `recommendation_detail` | one sentence explaining the decision |
| `found_on` | the exact page the address was found on |
| `evidence` | `structured` (schema.org), `mailto` (a link), or `text` — this also drives the score |
| `primary_phone` | best published number, in `+44…` form |
| `primary_phone_national` | the same number as a human would dial it |
| `primary_phone_type` | `fixed_line`, `mobile`, `toll_free`, `voip`, … |
| `primary_phone_location` | "London", "Berlin" — where the number is registered |
| `all_phones` | every number found |
| `linkedin_url` | the company's LinkedIn page, when it links to one |
| `is_role_account` / `is_free_provider` / `is_disposable` | the signals behind the score |
| `pages_scanned` | how many pages were read |
| `status` | `ok`, or a reason in plain words |

Export from the **Contacts** view for a clean lead list, or the **Phones** view for
one row per company.

### How the score works

An address the company **declared in its own structured data** scores highest, a
`mailto:` link it published scores nearly as high, and an address merely sitting in
page text scores much lower — because that one might be a supplier's, a customer
quotation, or years out of date. Where the address was found is treated as evidence,
not decoration.

An `info@` or `hello@` on a company's own contact page is the address they *ask*
strangers to use, so it is scored as the target rather than penalised as a shared
inbox. A `no-reply@` box reads 0 however prominently it is published.

Scores in the 60s and 70s are what a published, checked, non-probed address earns.
Nothing reaches 90+, and that is deliberate: without mailbox-level SMTP verification
we will not imply a certainty we do not have.

### How it works

1. Reads the homepage, then picks **up to four pages most likely to carry contact
   details** — contact, about, team, imprint, get-in-touch — by looking at both the
   URL and the link text. Blogs, shops, carts and legal boilerplate are skipped.
2. Extracts addresses from **schema.org structured data, `mailto:` links, and page
   text**, undoing the obfuscation sites use: HTML entities, `name [at] domain [dot]
   com`, and Cloudflare's email protection. A scraper that ignores that last one
   returns nothing for pages that plainly show an address to a human.
3. Extracts phone numbers with **Google's libphonenumber**, so VAT identifiers,
   company registration numbers, order references and coordinates are not returned
   as phone numbers. The country is taken from the site itself.
4. Checks every address — syntax, domain, mail host (MX), disposable services, role
   inboxes, free providers — and scores it.

### Which pages, and why so few

Homepage plus four is deliberate. A company that publishes no address on its
homepage, contact, about, team or imprint page has not published one, and opening
thirty pages to prove it would cost you money for nothing. Set **Extra pages per
website** to `0` for a homepage-only scan, or up to `10` if you would rather dig.

### What it does not do

- **No mailbox-level SMTP verification.** It cannot confirm that a specific inbox
  exists. It removes the provably bad addresses — invalid syntax, dead domains,
  disposable services, unattended `no-reply` boxes — and tells you honestly which
  ones it cannot be certain about. Use it as the first pass; send whatever survives
  to a per-email verifier if you need mailbox-level certainty.
- **No guessing.** It never invents `firstname.lastname@company.com`. Every address
  returned was actually published, and `found_on` shows where.
- **No phone numbers a numbering plan does not recognise.** A strict setting means
  the occasional real but oddly-formatted number is missed. That is the right trade:
  the permissive setting returns VAT numbers as phone numbers.
- **No login-gated sources.** Websites only.

### Judgement, and how it differs

An `info@` or `hello@` address on a company's own contact page is **the address they
ask strangers to use**, so it comes back `send` rather than flagged for review. A
`careers@` inbox is real and monitored but exists to receive job applications, so it
is named as the wrong door. A `no-reply@` box is suppressed — nobody reads it. A
consumer mailbox (`@gmail.com`) published as a company's contact is usually a small
business running on a personal inbox: deliverable, and labelled as the weaker signal
it is.

### Input

Paste domains or full URLs — `acme.com`, `www.acme.com`, `https://acme.com/` all
work and count as **one website, charged once**. Or point it at a CSV link, or the
dataset of an upstream Actor (a Google Maps scrape, for example) and the website
column is detected automatically.

### Pricing

**$0.002 per website — $2 per 1,000.** Charged once per website, after its results
are saved, whether it yields ten addresses or none.

- **Duplicates are not charged twice.** `acme.com`, `www.acme.com` and
  `https://acme.com/` are one website and one charge.
- **No per-email fee.** Every address found is checked and scored at no extra cost,
  so a company with nine contacts costs the same as one with a single address.
- **A website that publishes nothing still costs $0.002**, because the work happened
  — up to nine pages fetched and read. You get a row saying so, with the contact-form
  URL where there is one.

### Use it in a pipeline

Scrape companies with any Google Maps or directory Actor, feed that dataset in
here, and every company comes back with contactable people attached.

**You do not need to run the addresses through our [Bulk Email
Validator](https://apify.com/salomon.labs/bulk-email-validator) afterwards.** It is
the same engine, so it would repeat the checks you have already paid for and reach
the same conclusions. That Actor is for a list you got somewhere else — a CRM
export, a conference list, something a colleague sent you — where nothing is known
about the addresses yet. For the creator side of the same job, there is [YouTube
Creator Finder](https://apify.com/salomon.labs/youtube-creator-finder).

Built by **Salomon Labs**. Every number traces back to where it came from, and the
system says "unknown" rather than guessing.

# Actor input Schema

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

Company websites to scan. Paste domains or full URLs - acme.com, www.acme.com and https://acme.com are all accepted and count as one website. Leave empty to run a short sample.

## `csvUrl` (type: `string`):

A link to a CSV of companies. The column holding websites is detected automatically - or name it below.

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

Which column of your CSV or dataset holds the website. Only needed if the automatic detection picks the wrong one.

## `pageLimit` (type: `integer`):

How many likely contact pages to open after the homepage. The best candidates are chosen automatically - contact, about, team, imprint. Set 0 to read the homepage only, which is cheaper and finds less.

## `includePhones` (type: `boolean`):

Return published phone numbers alongside the email addresses, each with its type and location.

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

How many websites to scan in parallel. The pages of any single site are always read one at a time, so nobody's server gets hammered.

## `delaySeconds` (type: `integer`):

Optional politeness pause between the pages of one website. Raise it if you are scanning a small site you care about.

## Actor input object example

```json
{
  "websites": [
    "acme.com",
    "https://example-company.de/"
  ],
  "pageLimit": 4,
  "includePhones": true,
  "concurrency": 5,
  "delaySeconds": 0
}
```

# Actor output Schema

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

The clean export: website, email, send-risk score, recommended action, the reason, the best phone number, the page it was found on, whether it belongs to the company's own domain, and status. This is the one to use for a spreadsheet or a CSV download.

## `phones` (type: `string`):

One row per website with its published numbers: the number as a human would dial it, its E.164 form, the line type (fixed line, mobile, toll free) and where the number is registered.

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

Every column for every row, including the evidence: how the address was published (schema.org, a mailto link, or page text), the social profiles found, the numbering plan the phones were read against, the contact-form URL, and how many pages were read.

## `summary` (type: `string`):

Counts for the whole run: websites processed, how many yielded an email, how many yielded a phone, a breakdown by recommended action, how many failed, and whether the run stopped early on a spend limit.

# 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": [
        "apify.com",
        "stripe.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("salomon.labs/website-email-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": [
        "apify.com",
        "stripe.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("salomon.labs/website-email-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": [
    "apify.com",
    "stripe.com"
  ]
}' |
apify call salomon.labs/website-email-finder --silent --output-dataset

```

## MCP server setup

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