# Website Contact Scraper: Emails, Phones, Socials (`luzivanlt/website-contacts-scraper`) Actor

Emails, phones and social links from company websites, one row per website with the page each came from. $2 per 1,000 websites. Priced per website, not per page; reads Google Maps results.

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

## Pricing

$2.00 / 1,000 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: Emails, Phones, Socials

Get the emails, phone numbers and social profile links of company websites,
one row per website, priced per website rather than per page: up to 30 pages
of a site cost the same as one. Each contact comes with the page it was found
on. Feed it website links, or the results of a Google Maps run.

Tested daily. Latest check: 2 of 2 websites read, up to 10 pages each, with
emails on both. In a wider check, 17 of 20 well-known websites were read in
one minute; the other 3 refuse automated visits.

### What does this Actor do?

You give it website links. For each website it reads the page you gave, then
the site's own pages its links lead to, contact, imprint and about pages
first, up to your page limit and link depth. It collects every email, phone
number and social profile link it finds and saves one row per website.

Each website comes with:

- every email address on its pages, including addresses hidden by
  Cloudflare's email protection
- phone numbers from call links and from the site's business details, and
  numbers written in the text, kept apart because a number in the text can be
  something else
- links to its profiles on LinkedIn, X/Twitter, Facebook, Instagram, YouTube,
  TikTok, Pinterest, Threads, Telegram, WhatsApp, Discord, Snapchat, Reddit
  and GitHub
- the company name, the home page's title, description and language, and the
  postal addresses the site publishes about itself
- its contact page, the pages read, and where each contact was found

### Why use it?

- **One price per website.** You pay for a website, not for each page read,
  so reading more pages to find a contact page costs nothing extra.
- **Contact pages first.** Contact, imprint ("Impressum"), about and legal
  pages are read before the rest, in many languages, so a small page limit
  still finds most contacts.
- **Straight from Google Maps.** Give the dataset of a Google Maps run, or
  paste its rows, and every place's website is read.
- **Use the data anywhere.** Download as JSON, CSV or Excel, or send it to
  your own tools through Apify's API and integrations.

### How to use it

1. Paste **Website links**, one per website, or give the **Dataset id from a
   Google Maps run**, or paste **Rows from a Google Maps run**.
2. Optionally, set **Max pages per website** and **Max link depth**.
3. Click **Start**. When the run ends, open the **Output** tab to view the
   websites or download them.

Run it with no input to see a sample: two websites, charged as usual.

#### Links that work

A company's own website, as a full link or a bare domain
(`https://www.example.com`, `example.com`), or any page of it. Only that
host's pages are read, with or without `www.`. Social networks, search
engines, maps and marketplaces are refused before anything is read.

### Input

| Field                                               | What it does                                             | Default |
| --------------------------------------------------- | -------------------------------------------------------- | ------- |
| **Website links** (`startUrls`)                     | Company websites, one row each                           | —       |
| **Dataset id from a Google Maps run** (`datasetId`) | Reads each place's website from a run's dataset          | —       |
| **Rows from a Google Maps run** (`datasetRows`)     | The same, from rows pasted as JSON                       | —       |
| **Max pages per website** (`maxPages`)              | Pages read per website, the start page included, 1 to 30 | `10`    |
| **Max link depth** (`maxDepth`)                     | Links away from the start page, 0 to 5; 0 reads it alone | `2`     |

Example input:

```json
{
  "startUrls": [{ "url": "https://www.example.com" }, { "url": "example.org" }],
  "maxPages": 10,
  "maxDepth": 2
}
```

### Output

One row per website. An example, with made-up values and lists cut:

```json
{
  "domain": "example-bakery.com",
  "website": "https://www.example-bakery.com/",
  "company_name": "Example Bakery Ltd",
  "title": "Example Bakery | Bread & Cakes",
  "description": "Fresh bread and cakes, baked daily.",
  "language": "en",
  "emails": ["hello@example-bakery.com", "orders@example-bakery.com"],
  "phones": ["+1 555 010 0199"],
  "phones_uncertain": ["+44 20 7946 0958"],
  "linkedins": ["https://www.linkedin.com/company/example-bakery"],
  "twitters": [],
  "facebooks": ["https://www.facebook.com/examplebakery"],
  "instagrams": ["https://www.instagram.com/examplebakery"],
  "youtubes": [],
  "tiktoks": [],
  "pinterests": [],
  "threads": [],
  "telegrams": [],
  "whatsapps": ["https://wa.me/15550100199"],
  "discords": [],
  "snapchats": [],
  "reddits": [],
  "githubs": [],
  "addresses": [
    {
      "full": "1 Example Street, Springfield, IL, 62701, US",
      "street": "1 Example Street",
      "city": "Springfield",
      "region": "IL",
      "postal_code": "62701",
      "country": "US"
    }
  ],
  "contact_page_url": "https://www.example-bakery.com/contact",
  "pages_crawled": ["https://www.example-bakery.com/", "https://www.example-bakery.com/contact"],
  "pages_count": 2,
  "sources": [
    {
      "type": "email",
      "value": "hello@example-bakery.com",
      "page_url": "https://www.example-bakery.com/"
    }
  ]
}
```

| Field              | What it holds                                                                                                  |
| ------------------ | -------------------------------------------------------------------------------------------------------------- |
| `domain`           | The website's host, without `www.`                                                                             |
| `website`          | The link the website was read from                                                                             |
| `company_name`     | The name the site gives itself, when it states one                                                             |
| `title`            | The start page's title                                                                                         |
| `description`      | The start page's description                                                                                   |
| `language`         | The start page's language                                                                                      |
| `emails`           | Every email address found, in lower case                                                                       |
| `phones`           | Phone numbers from call links and the site's business details                                                  |
| `phones_uncertain` | Numbers written in the page text that look like phone numbers                                                  |
| `linkedins`        | LinkedIn company, school and profile links                                                                     |
| `twitters`         | X/Twitter profile links                                                                                        |
| `facebooks`        | Facebook Page, profile and group links                                                                         |
| `instagrams`       | Instagram profile links                                                                                        |
| `youtubes`         | YouTube channel links                                                                                          |
| `tiktoks`          | TikTok profile links                                                                                           |
| `pinterests`       | Pinterest profile links                                                                                        |
| `threads`          | Threads profile links                                                                                          |
| `telegrams`        | Telegram links                                                                                                 |
| `whatsapps`        | WhatsApp chat and channel links                                                                                |
| `discords`         | Discord invite links                                                                                           |
| `snapchats`        | Snapchat profile links                                                                                         |
| `reddits`          | Reddit community and user links                                                                                |
| `githubs`          | GitHub links                                                                                                   |
| `addresses`        | Postal addresses the site publishes about itself: `full`, `street`, `city`, `region`, `postal_code`, `country` |
| `contact_page_url` | The first contact or imprint page read                                                                         |
| `pages_crawled`    | Every page read, in order                                                                                      |
| `pages_count`      | How many pages were read                                                                                       |
| `sources`          | Each contact once, with its `type`, `value` and the `page_url` it was first found on                           |

### How much does it cost?

No surcharges and no paid options; an item that fails is not charged.

You pay for what you get:

- **$2 per 1,000 websites** ($0.002 a website), however many pages of it are
  read. Each website is charged once per run, even when two of your links
  lead to it.
- No start fee, and nothing per page.

So 500 websites cost $1. A website that cannot be read is not charged. If you
set a maximum cost for a run, the Actor stops when it reaches it and keeps
every website saved so far.

### How long does a run take?

About 20 to 40 seconds for two websites; 20 websites of up to 10 pages each in
about a minute.

### Good to know

- **Pages are read as the server sends them.** A site that builds its pages
  in the browser with JavaScript may show few or no contacts.
- **One host per website.** Links to other hosts, subdomains included, are
  not followed; give a subdomain as its own link.
- **Numbers in the text are kept apart** (`phones_uncertain`): a number that
  looks like a phone can be an order number or a fax.
- **A website that cannot be read** (it does not answer, refuses, its start
  page is not found, or one of its pages keeps refusing) is reported in the
  run's status and not charged; the other websites carry on. The run fails
  only when none could be read. If a website fails this way, a lower **Max
  pages per website** often reads it.
- **A website found twice** in your links, or in a Google Maps dataset, is
  read and charged once.

### More Actors from us

- [AliExpress Product Scraper: Search Results & Prices](https://apify.com/luzivanlt/aliexpress-products-scraper): Every product of an AliExpress search to its last page, with exact sold counts and ratings.
- [Amazon Product Scraper: Search Results & Prices](https://apify.com/luzivanlt/amazon-products-scraper): Every product on Amazon's search pages to the last page, sponsored marked, with monthly sales.
- [App Store Reviews Scraper: All Reviews, Not 500](https://apify.com/luzivanlt/app-store-reviews-scraper): Every written App Store review of an app, not just the newest 500.
- [Woolworths Coles ALDI Scraper: AU Grocery Prices](https://apify.com/luzivanlt/au-supermarkets-scraper): Woolworths, Coles and ALDI products for any search in one run, every results page to the end.
- [Bilibili Video Scraper: Search & Channel Videos](https://apify.com/luzivanlt/bilibili-videos-scraper): Bilibili videos from searches or whole channels, each with its full detail page's data.
- [Career Site Jobs Scraper: Workday, Greenhouse, Lever](https://apify.com/luzivanlt/career-site-jobs-scraper): Every job on Greenhouse, Lever, Ashby, SmartRecruiters and Workday boards, checked against each board's count.

### Use it from an AI agent or the API

One call starts a run and returns its items (Apify waits up to 300 seconds; for a
longer run, start it with `POST /v2/acts/…/runs` and read its dataset):

```text
POST https://api.apify.com/v2/acts/luzivanlt~website-contacts-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>
```

With the sample input as its JSON body:

```json
{
  "startUrls": [
    {
      "url": "https://www.eff.org"
    },
    {
      "url": "https://www.python.org"
    }
  ],
  "maxPages": 10,
  "maxDepth": 2
}
```

Its inputs, in the order of the input form:

| Input                                           | What it takes                                                                                                                                      | Default |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `startUrls` (Website links)                     | Links to company websites, one website per link, e.g. https://www.eff.org or eff.org.                                                              | —       |
| `datasetId` (Dataset id from a Google Maps run) | The id of a run's dataset from Google Maps Scraper (compass/crawler-google-places), so you do not copy websites by hand.                           | —       |
| `datasetRows` (Rows from a Google Maps run)     | Rows pasted from Google Maps Scraper (compass/crawler-google-places) results, as a JSON list, when its dataset id is not readable by your account. | —       |
| `maxPages` (Max pages per website)              | Read at most this many pages of each website, the start page included, from 1 to 30.                                                               | `10`    |
| `maxDepth` (Max link depth)                     | How many links away from the start page to go, from 0 to 5.                                                                                        | `2`     |

Apify's MCP server, `https://mcp.apify.com`, lists it as `luzivanlt/website-contacts-scraper`. To give an
AI agent this Actor as a tool, add the server to the agent's MCP config:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=luzivanlt/website-contacts-scraper"
    }
  }
}
```

The client signs in with OAuth, or add `"headers": { "Authorization": "Bearer <APIFY_TOKEN>" }`.

Charged per website delivered; the sample input above costs at most $0.01.

### FAQ

**Can I feed it Google Maps results?**
Yes. Give the dataset id of a Google Maps Scraper run (or paste its rows), and
each place's `website` is read. Places without a website are skipped and
counted in the run's status.

**Does it verify emails?**
No. It returns the addresses the website shows.

**Can I run it from my own code, on a schedule, or with other apps?**
Yes. Like any Apify Actor, you can start it through the Apify API, run it on a
schedule, and connect it to other apps with Apify's integrations and webhooks.

### Is it legal to scrape contact details?

This Actor reads only pages anyone can open. Contact details of a person, such
as a named email address or a direct phone number, are personal data. Check
the laws that apply to you (such as the GDPR, the CCPA or anti-spam laws)
before you use the data, especially for unsolicited messages. If you are
unsure, ask a lawyer.

### Support

Found a problem or missing a feature? Open an issue on the **Issues** tab and
include the run's link.

# Changelog

This Actor's version history is a separate document: https://apify.com/luzivanlt/website-contacts-scraper/changelog.md

# Actor input Schema

## `startUrls` (type: `array`):

Links to company websites, one website per link, e.g. https://www.eff.org or eff.org. Each website is read from that page through its own pages, contact, imprint and about pages first; links to other websites are not followed. Social networks, search engines and marketplaces are refused before anything is read. Two links to the same website give one row. Required unless you give a dataset id or rows below.

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

The id of a run's dataset from Google Maps Scraper (compass/crawler-google-places), so you do not copy websites by hand. Every row's website is read; rows without one are skipped and counted in the status. Reads up to 10,000 rows. Example: your run's dataset id, from its Storage tab (a username~name or a dataset link works too).

## `datasetRows` (type: `array`):

Rows pasted from Google Maps Scraper (compass/crawler-google-places) results, as a JSON list, when its dataset id is not readable by your account. Each row's website is read; rows without one are skipped. Reads up to 10,000 rows. Example: \[{"website": "https://www.eff.org/"}]

## `maxPages` (type: `integer`):

Read at most this many pages of each website, the start page included, from 1 to 30. Default 10. Contact, imprint and about pages are read first, so a small number still finds most contact details. You pay per website, not per page.

## `maxDepth` (type: `integer`):

How many links away from the start page to go, from 0 to 5. Default 2: the start page's links, and the links on those pages. 0 reads the start page only.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.eff.org"
    },
    {
      "url": "https://www.python.org"
    }
  ],
  "maxPages": 10,
  "maxDepth": 2
}
```

# Actor output Schema

## `websites` (type: `string`):

Every website the run read, one row per website.

## `allFields` (type: `string`):

The same rows with every field.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.eff.org"
        },
        {
            "url": "https://www.python.org"
        }
    ],
    "maxPages": 10,
    "maxDepth": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("luzivanlt/website-contacts-scraper").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 = {
    "startUrls": [
        { "url": "https://www.eff.org" },
        { "url": "https://www.python.org" },
    ],
    "maxPages": 10,
    "maxDepth": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("luzivanlt/website-contacts-scraper").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 '{
  "startUrls": [
    {
      "url": "https://www.eff.org"
    },
    {
      "url": "https://www.python.org"
    }
  ],
  "maxPages": 10,
  "maxDepth": 2
}' |
apify call luzivanlt/website-contacts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,luzivanlt/website-contacts-scraper"
        }
    }
}
```

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/zHnsgqhOyLBt714a0/builds/EtXUdNjD2ccG7ROio/openapi.json
