# Website Contact Scraper: Emails, Phones, Socials | $1/1K (`one_dollar_store/website-emails-phones-scraper`) Actor

$1 per 1,000 results, platform usage included. Public emails, phone numbers and social profile links from company websites: the page you give plus its contact and about pages. One row per website, plain HTTP, robots.txt honoured, no login.

- **URL**: https://apify.com/one_dollar_store/website-emails-phones-scraper.md
- **Developed by:** [One Dollar Store](https://apify.com/one_dollar_store) (community)
- **Categories:** Lead generation, Marketing
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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: public emails, phone numbers and social links

**$1 per 1,000 websites. Platform usage included. No rental, no minimum.**

Give it company websites and get one row per site with the public contact details it finds: email addresses, phone numbers (in international format) and links to social profiles. It reads the page you give plus the contact, about, team and legal pages linked from it, using plain HTTP and no browser.

[Open it in Apify Console](https://console.apify.com/actors/JFLJvQTJFExfI30cL/input)

### What it costs

| Websites | Price |
|---|---|
| 1,000 | $1.00 |
| 10,000 | $10.00 |
| 100,000 | $100.00 |

Plus $0.001 per run. One flat price, platform usage included, so there is no separate compute or proxy bill. A weekly run of 500 websites costs about $0.50, or about $2 a month.

### Why use it

- **One flat price per website**, however many pages are read on it (up to the page limit you set).
- **Clean, merged rows.** Contacts from all the pages read on a site are merged and de-duplicated into one row. Emails on the site's own domain come first.
- **Phone numbers are validated** and returned in international format (`+1 415 555 2671`) and as E.164 (`+14155552671`). Numbers written without a country code are read using your default country, the page language or the domain ending.
- **Hidden-in-plain-sight emails are found**: `mailto:` links, text such as `name [at] site [dot] com`, JSON-LD data, and the addresses Cloudflare scrambles on a page.
- **Only pages a site allows.** robots.txt is checked for every page. Sites that cannot be read are not charged and are listed in the run summary.

### Quick start

1. Open the actor, add one website per line, press **Start**.
2. Or paste this input:

```json
{ "urls": ["https://www.example.com"], "maxPagesPerSite": 3, "maxItems": 10 }
```

3. Download the results as JSON, CSV or Excel, or read them through the API.

### Use it from code

```bash
curl -X POST "https://api.apify.com/v2/acts/one_dollar_store~website-emails-phones-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.example.com"], "maxPagesPerSite": 3, "maxItems": 10}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("one_dollar_store/website-emails-phones-scraper").call(
    run_input={"urls": ["https://www.example.com"], "maxPagesPerSite": 3, "maxItems": 10}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["domain"], item.get("emails"), item.get("phones"))
```

### Use it from an AI assistant

Claude, ChatGPT, Cursor and other assistants can call this actor through Apify's MCP server. Add it with this server address:

```
https://mcp.apify.com?tools=one_dollar_store/website-emails-phones-scraper
```

Then ask, for example: "Find the contact emails and phone numbers on these 20 company websites."

### Switching from another contact details scraper

Most contact scrapers take start URLs, a crawl depth and a page limit. Here they are `urls` and `maxPagesPerSite`, with `maxItems` for the total. The core fields are `domain`, `emails`, `phones` and one field per social network (`facebook`, `instagram`, `linkedin`, `twitter`, `youtube` and more).

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `urls` | list of text | none | Company websites, one per line: a bare domain (`example.com`) or a full address. |
| `maxPagesPerSite` | number | 4 | The most pages to read per website, including the address you give (1 to 10). The others are contact, about, team or legal pages found in its links. |
| `defaultCountry` | text | empty | Optional two letter country code (`GB`, `IN`, `US`) used to read phone numbers written without a country code. |
| `maxItems` | number | 100 | The run stops after this many websites. Minimum 10. |

One website gives one row. If you list two addresses on the same site, the first is used and the second is listed as skipped. A bare domain is tried over https first, then http, then with `www.` added.

### Output fields

| Field | Meaning |
|---|---|
| `url`, `domain`, `finalUrl` | The address as you gave it, the host name without `www.`, and the page it landed on. |
| `emails` | Email addresses found, up to 50. Addresses on the site's own domain come first. |
| `phones`, `phonesE164` | Valid phone numbers in international format and in E.164 format, up to 30. |
| `facebook`, `instagram`, `linkedin`, `twitter`, `youtube`, `tiktok`, `pinterest`, `github`, `telegram`, `whatsapp`, `threads`, `snapchat`, `discord` | The first profile link found for each network. Share buttons and post links are ignored. |
| `emailCount`, `phoneCount` | Totals found. |
| `pagesChecked`, `pageUrls` | How many pages were read and which. |
| `scrapedAt` | Time of the lookup in UTC. |

A website that loads fine but shows no contact details still gives a row, with empty lists. That tells you the site was read and had nothing public to find.

### Sample results

Real rows from a test run on 9 October 2026.

```json
[
  {"url": "https://www.eff.org", "domain": "eff.org", "finalUrl": "https://www.eff.org/", "emails": ["info@eff.org", "press@eff.org", "membership@eff.org", "activist@eff.org", "extension-devs@eff.org", "hr@eff.org"], "phones": ["+1 510 243 8020", "+1 415 436 9333 ext. 177", "+1 415 436 9993"], "phonesE164": ["+15102438020", "+14154369333", "+14154369993"], "facebook": "https://www.facebook.com/eff", "instagram": "https://www.instagram.com/efforg", "linkedin": "https://www.linkedin.com/company/EFF", "tiktok": "https://www.tiktok.com/@efforg", "threads": "https://www.threads.net/@efforg", "emailCount": 6, "phoneCount": 3, "pagesChecked": 3, "pageUrls": ["https://www.eff.org/", "https://www.eff.org/about/contact", "https://www.eff.org/press/contact"], "scrapedAt": "2026-10-09T03:57:30.740Z"},
  {"url": "https://www.apache.org", "domain": "apache.org", "finalUrl": "https://www.apache.org/", "emails": ["press@apache.org", "fundraising@apache.org", "apache@apache.org"], "phones": [], "phonesE164": [], "github": "https://github.com/apache", "linkedin": "https://www.linkedin.com/company/the-apache-software-foundation", "youtube": "https://www.youtube.com/c/TheApacheFoundation", "twitter": "https://twitter.com/TheASF", "emailCount": 3, "phoneCount": 0, "pagesChecked": 3, "pageUrls": ["https://www.apache.org/", "https://www.apache.org/foundation/contact", "https://www.apache.org/foundation/"], "scrapedAt": "2026-10-09T03:57:30.889Z"},
  {"url": "https://www.python.org", "domain": "python.org", "finalUrl": "https://www.python.org/", "emails": ["python-announce@python.org", "psf@python.org", "webmaster@python.org"], "phones": [], "phonesE164": [], "linkedin": "https://www.linkedin.com/company/python-software-foundation", "twitter": "https://twitter.com/ThePSF", "emailCount": 3, "phoneCount": 0, "pagesChecked": 3, "pageUrls": ["https://www.python.org/", "https://www.python.org/about/help/", "https://www.python.org/about/"], "scrapedAt": "2026-10-09T03:57:30.992Z"}
]
```

### Limits

- **Public pages only, no JavaScript run.** The actor reads the HTML a site sends. Emails and phone numbers that a page adds with scripts after loading, or shows only inside images, are not found.
- **It reads a few pages, not the whole site.** It reads the address you give plus up to `maxPagesPerSite` minus one contact, about, team or legal pages that the first page links to. If the first page has no such link, it tries `/contact` and `/contact-us`. Contacts that sit on other pages are missed.
- **robots.txt is honoured** for every page. A page it does not allow is not read, and a site whose own address is not allowed gives no row. If robots.txt cannot be read because of a server error, the page is skipped too.
- **Sites that block cloud servers** or show a bot check page are skipped. The actor does not try to get past a check.
- **Not every address is a person.** Results include shared addresses such as `info@` and `press@`, and mailing list addresses. The actor does not check whether a mailbox exists.
- **Phone numbers are checked for a valid format**, not for whether they are in service. A number written without a country code is read using your `defaultCountry`, then the page language and domain ending, then the US. A wrong guess can drop a valid number or read a different one.
- **Only HTML pages with a 2xx status** are read. Redirects are followed, up to 5. Pages are read up to 2 MB.
- **Free plan runs** return the first 10 results.
- Every run writes a `RUN_SUMMARY` record with counts and the list of skipped websites and why.

### FAQ

**How much does it cost to scrape contact details from 10,000 websites?**
$10, plus $0.001 for each run.

**Do I pay for compute or proxy on top?**
No. Platform usage is included in the $1 per 1,000 price. The actor uses no proxy.

**Does it find emails hidden as `name [at] site [dot] com`?**
Yes, along with `mailto:` links, JSON-LD data and the addresses Cloudflare scrambles on a page.

**Does it verify that an email works?**
No. It returns addresses published on the site. Check them with an email verification service before you send anything.

**Why is a website missing from my results?**
The run summary lists every website that gave no row, with the reason: blocked by robots.txt, an error status, a bot check page, an address that could not be reached, or a duplicate site.

**Is it legal?**
The actor reads public pages that a site sends to any visitor, with no login, and respects robots.txt. Email addresses and phone numbers can be personal data under laws such as GDPR and CAN-SPAM. How you use them is up to you: follow the law where you and your contacts live, including the rules for marketing messages.

**Is this an official product of the sites it reads?**
No. It is an independent tool and is not affiliated with or endorsed by any site.

**Do I need an Apify account?**
Yes. Actors run from an Apify account, and a free account is enough to start: it includes $5 of platform usage every month. You can [sign up here](https://www.apify.com?fpr=ymnoit). This is a referral link and it costs you nothing.

**Is there a limit on the free plan?**
Yes. A run started from a free plan account delivers the first 10 results, then ends with a message and a link to the plans. Any paid plan returns everything you ask for, up to your own limit. The price per result is the same on every plan.

### More from The One Dollar Store

Every actor here costs $1 per 1,000 results, platform usage included. See the whole store: https://apify.com/one_dollar_store

**Website tools**

- [Website Technology Detector](https://apify.com/one_dollar_store/website-technology-detector): Frameworks, CMS, analytics and ad tags on any site.
- [Website to Markdown Converter](https://apify.com/one_dollar_store/website-to-markdown-converter): Clean Markdown text from any web page.
- [SEO Page Audit](https://apify.com/one_dollar_store/seo-page-audit-scraper): On-page SEO checks for any URL.
- [PDF Text Extractor](https://apify.com/one_dollar_store/pdf-text-extractor-scraper): Text and page data from PDF links.

### Changelog

- October 2026: first release.

# Actor input Schema

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

Company websites, one per line: a bare domain (example.com) or a full address. The actor reads that page and a few contact or about pages on the same site. Pages that robots.txt does not allow are skipped. Local and private addresses are refused.

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

The most pages to read on each website: the address you give plus contact, about, team or legal pages found in its links. 1 reads only the page you give.

## `defaultCountry` (type: `string`):

Optional. A two letter country code such as GB, IN or US, used to read phone numbers that are written without a country code. If empty, the country is guessed from the page language and the domain ending, then US. Numbers with a + country code are always read correctly.

## `maxItems` (type: `integer`):

The run stops after this many results (one per website). Minimum 10.

## Actor input object example

```json
{
  "urls": [
    "https://www.fsf.org",
    "https://www.eff.org",
    "https://www.debian.org",
    "https://www.apache.org",
    "https://www.python.org"
  ],
  "maxPagesPerSite": 3,
  "maxItems": 10
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "urls": [
        "https://www.fsf.org",
        "https://www.eff.org",
        "https://www.debian.org",
        "https://www.apache.org",
        "https://www.python.org"
    ],
    "maxPagesPerSite": 3,
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("one_dollar_store/website-emails-phones-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 = {
    "urls": [
        "https://www.fsf.org",
        "https://www.eff.org",
        "https://www.debian.org",
        "https://www.apache.org",
        "https://www.python.org",
    ],
    "maxPagesPerSite": 3,
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("one_dollar_store/website-emails-phones-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 '{
  "urls": [
    "https://www.fsf.org",
    "https://www.eff.org",
    "https://www.debian.org",
    "https://www.apache.org",
    "https://www.python.org"
  ],
  "maxPagesPerSite": 3,
  "maxItems": 10
}' |
apify call one_dollar_store/website-emails-phones-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,one_dollar_store/website-emails-phones-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/JFLJvQTJFExfI30cL/builds/XQrXaNXv7MHvLO81X/openapi.json
