# Website Contact & Lead Enricher - owner email, phones, tech (`vital_tuxedo/website-contact-lead-enricher`) Actor

Give it company domains, get one lead row each: the published email most likely to reach the owner (not just info@), every email, phone and social link on the site, its CMS and tech stack, and its email provider, SPF and DMARC. Dead mail domains are dropped. No login, no key.

- **URL**: https://apify.com/vital_tuxedo/website-contact-lead-enricher.md
- **Developed by:** [Stephen Psaradellis](https://apify.com/vital_tuxedo) (community)
- **Categories:** Lead generation, SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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 & Lead Enricher

Paste a list of company domains. Get back one lead row per company:

- **the email most likely to reach the owner**, not whichever address the page
  listed first
- every email, phone number and social profile the site publishes, with the
  page each was found on
- what the site is built with: CMS, ecommerce platform, analytics and ad
  pixels, chat widget, payments, hosting and CDN
- who runs the domain's email (Google Workspace, Microsoft 365, ...), which
  services send as it, and its SPF and DMARC setup

### Why the "best email" is better than a list

Most contact scrapers hand you every address on the page. On a small-business
site that list is full of addresses that will never reach a decision maker:
the web designer's address in the footer, `careers@`, a ticket desk, a
template placeholder like `contact@mysite.com`, a font author's credit hidden
in a stylesheet, or a mailbox on a domain that no longer receives mail.

This Actor's ranking rules come from sending real outreach to small
businesses and fixing every case where an email went to the wrong inbox or
bounced. In order, it prefers:

1. a named person on the company's own domain (`melissa@company.com`)
2. a Gmail address the owner published (these bounce least)
3. a general box on the company's domain (`info@`, `hello@`, `office@`)
4. sales and other boxes
5. support, events, jobs and similar desks
6. old ISP addresses (AOL, Yahoo, Comcast), which are the most often abandoned

It never picks an address on a third party's domain (your web agency, a
franchisor, a booking platform's relay), and it drops any address whose
domain has no working mail server - checked live in DNS, so you do not pay
to bounce. Every address is still in `emails`, labelled, if you want them all.

### What you get

| field | meaning |
|---|---|
| `domain` | the domain, `www.` removed |
| `best_email` | the address most likely to reach the owner (or `null`) |
| `best_email_type` | `person`, `general`, `freemail`, `sales`, `other` or `support` |
| `owner_emails` | every address that reaches the business, best first |
| `emails` | every address found: page, type, `reaches_business`, `domain_takes_mail` |
| `phones` | phone numbers from `tel:` links and page text, with the page |
| `socials` | Facebook, Instagram, LinkedIn, X, YouTube, TikTok, Pinterest, Yelp |
| `contact_page` | the contact page it found |
| `tech_names`, `technologies` | ~140 technologies, each with the evidence it was detected from |
| `cms`, `ecommerce`, `analytics` | the same, grouped |
| `email_provider` | Google Workspace, Microsoft 365, Zoho, ... from MX (or SPF behind a gateway) |
| `email_gateway` | Mimecast, Proofpoint, Barracuda, ... when one sits in front |
| `email_senders` | services the SPF record authorises: SendGrid, Mailchimp, HubSpot, Klaviyo, ... |
| `spf`, `dmarc`, `dmarc_policy`, `email_issues` | the records, and plain-English problems with them |
| `dns_host` | Cloudflare, Route 53, GoDaddy, ... from the nameservers |
| `title`, `final_url`, `http_status`, `https`, `ssl_error` | the site itself |
| `website_error` | why the site could not be read, if it could not |

The key-value store also gets a `RUN_SUMMARY` with counts for the run.

### Sample row (illustrative)

```json
{
  "domain": "acmebakery.com",
  "best_email": "maria@acmebakery.com",
  "best_email_type": "person",
  "owner_emails": ["maria@acmebakery.com", "info@acmebakery.com"],
  "phones": [{ "phone": "(555) 201-3344", "source": "tel-link", "page": "https://acmebakery.com/contact" }],
  "socials": { "facebook": "https://www.facebook.com/acmebakery", "instagram": "https://www.instagram.com/acmebakery" },
  "cms": ["WordPress"],
  "ecommerce": ["WooCommerce"],
  "analytics": ["Google Analytics", "Meta Pixel"],
  "email_provider": "Google Workspace",
  "email_senders": ["Google Workspace", "Mailchimp / Mandrill"],
  "dmarc_policy": "none",
  "email_issues": ["DMARC policy is p=none (monitoring only)"]
}
```

### Use cases

- **Cold outreach lists**: turn a list of company websites (from Google Maps
  exports, directories, your CRM) into an owner email per company.
- **Lead qualification**: find every Shopify store, every WordPress site
  without analytics, every company still on GoDaddy email.
- **Agency prospecting**: sites with no DMARC, an expired certificate or an
  old CMS are sites that need you.
- **CRM enrichment**: add tech stack, email provider and socials to accounts
  you already have.

### Pricing

Pay per event. No charge to start, no platform usage charge:

| event | price | when |
|---|---|---|
| `domain-analysed` | **$0.004** | every domain: homepage + contact pages + tech stack + DNS |
| `lead-with-email` | **$0.006** | only when the row has a `best_email` |

So **$0.01 per lead with an owner email**, and $0.004 for a domain without
one. 1,000 domains where 450 have an owner email cost about $6.70. Set **Max
domains** to cap a run.

### Input

- **domains** - domains or URLs, one per line.
- **maxContactPages** - contact/about pages to read besides the homepage
  (default 2, included in the price).
- **checkWebsite** / **checkEmail** - turn either half off; DNS only is fast.
- **includeEvidence** - keep the "detected from" evidence per technology.
- **maxDomains**, **concurrency**, **timeoutSecs**.

### How it reads sites

It identifies itself honestly (`LeadEnricherBot`), reads `robots.txt` first
and skips any page it disallows, reads only the homepage and contact/about
pages the site links to, and never submits a form or signs in. A site that
blocks bots or disallows crawling comes back with the reason in
`website_error` and the DNS half still filled in. It does not query WHOIS.

### Use it from code, Claude or ChatGPT

The **API** tab has snippets for Node.js, Python and curl, including
`run-sync-get-dataset-items`. As an MCP tool:

```
https://mcp.apify.com?tools=vital_tuxedo/website-contact-lead-enricher
```

### FAQ

**Does it verify mailboxes over SMTP?** No. It checks that the address's
domain has a working mail server, which removes dead domains, the most common
bounce on small-business lists. A deleted mailbox on a live domain cannot be
seen from DNS.

**Where do the emails come from?** Only from pages the company itself
publishes. Nothing is bought, guessed or pulled from a third-party database.

**Your responsibility.** Contact details on a business website are published
to be used, but laws on emailing them differ (CAN-SPAM, GDPR, CASL). Use the
data lawfully, identify yourself and honour opt-outs.

**What does "Own mail server (often the web host)" mean?** The MX points at
the domain itself (for example `mail.company.com`), which usually means the
web hosting company's bundled email.

### More from this developer

- [Google Ads Transparency Scraper](https://apify.com/vital_tuxedo/google-ads-transparency-scraper) - every ad a competitor runs on Google (text, image and YouTube), with ad copy, landing URLs, dates and countries. No login.

# Actor input Schema

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

Company domains or website URLs, one per line (acme.com, https://www.acme.com/about). Duplicates are removed. Each becomes one dataset row.

## `maxContactPages` (type: `integer`):

Besides the homepage, how many contact/about pages linked from it to read for emails and phones. 0 reads the homepage only. Included in the per-domain price.

## `checkWebsite` (type: `boolean`):

Read the homepage and contact pages: emails, phones, socials, tech stack. Off: DNS only (email provider, SPF, DMARC).

## `checkEmail` (type: `boolean`):

Look up MX, SPF, DMARC and nameservers: email provider, sending services, mail-security issues. Also used to drop addresses whose domain cannot receive mail.

## `includeEvidence` (type: `boolean`):

For every detected technology, include the header, asset URL or tag it was detected from.

## `maxDomains` (type: `integer`):

Stop after this many domains. 0 means no limit. This is also your bill ceiling.

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

Domains processed in parallel.

## `timeoutSecs` (type: `integer`):

Per request.

## Actor input object example

```json
{
  "domains": [
    "urbanwoodgoods.com",
    "asccare.com",
    "elideas.com"
  ],
  "maxContactPages": 2,
  "checkWebsite": true,
  "checkEmail": true,
  "includeEvidence": true,
  "maxDomains": 5000,
  "concurrency": 8,
  "timeoutSecs": 15
}
```

# Actor output Schema

## `leads` (type: `string`):

One row per domain.

## `runSummary` (type: `string`):

Domains asked, analysed, reachable, with an owner email, blocked by robots.txt, and what stopped the run if a cap did.

# 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": [
        "urbanwoodgoods.com",
        "asccare.com",
        "elideas.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("vital_tuxedo/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 = { "domains": [
        "urbanwoodgoods.com",
        "asccare.com",
        "elideas.com",
    ] }

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

```

## MCP server setup

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