Website Contact Details Scraper – Emails, Phones & Socials
Pricing
from $7.50 / 1,000 website scrapeds
Website Contact Details Scraper – Emails, Phones & Socials
Give it a list of websites or domains: it crawls each site's contact, about, team and impressum pages and returns one clean lead record per website with emails, phone numbers (E.164) and LinkedIn, Facebook, Instagram, X, YouTube, TikTok, WhatsApp and other social profiles.
Pricing
from $7.50 / 1,000 website scrapeds
Rating
0.0
(0)
Developer
Zee Lex
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Give it a list of websites or domains. For each one it crawls the most useful pages — homepage, contact, about, team, impressum/imprint and footer links — and returns one merged, deduplicated lead record per website with:
- Emails, including addresses hidden by Cloudflare email protection or written as
name [at] domain [dot] com. Junk such aslogo@2x.png,you@example.comand tracking keys is dropped. - Phone numbers, normalized to E.164 (
+14155550132), plus the raw text as found. Dates, prices, postcodes and VAT/registration numbers are not mistaken for phones. - Social profiles: LinkedIn, Facebook, Instagram, X/Twitter, YouTube, TikTok, Pinterest, GitHub, WhatsApp, Telegram and Threads. Share buttons, posts and platform home pages are left out.
- The homepage title, meta description and language, and the URLs of the site's contact pages.
It works over plain HTTP, so it is fast and cheap. An optional browser fallback handles sites built entirely with JavaScript.
Use cases
- Lead generation: turn a list of company websites into emails, phone numbers and LinkedIn pages ready for outreach.
- Enrich Google Maps or LinkedIn lists: add contact details to businesses you scraped elsewhere, using their website column.
- CRM enrichment: fill missing emails, phones and social links for the accounts in your CRM, and spot dead websites (
status: "unreachable"). - Market research: see which competitors are active on which social networks.
Input example
{"startUrls": [{ "url": "https://www.example-dental.com" },{ "url": "acme-widgets.co.uk" }],"websites": "beispiel-gmbh.de\nhttps://www.example.com.my/about","maxPagesPerSite": 10,"maxDepth": 2,"sameDomainOnly": true,"prioritizeContactPages": true,"includeEmails": true,"includePhones": true,"includeSocials": true,"phoneCountryHint": "","outputPerPage": false,"useBrowserFallback": false}
The inputs:
- Websites can be full URLs or bare domains. Inputs on the same registrable domain (
www.example.com,shop.example.com,example.com/contact) count as one website. maxPagesPerSiteincludes the homepage. Contact-type pages are fetched first. Ordinary links may use only half of what is left of the limit, so contact pages found deeper still fit.phoneCountryHintlets the scraper read numbers written without a country code, such as "(415) 555-0132". When it is empty:- the website's country domain is used (
.my→ MY,.co.uk→ GB,.de→ DE); - on
.com/.orgsites, only+countrynumbers are read from text; tel:links are always read.
- the website's country domain is used (
An empty input ({}) scrapes apify.com and python.org with 5 pages each and finishes in well under a minute.
Output example
One record per website (type: "website"):
{"type": "website","inputUrl": "acme-widgets.co.uk","domain": "acme-widgets.co.uk","siteUrl": "https://www.acme-widgets.co.uk","siteTitle": "Acme Widgets — Industrial Widgets since 1999","description": "Acme Widgets makes industrial widgets.","language": "en-GB","emails": ["sales@acme-widgets.co.uk", "support@acme-widgets.co.uk", "acme.widgets@gmail.com"],"emailsSameDomain": ["sales@acme-widgets.co.uk", "support@acme-widgets.co.uk"],"phones": ["+442079460018", "+441614960000"],"phonesRaw": ["020 7946 0018", "+44 161 496 0000"],"linkedin": ["https://www.linkedin.com/company/acme-widgets"],"facebook": ["https://www.facebook.com/acmewidgets"],"instagram": ["https://www.instagram.com/acmewidgets"],"x": ["https://x.com/acmewidgets"],"youtube": ["https://www.youtube.com/@acmewidgets"],"tiktok": [],"pinterest": [],"github": [],"whatsapp": ["https://wa.me/447700900123"],"telegram": [],"otherSocials": [],"contactPageUrls": ["https://www.acme-widgets.co.uk/contact-us"],"pagesCrawled": 8,"status": "ok","error": null,"scrapedAt": "2026-10-29T08:00:00.000Z"}
All values above are examples.
status is one of:
ok: contact details were found.no_contacts: the site was crawled, but nothing was found.unreachable: DNS failure, connection refused or timeout.errorsays which.blocked: 403/429 or a bot-challenge page, still blocked after one retry on a new session.
The other fields:
emailsSameDomainlists the emails on the website's own domain or its subdomains. Those come first inemails.otherSocialsholds Threads profiles.
With outputPerPage: true, every crawled page also gets a free record: { "type": "page", "domain", "url", "emails", "phones", "phonesRaw", "socials" }.
The run summary is in the key-value store under OUTPUT:
{ "sites": 250, "sitesWithEmail": 191, "sitesWithPhone": 168, "sitesWithSocials": 203, "unreachable": 9, "blocked": 4, "pagesCrawled": 1840 }
Pricing
Pay per event:
websiteis charged once per website record with statusokorno_contacts. Unreachable and blocked websites are free.page-browseris charged per page rendered in the browser. This only happens withuseBrowserFallback, and only for pages that had almost no text over HTTP.
Per-page records are free. The run stops cleanly when your maximum cost per run is reached.
Setup note for the Actor owner: in Monetization, create the events
websiteandpage-browser. Do not enable the default per-dataset-item event.
How it works
- Each website is normalized to its origin and crawled from its homepage, following redirects (http → https, apex → www). If https fails at the network level, plain http is tried once.
- Links on the same registrable domain are ranked. Contact, about, team, impressum, legal, privacy, support, staff, people and careers pages, in many languages, come first. Crawling stops at
maxPagesPerSiteandmaxDepth. These are skipped:- PDFs, images, archives and other files
- login and cart pages
- pages over 5 MB
- Extraction runs on every page:
mailto:andtel:links- the visible text, with obfuscated addresses decoded
- the raw HTML, with HTML entities decoded
- Cloudflare-protected emails
- JSON-LD structured data (
email,telephone,sameAs)
- Merging. Everything a website's pages yielded is merged and deduplicated into one record.
- Browser fallback (optional). After the HTTP crawl, pages with almost no text are rendered in a headless browser and read again. Links that only appear after rendering are followed within the same limits.
- Safety:
- One failing website never stops the others.
- About 45 seconds before the run's timeout, the scraper stops and saves a record for every website with what it has collected.
- The run fails only if every website was unreachable.
FAQ
Why are some phone numbers only in phonesRaw?
Some numbers can't be turned into a full international number: a tel: link without a country code on a .com site, or a short service number. Set phoneCountryHint to read national-format numbers on generic domains.
Why is emailsSameDomain empty when emails isn't?
The business uses an address on another domain (Gmail, a parent company, an agency). Those are still in emails.
Does it find emails that are only shown after clicking or in images? Emails in images or behind a click-to-reveal button can't be read. Cloudflare-protected emails, HTML entities and "[at]"/"[dot]" spellings are decoded.
A website shows blocked. What can I do?
The site's bot protection refused the request twice. Run those websites again with RESIDENTIAL proxy, and with useBrowserFallback for JavaScript-heavy sites. Blocked websites are not charged.
Can I give it a list from a spreadsheet?
Yes. Paste the column into Websites (plain list), one website per line, or pass startUrls through the API.
Is it legal? The scraper reads public pages only. Emails and phone numbers of people are personal data. Make sure your use, especially cold outreach, complies with the GDPR, CAN-SPAM, PDPA and the other laws that apply to you.
Development
npm installnpm test # unit tests + end-to-end runs against local fixture websites (E2E_BROWSER=0 skips the browser run)npm run fixture-sites # serve the fixture websites on :4611–4616 to try the Actor by handnpm start # run locally (input in storage/key_value_stores/default/INPUT.json)