# Website Contact Extractor: Emails, Phones, Socials (`ailu_studio/website-contact-extractor`) Actor

Extract emails, phone numbers (E.164), social profiles, contact page and address from company websites. Crawls homepage plus contact, about, team and imprint pages, obeys robots.txt. One row per website.

- **URL**: https://apify.com/ailu_studio/website-contact-extractor.md
- **Developed by:** [Ailu Studio](https://apify.com/ailu_studio) (community)
- **Categories:** Lead generation, Marketing, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 website scanneds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Extractor: Emails, Phone Numbers, Social Links and Addresses from Company Websites

Give it a list of company websites and get one clean row per website: email addresses, phone numbers normalised to E.164, LinkedIn / X (Twitter) / Facebook / Instagram / YouTube / TikTok / GitHub profiles, the contact page URL, the company name and the postal address from schema.org JSON-LD. The Actor fetches the homepage and up to 8 contact-like pages on the same domain (contact, imprint/Impressum, about, team, support, legal, privacy), reads robots.txt first and never submits forms or logs in. Unlike per-page priced contact scrapers, you pay **once per website**, whatever number of pages was scanned, and every item comes with the pages it was found on (`foundOn`). No browser and no proxy are needed, so runs are fast and cheap.

### Use cases

- B2B lead research: turn a list of company domains into generic contact emails (info@, sales@, support@) and phone numbers.
- CRM enrichment: add LinkedIn company pages, X handles and headquarters addresses to existing accounts.
- Supplier and partner directories: collect imprint (Impressum) contact data of European companies.
- Data quality: check which websites still publish a contact page, an email or a phone number.
- AI agents: a single call returns structured contact data instead of raw HTML.

### Input

| Field | Default | Meaning |
|---|---|---|
| `websites` | `["https://apify.com", "https://www.python.org"]` | Company websites, one per line. Bare domains are accepted (`acme.com`). Duplicates by host are removed. |
| `maxPagesPerSite` | `8` | Contact-like pages fetched per website in addition to the homepage (0 to 19), best candidates first. |
| `includePhones` | `true` | Extract phone numbers (tel: links, JSON-LD, text next to a phone keyword). |
| `includeSocialLinks` | `true` | Extract social profile links. |
| `sameDomainEmailsOnly` | `false` | Keep only emails on the website's own domain in `emails`. |
| `timeoutSecs` | `20` | Timeout per page. |
| `maxConcurrency` | `3` | Websites crawled in parallel (pages of one site are fetched one by one). |
| `maxItems` | `100` | Maximum charged rows (websites whose homepage loaded). |

Example: `{ "websites": ["stripe.com", "https://www.hetzner.com"], "maxPagesPerSite": 5, "sameDomainEmailsOnly": true }`

### Output

One row per website. Fields:

| Field | Meaning |
|---|---|
| `id` | `site:<host>` |
| `inputUrl`, `domain`, `finalUrl` | Input, registrable domain, homepage after redirects |
| `companyName` | og:site_name, JSON-LD Organization name or page title |
| `emails`, `emailsSameDomain` | Unique emails (same-domain first) and the same-domain subset |
| `phones`, `phonesE164` | Numbers as written, and valid numbers in E.164 |
| `socialLinks` | `{ linkedin, twitter, facebook, instagram, youtube, tiktok, github }`, arrays of profile URLs |
| `contactPageUrl` | Contact page that was found, if any |
| `addresses` | schema.org PostalAddress objects (street, locality, region, postal code, country, formatted) |
| `countryHint` | Country used to read national phone numbers |
| `pagesCrawled`, `crawledUrls` | Pages scanned |
| `robotsBlocked` | Pages skipped because robots.txt disallows them |
| `foundOn` | Map item -> page URLs where it appeared |
| `error`, `fetchedAt` | Error reason (free rows) and run time |

Sample row (trimmed):

```json
{
  "id": "site:apify.com", "inputUrl": "https://apify.com", "domain": "apify.com", "finalUrl": "https://apify.com/",
  "companyName": "Apify",
  "emails": ["support@apify.com", "hello@apify.com", "legal@apify.com", "privacy@apify.com"],
  "emailsSameDomain": ["support@apify.com", "hello@apify.com", "legal@apify.com", "privacy@apify.com"],
  "phones": [], "phonesE164": [],
  "socialLinks": { "linkedin": ["https://www.linkedin.com/company/apify"], "twitter": ["https://x.com/apify"], "facebook": [], "instagram": [],
    "youtube": ["https://www.youtube.com/apify"], "tiktok": ["https://www.tiktok.com/@apifytech"], "github": ["https://github.com/apify"] },
  "contactPageUrl": "https://apify.com/contact",
  "addresses": [{ "streetAddress": "Na Příkopě 959/27", "addressLocality": "Prague", "addressRegion": null, "postalCode": "11000",
    "addressCountry": "CZ", "postOfficeBoxNumber": null, "formatted": "Na Příkopě 959/27, 11000 Prague, CZ" }],
  "countryHint": "CZ", "pagesCrawled": 8, "robotsBlocked": 0,
  "foundOn": { "hello@apify.com": ["https://apify.com/contact"] },
  "error": null, "fetchedAt": "2026-11-04T11:30:02.588Z"
}
```

### How it works and limits

- robots.txt is read for every host before any page is fetched (rules for `User-agent: *` or `apify-actor-website-contact-extractor`). If robots.txt answers 5xx or cannot be reached, the host is not crawled. Disallowed pages are counted in `robotsBlocked`.
- Pages are found from homepage links (URL or link text matching contact, kontakt, impressum, imprint, about, team, support, legal, privacy and a few translations), then from sitemap.xml when the homepage has too few such links. Only the website's own domain and its sub-domains are visited, at most 20 pages per website, at most 2 requests per second per host.
- Emails: mailto links, visible text, JSON-LD, Cloudflare email protection and simple obfuscation such as `name [at] domain [dot] com`. Image names like `logo@2x.png`, placeholder domains and error-tracking addresses are removed.
- Phones: tel: links and JSON-LD always; numbers in text only when they are valid (libphonenumber) and international, or national with a phone keyword nearby and a known country.
- Plain HTML only: content that appears only after JavaScript runs, contact forms and pages behind logins or bot challenges are not covered. A website whose homepage fails (HTTP error, timeout, non-HTML, robots.txt) gets a free row with `error`.

### Pricing

Pay per result: **$2.00 per 1,000 rows** (`result` event). Platform usage is billed separately by Apify. Removed/free rows are not charged.

One row is one website whose homepage loaded, however many pages were scanned. Error rows are free.

### Legal

Not affiliated with any of the scanned websites or social networks. Uses only publicly accessible web pages and obeys robots.txt. Data licence/terms: each website's own terms of use apply. This Actor is meant for B2B lead research on public company websites. It does not scrape personal social media profiles; it only lists links that a company publishes on its own site. You are responsible for complying with GDPR, CAN-SPAM, ePrivacy and other laws when you store or contact the addresses you collect, and with each website's terms of use.

### FAQ

**Why did a website return no emails?** Many companies only offer a contact form or load contact details with JavaScript. Check `crawledUrls`, `robotsBlocked` and `error`.

**Why are some numbers in `phones` but not in `phonesE164`?** A national number such as `(415) 555-2671` can only be converted when the country is known. Numbers with a `+` prefix are always converted.

**Does it verify the emails?** No. Use an email validation Actor (syntax, MX, disposable checks) on the results.

**Can I crawl more pages per site?** Yes, up to 19 extra pages with `maxPagesPerSite`; the price per website stays the same.

# Actor input Schema

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

Company websites to scan, one per line. A bare domain such as "acme.com" is fine (https:// is added). Duplicates by host are removed.

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

How many contact-like pages to fetch per website in addition to the homepage (contact, imprint, about, team, support, legal, privacy, best first). 0 scans only the homepage.

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

Collect phone numbers from tel: links, JSON-LD and page text, normalised to E.164 when the country can be inferred. When off, the phone fields are null.

## `includeSocialLinks` (type: `boolean`):

Collect LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok and GitHub profile links (share and intent links are dropped). When off, socialLinks is null.

## `sameDomainEmailsOnly` (type: `boolean`):

When on, the emails field keeps only addresses on the website's own domain (e.g. @acme.com for acme.com). emailsSameDomain is always filled.

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

Maximum time to wait for one page. Slow pages are skipped; a website whose homepage times out gets a free error row.

## `maxConcurrency` (type: `integer`):

How many websites are crawled at the same time. Pages of one website are always fetched one by one, at most two per second.

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

Upper limit on charged rows, i.e. websites whose homepage loaded. Error rows for failed or blocked websites are free and do not count.

## Actor input object example

```json
{
  "websites": [
    "https://apify.com",
    "https://www.python.org"
  ],
  "maxPagesPerSite": 8,
  "includePhones": true,
  "includeSocialLinks": true,
  "sameDomainEmailsOnly": false,
  "timeoutSecs": 20,
  "maxConcurrency": 3,
  "maxItems": 100
}
```

# Actor output Schema

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

All rows in the default dataset.

# 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://apify.com",
        "https://www.python.org"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ailu_studio/website-contact-extractor").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://apify.com",
        "https://www.python.org",
    ] }

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

```

## MCP server setup

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

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/idMqFt6SrsY39XVgQ/builds/jB5AQRfWJuSA71sDe/openapi.json
