Website Contact Extractor
Pricing
from $3.00 / 1,000 domain contacts
Website Contact Extractor
Extract contact information (emails, phone numbers, social profiles, addresses) from websites by crawling homepage, contact, about, and footer links. One structured result per domain with CSV and Markdown export.
Pricing
from $3.00 / 1,000 domain contacts
Rating
0.0
(0)
Developer
Harsh
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Extract emails, phone numbers, social profiles, and addresses from one or more websites.
Website Contact Extractor is an Apify Actor built with TypeScript and Crawlee CheerioCrawler. Point it at company homepages — it crawls the landing page plus contact/about/footer links, deduplicates contacts per domain, and writes a structured dataset plus CSV and Markdown summaries.
Features
- Email extraction —
mailto:links, page text regex, schema.org; filters noise (example.com, Sentry, Wixpress, noreply, image extensions) - Phone extraction —
tel:links, international patterns,itemprop="telephone" - Social profiles — LinkedIn, Twitter/X, Facebook, Instagram, YouTube, TikTok, GitHub
- Physical addresses — schema.org
PostalAddress, microdata, footer<address>blocks - Contact page discovery — follows contact, about, team, support, locations, impressum links
- Company name — from
og:site_name, page title, or JSON-LD Organization - Per-domain deduplication — one dataset item per domain with merged contacts
- Rate limiting & retries — configurable concurrency, delay, and request retries
- Proxy-ready — optional Apify Proxy configuration
- Exports —
CONTACTS.csvandCONTACTS.mdin the default key-value store
Installation
# Clone / open the Actor directorycd website-contact-extractor# Install dependenciesnpm install# Run locally (uses storage/key_value_stores/default/INPUT.json or --input)apify run
On the Apify platform, deploy with apify push or use the published Actor from the Store.
Input
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
startUrls | object[] | Yes | — | Homepage URLs to extract contacts from |
maxPagesPerDomain | integer | No | 10 | Max pages crawled per domain |
maxConcurrency | integer | No | 5 | Parallel request limit |
maxRequestRetries | integer | No | 3 | Retries for failed requests |
requestDelayMs | integer | No | 200 | Delay hint for rate limiting |
followContactLinks | boolean | No | true | Crawl discovered contact/about links |
includeEmails | boolean | No | true | Extract emails |
includePhones | boolean | No | true | Extract phones |
includeSocials | boolean | No | true | Extract social profiles |
includeAddresses | boolean | No | true | Extract physical addresses |
proxyConfiguration | object | No | Proxy off | Apify Proxy settings |
Example input
{"startUrls": [{ "url": "https://www.apify.com" },{ "url": "https://www.shopify.com" },{ "url": "https://stripe.com" }],"maxPagesPerDomain": 5,"maxConcurrency": 5,"requestDelayMs": 200,"followContactLinks": true,"includeEmails": true,"includePhones": true,"includeSocials": true,"includeAddresses": true}
See also: examples/input.json
Output
Each dataset item is one domain:
| Field | Type | Description |
|---|---|---|
domain | string | Normalized host without www |
url | string | Start URL for the domain |
companyName | string | null | Detected company/site name |
emails | string[] | Deduplicated emails |
phones | string[] | Deduplicated phones |
socials | object | Map of platform → profile URL |
addresses | string[] | Physical addresses when found |
contactPageUrls | string[] | Discovered contact/about URLs |
pagesScraped | integer | Pages successfully scraped |
scrapedAt | string | ISO timestamp |
error | string | null | Present if the domain failed entirely |
Example output
{"domain": "apify.com","url": "https://www.apify.com","companyName": "Apify","emails": ["hello@apify.com"],"phones": [],"socials": {"linkedin": "https://www.linkedin.com/company/apifytech","twitter": "https://twitter.com/apify","github": "https://github.com/apify"},"addresses": [],"contactPageUrls": ["https://apify.com/contact"],"pagesScraped": 3,"scrapedAt": "2026-07-13T12:00:00.000Z"}
Key-value store exports
| Key | Content type | Description |
|---|---|---|
CONTACTS.csv | text/csv | Flat CSV summary of all domains |
CONTACTS.md | text/markdown | Human-readable Markdown report |
OUTPUT | application/json | Run summary stats |
See also: examples/output.json
How it works
- Start URLs — Each unique domain is queued once (first URL wins as the primary).
- Homepage scrape — Cheerio parses the page for emails, phones, socials, addresses, and company name.
- Contact link discovery — Same-domain links matching contact/about/team/support/locations patterns are enqueued (up to
maxPagesPerDomain). - Merge & dedupe — Contacts from all pages are merged per domain.
- Export — One dataset item per domain, plus
CONTACTS.csvandCONTACTS.md.
Pricing
$0.003 per domain result using Apify pay-per-event (apify-default-dataset-item). Charged once per domain written to the dataset.
Run locally
npm installnpm run buildnpm testapify run -p -i examples/input.json
Limitations
- Static HTML only — Uses Cheerio (no browser). Contacts injected purely via client-side JavaScript may be missed.
- Obfuscated emails — Cloud-encoded or image-only emails are not decoded.
- Address detection — Best with schema.org / microdata; free-form footer text is heuristic.
- Phone false positives — Aggressive phone regex can occasionally match non-phone number sequences; noise filters reduce this.
- Same-domain only — Contact links on other domains are not followed.
- Rate limits — Aggressive settings may trigger site WAF/bot protection; enable Apify Proxy for production.
FAQ
How many pages are crawled per site?
Up to maxPagesPerDomain (default 10): the start URL plus discovered contact/about links.
Can I extract only emails?
Yes. Set includePhones, includeSocials, and includeAddresses to false.
Does it work without a proxy?
Yes for small local tests. For bulk production runs, enable proxyConfiguration.useApifyProxy.
Where is the CSV?
In the run’s default key-value store under key CONTACTS.csv.
One result or many?
One dataset item per unique domain, regardless of how many pages were scraped on that domain.
Changelog
1.0.0
- Initial release
- Email, phone, social, address extraction with CheerioCrawler
- Contact/about link following and per-domain deduplication
- CSV and Markdown exports
- Input flags for selective extraction
- LEAD_GENERATION + DEVELOPER_TOOLS categories
License
Apache-2.0