# Website Contact Scraper for Emails and Phones (`arjun_code/website-contact-scraper`) Actor

Extract public emails, phone numbers, social profiles and messaging links from website URLs. Discover contact pages, merge duplicate contacts and keep source URLs. Export structured results for company research, CRM enrichment and outreach preparation.

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

## Pricing

from $8.00 / 1,000 website contact 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

Turn website URLs into **public emails, phone numbers and social links with source evidence**. Website Contact Scraper collects published contact details from company, publisher and organization websites, then groups them into one record per unique input for CRM enrichment, supplier research and media outreach.

Start with a website domain or an exact contact-page URL. No code or selectors are needed. Review the sources, then export JSON, CSV or Excel, or retrieve the results through the Apify API.

Pricing is **$8 per 1,000 saved website contact results**, plus $0.00005 per run. Multiple contacts in one result still count as one result. See [Pricing](#pricing) for what is charged and what has no result fee.

### Why use this website contact scraper?

- **Review contacts at their source.** Emails, phones, social profiles and messaging contacts include `sourceUrls` so you can check their context.
- **Keep each website lookup together.** Contacts from selected pages are merged into one record, with repeated contacts deduplicated within that lookup.
- **Choose a focused search.** Read only your supplied contact page, or discover relevant contact, about, imprint and office pages within your page allowance.
- **Collect more than email.** Extract phone and fax numbers, supported social profiles, messaging contacts and contact-form links. Recognized phones can include international formatting and extensions.
- **Understand empty and partial results.** Website-level statuses explain what happened, while available contacts are retained.

This is a public website contact extractor. It does not guess email addresses, verify inbox delivery or confirm contact ownership.

### How to extract contacts from a website

1. Open **Input** and enter one website URL or domain per line.
2. Keep **Maximum pages per website** at `5` for contact discovery, or choose `1` to read only the supplied page.
3. Leave **Phone country hint** on **Automatic detection** unless you know the country needed to interpret local numbers.
4. Select **Start** and open the run's Dataset to review contacts and website statuses.
5. Check the source links, then download your data or use it in an integration.

An exact URL starts at that page, not the website homepage. Try one website first to check the results and budget before submitting a larger batch.

### Input parameters

| Parameter | Default | Description |
| --- | --- | --- |
| `websites` | Required | An array of website URL or domain strings. Paths are preserved. Identical strings are deduplicated after trimming whitespace; different URLs on the same website remain separate inputs. |
| `maxPagesPerWebsite` | `5` | A whole number from `1` to `10`, including the starting page. Use `1` for an exact page or keep `5` to discover related contact pages. Failed additional-page attempts use the allowance too. This limits pages, not contacts. |
| `defaultPhoneCountry` | Automatic detection | An optional phone-number country hint, selected from the dropdown or supplied as a supported two-letter code such as `US`, `GB` or `CN`. Omit it or use `""` for automatic detection. It applies to every website in the run and does not filter websites by country. |

Blank entries are ignored; at least one nonblank website is required. Quoted page counts, decimals, booleans and a `null` country hint are not supported. If the Actor receives an input it cannot use, such as an unsupported phone-country code, it completes successfully with `invalidInput` Dataset notices and `errorCode: "INVALID_INPUT_PARAMETERS"`. The message explains how to correct the input. No websites are requested and no result fees are charged. Empty input never runs a demonstration website.

Some inputs that do not match the input schema can be rejected by Apify before a run starts. Such requests do not create a run or Dataset. For an input notice returned by a started run, the separate Actor start fee can still apply.

Individual malformed website strings receive `invalidInput` rows while valid websites continue. Use HTTP(S) URLs or domains with valid hostnames and ports; encode spaces as `%20`. FTP is not supported.

Keep automatic phone-country detection for mixed-country batches. Choose a hint only when you know the context of local numbers; it is not a location filter or proof of a company's address.

### Output fields and example

The Dataset contains **one row per unique input**, not one row per email or phone. A `websiteContact` row holds contact arrays, including empty arrays when nothing of that type was found. A `websiteStatus` row contains status information without unnecessary contact fields.

| Field | Description |
| --- | --- |
| `recordType` | `websiteContact` for a retrieved-page result; `websiteStatus` for a status-only result, such as an invalid website or an unsaved result after a budget limit. |
| `status` | Per-website outcome. See the status table below. |
| `inputUrl` | Original trimmed input, included when it differs from `websiteUrl` or no final URL is available. |
| `websiteUrl` | Final starting-page URL after redirects, when available. |
| `domain` | Registrable website domain used for grouping, such as `usp.br`. |
| `title` | Page title, preferring the starting page. This is not a verified company name. |
| `emails` | Objects containing `value` and `sourceUrls`. |
| `phones` | Objects containing published `value` and `sourceUrls`, with `e164`, `extension` or `type: "fax"` only when applicable. |
| `socialLinks` | Objects containing `platform`, normalized profile `url` and `sourceUrls`. |
| `messagingLinks` | Objects containing `platform` and `sourceUrls`, with a published `url` and/or an explicitly labeled WhatsApp `value` and recognized `e164` when available. No messaging URL is invented from a number. |
| `contactFormUrls` | Contact-form pages or published embedded-form URLs. These are possible contact methods, not verified sales forms. |
| `errorCode` | Optional machine-readable reason, such as `INVALID_INPUT` or `MAX_CHARGE_REACHED`. Generated by the Actor for programmatic handling. |
| `message` | Optional human-readable explanation or guidance. It can describe an empty or partial result, not only an error. |
| `scrapedAt` | UTC extraction time in ISO 8601 format. |

The default table focuses on status, emails, phones, social links, messaging links, contact forms and website URLs. Download JSON for the full contact objects and their source URLs.

#### Real website contact output

This input corresponds to the real cloud result below. The example uses three selected pages; the default page allowance is five.

```json
{
  "websites": ["https://www.fea.usp.br/contato"],
  "maxPagesPerWebsite": 3,
  "defaultPhoneCountry": ""
}
```

The record is an excerpt from a cloud run on October 22, 2026. It shows one email, phone and social profile from the result, retaining their original values, sources and timestamp. Published information can change.

```json
{
  "recordType": "websiteContact",
  "status": "complete",
  "websiteUrl": "https://www.fea.usp.br/contato",
  "domain": "usp.br",
  "title": "Contato | FEA - USP",
  "emails": [
    {
      "value": "alunofea@usp.br",
      "sourceUrls": [
        "https://www.fea.usp.br/contato/fale-conosco"
      ]
    }
  ],
  "phones": [
    {
      "value": "(11) 3091-5852",
      "e164": "+551130915852",
      "sourceUrls": [
        "https://www.fea.usp.br/contato/fale-conosco"
      ]
    }
  ],
  "socialLinks": [
    {
      "platform": "x",
      "url": "https://x.com/FEAUSPcomunica",
      "sourceUrls": [
        "https://www.fea.usp.br/contato",
        "https://www.fea.usp.br/contato/fale-conosco",
        "https://www.fea.usp.br/internacional/contato"
      ]
    }
  ],
  "messagingLinks": [],
  "contactFormUrls": [],
  "scrapedAt": "2026-10-22T06:15:25.633077Z"
}
```

#### What do `errorCode` and `message` mean?

These are Actor-generated status fields, not contact details taken from the website. Successful, complete results normally omit them.

- `errorCode` helps software distinguish situations. `INVALID_INPUT` means a website string is malformed or unsupported; `INVALID_INPUT_PARAMETERS` means input settings need correction and no lookups were started; `PARTIAL_PAGE_FAILURE` means some selected pages were unavailable; and `MAX_CHARGE_REACHED` means the remaining budget cannot cover another result.
- `message` explains the situation for the person reading the results. A `noContactsFound` row can have a message without an error code. A `partial` row can contain useful contacts alongside its explanation.

Check `status` first. For unexpected results, use the run's **Run summary and page diagnostics** output and include the run ID when requesting support.

#### Supported social and messaging links

The Actor recognizes supported profile, company, channel, community and messaging links published on the selected pages, including these platforms:

| Group | Supported platforms |
| --- | --- |
| Social networks and communities | Facebook, Instagram, X/Twitter, LinkedIn, Threads, Bluesky, Snapchat, Reddit, Pinterest, Mastodon |
| Video and livestreaming | YouTube, TikTok, Twitch, Vimeo, Dailymotion, Rumble, Kick |
| Chinese-language platforms | Weibo, Douyin, Xiaohongshu/RED, Kuaishou, Bilibili, Zhihu, Toutiao, Qzone |
| Russia and CIS | VK, OK, Dzen, Rutube |
| Regional blogs and communities | Pixiv, Naver Blog, Naver Cafe, ShareChat, Moj |
| Professional and creator pages | GitHub, GitLab, Behance, Dribbble, Flickr, Quora, Medium, Tumblr, Patreon, Substack |
| Audio and creator link pages | Spotify, SoundCloud, Linktree, Beacons |
| Messaging contacts and invitations | WhatsApp, Telegram, Discord, Messenger, Signal, LINE, KakaoTalk, Viber, WeChat, WeCom, QQ, Slack, Zalo, Threema |

Individual posts, videos and share links are not profile results. This is not an account-search or social-profile-details service. URL coverage varies; not every regional, custom-domain or shortened link is supported. Messaging invitations may expire or require approval.

#### Website statuses and troubleshooting

| Status | Meaning |
| --- | --- |
| `complete` | At least one supported contact or form candidate was found and no selected-page errors were recorded. This does not mean every contact on the entire website was found. |
| `partial` | Some selected pages could not be accessed completely. Available extracted contacts are retained. |
| `noContactsFound` | Selected pages were retrieved without recorded errors, but no supported contacts or form candidates were found. |
| `invalidInput` | A website string or input setting needs correction. Check `errorCode` and `message`. Input-setting notices complete the run successfully without starting website lookups. |
| `requestBlocked` | Access to the starting page was restricted. |
| `unavailable` | The starting page could not be accessed or is not a supported website page. Check `errorCode` and `message`. |
| `chargeLimitReached` | The spending limit does not allow another paid result. This input has no saved contact result and no result fee was charged for it. |

Handled website-level problems produce Dataset rows rather than silently disappearing. The run completes successfully even if every lookup returns an `unavailable` notice. A successful run means inputs were handled, not that every website yielded contacts: check each row's `status`, `errorCode` and `message`. Unsuccessful lookup notices have no result fee; the Actor start fee can still apply. Input-setting problems handled by the Actor also return successful runs with clear Dataset notices. Known input websites each receive a notice; when no website list can be read, one general input notice is returned. Unexpected application errors or a platform timeout can still prevent a run from completing.

`complete` describes the selected pages, not full-site coverage. Empty arrays do not prove that contacts are absent elsewhere. Processing time varies with the website and page allowance.

### API and automation

Use the website contact scraper through the Apify API from Python, JavaScript or an HTTP step in n8n, Make or Zapier. Apify also provides scheduling, integrations and monitoring tools for recurring workflows.

Start a run with an Apify token:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/arjun_code~website-contact-scraper/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"websites":["https://www.fea.usp.br/contato"],"maxPagesPerWebsite":3,"defaultPhoneCountry":""}'
```

The start response contains the run ID. Wait for that run to finish, then use its `defaultDatasetId` to fetch the results:

```bash
curl \
  "https://api.apify.com/v2/datasets/DATASET_ID/items?format=json" \
  -H "Authorization: Bearer $APIFY_TOKEN"
```

Replace `DATASET_ID` with the run's actual Dataset ID and keep tokens private. Download JSON to preserve nested contact objects and source URLs, or export CSV or Excel from Console. For one spreadsheet row per contact, flatten the arrays in your downstream workflow.

### Pricing

Pay per saved website contact result, not per email, phone, page or request. You do not need to purchase 1,000 results at once.

| Event | Price |
| --- | --- |
| Website contact result (`website-contact-result`) | **$0.008 each, or $8 per 1,000 results** |
| Actor start (`apify-actor-start`) | **$0.00005 per run** at the supported 512 MB memory setting |

Standard Actor runs do not add platform usage charges to these event prices. Check the **Pricing** tab for the active rates before starting a run.

#### What counts as a paid result?

A saved website result is charged once when it contains at least one public **email, phone number, social profile or messaging contact**. Many contacts in that result still count as one paid result. `partial` results are charged on the same basis when at least one of these contact types was saved.

Invalid, blocked, unavailable, empty and spending-limit status rows have no result fee. A result containing only contact-form links also has no result fee. The separate start fee can still apply when a run starts but returns no paid results.

Identical trimmed inputs are deduplicated. Different URLs on the same domain are separate lookups and can produce separate paid results.

#### How much does website contact scraping cost?

A run saving 10 paid website results costs **$0.08005**: `10 × $0.008 + $0.00005` for one start. A run saving 100 paid results costs **$0.80005**. Extra contacts within those results do not increase the result fee.

The minimum selectable run budget is **$0.01**, not a minimum charge. When the remaining budget cannot cover another paid result, further lookups stop and unsaved inputs receive `chargeLimitReached` rows without result fees. Available saved results remain in the Dataset. Requests already in progress may have consumed resources before cancellation.

### FAQ

#### Can I scrape only a contact page?

Yes. Enter its exact URL and set `maxPagesPerWebsite` to `1`. The input path is preserved, and no additional website pages are selected.

#### Can I use it as a website email finder?

Yes. Public emails, phones and social contacts can be returned together. This is not an email-guessing or deliverability-verification service.

#### Why is a published phone number missing or not in international format?

Some local numbers need the correct country context. Keep automatic detection for mixed-country batches, or choose `defaultPhoneCountry` when you know the region. A published number may appear in `value` without `e164`. Recognized extensions and explicit fax markers are included when available.

#### Does the phone-country hint filter websites by country?

No. It helps interpret local phone numbers across the run. It does not search for websites, translate pages or verify an organization's location.

#### Does it process every page on a website?

No. It reads the supplied page and selected relevant links within `maxPagesPerWebsite`. More pages can reveal additional contacts but may take longer. For large batches, check the run timeout and spending limit and split inputs when needed.

#### Why is my result partial or empty?

Some pages may be unavailable, restricted or missing supported contacts. Review the status and sources. Increasing the page allowance can help when details are on another contact page, but it does not guarantee more contacts or access to restricted pages.

#### Can it read contacts hidden in images, QR codes or login-only pages?

Not comprehensively. Image-only contacts, QR codes and sign-in-only pages may not produce results. Bare WeChat or QQ IDs without supported contact links, and app-only contact methods, are not comprehensively covered.

### Limitations and responsible use

Coverage is limited to the selected public pages and supported contact types. The Actor does not send emails, call numbers, submit forms, score leads or perform outreach. Access to every website and full-site completeness are not guaranteed.

A contact may belong to a department, employee, group company or external service provider. Source links and phone formatting do not verify ownership or reachability. Published details can be outdated, and false positives remain possible; review the sources before use.

Respect website terms and applicable privacy and communication requirements. Publicly available contact information is not consent to unsolicited messages. This is an independent extraction tool, not an endorsement by the websites or social platforms it recognizes.

### Support

Open an issue on this Actor's Apify page with the **run ID**, affected website, input settings and the contact or status you want reviewed. Include relevant source URLs and the page information from the run summary when available. Do not post API keys, cookies, passwords or private contact information.

For other extraction tools, browse [Arjun AI's Actors](https://apify.com/arjun_code).

# Actor input Schema

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

One HTTP(S) website URL or domain per line; API input must be an array of strings. Blank lines are ignored, but at least one nonblank website is required. A URL with a path starts at that page. Invalid or unavailable websites receive informative Dataset rows. FTP is not supported. If a temporary processing issue prevents every valid lookup from completing, the run can fail after saving those status rows.

## `maxPagesPerWebsite` (type: `integer`):

Enter a whole number from 1 to 10, without quotes or decimals. This limits selected pages per input, including the input page. Keep 5 for contact discovery, or use 1 for an exact contact page. Failed page attempts also use the allowance. This is a page limit, not a contact-count limit.

## `defaultPhoneCountry` (type: `string`):

Choose Automatic detection (recommended), or a country/region to interpret local phone numbers. The hint applies to the whole run; it is not a website country filter. Automatic detection uses supported page metadata, then supported country-specific domains. API input: omit this field or use an empty string for automatic detection, or use a supported two-letter region code. Do not use null or a number.

## Actor input object example

```json
{
  "websites": [
    "https://www.python.org"
  ],
  "maxPagesPerWebsite": 5,
  "defaultPhoneCountry": ""
}
```

# 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 = {
    "websites": [
        "https://www.python.org"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arjun_code/website-contact-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 = { "websites": ["https://www.python.org"] }

# Run the Actor and wait for it to finish
run = client.actor("arjun_code/website-contact-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 '{
  "websites": [
    "https://www.python.org"
  ]
}' |
apify call arjun_code/website-contact-scraper --silent --output-dataset

```

## MCP server setup

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