Website Contact Extractor: Emails, Phones & Socials
Pricing
from $2.00 / 1,000 website with contacts
Website Contact Extractor: Emails, Phones & Socials
Find emails, phone numbers and social media profiles (LinkedIn, Facebook, Instagram, X, YouTube, TikTok, WhatsApp, Telegram...) on any list of company websites. Checks the homepage and contact, about and imprint pages. Pay only for websites where contacts are found.
Pricing
from $2.00 / 1,000 website with contacts
Rating
0.0
(0)
Developer
Catalin Friptu
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Give it a list of company websites and get back, for each one, every email address, phone number and social media profile published on the site: LinkedIn, Facebook, Instagram, X/Twitter, YouTube, TikTok, Pinterest, GitHub, Telegram, WhatsApp, Threads and Discord. One clean row per website, ready for a CRM, a spreadsheet or an outreach tool.
- Pay only for results: $2 per 1,000 websites where contacts are found. Websites that are down, blocked or have no contact details cost nothing.
- Looks where contacts actually are. It reads the homepage, then follows links to Contact, About, Team, Imprint/Impressum and Legal pages in 20+ languages (Kontakt, Contacto, Contatti, Despre noi, Контакты, Hakkımızda...).
- Finds hidden emails too:
mailto:links, plain text,info [at] company [dot] comspellings and Cloudflare-protected addresses. - Phone numbers in international format (E.164), from click-to-call links and page text. Only numbers that are valid for their country are kept, so dates, prices and order numbers don't sneak in. The country is taken from the domain (
.de,.co.uk,.md...) or from your setting. - Real profiles, not share buttons.
facebook.com/sharer,twitter.com/intent, YouTube videos and LinkedIn share links are dropped; profile URLs are cleaned and de-duplicated (github.com/acme/repobecomesgithub.com/acme). - The company's own email comes first (
info@acme.combeforeacme.office@gmail.com). - Fast and light. Plain HTTP requests, 20 websites in parallel, no browser.
Who it's for
- Sales and lead-generation teams enriching a list of company domains (from a CRM export, a directory or a Google Maps / OpenStreetMap scrape) with emails, phones and socials.
- Agencies and recruiters building contact lists for outreach.
- Data teams keeping company records up to date.
Input
| Field | Default | Description |
|---|---|---|
startUrls | Websites or plain domains (acme.com). Several pages of one website count as one website. | |
maxPagesPerWebsite | 5 | Homepage + the most promising contact/about/team/imprint pages. |
defaultCountry | from domain | Two-letter code (US, GB, DE...) to read local phone numbers written without +country. |
phonesFromText | true | Also read numbers from the page text, not only tel: links. |
respectRobotsTxt | true | Skip pages a website's robots.txt disallows. |
maxConcurrency | 20 | Websites processed in parallel. |
{"startUrls": ["acme-dental.com", "https://www.example-bakery.co.uk/"],"maxPagesPerWebsite": 5,"defaultCountry": "US"}
Output
One row per website. Lists are empty ([]) when nothing was found.
| Field | Description |
|---|---|
input_url, url, domain | What you gave, the homepage after redirects, and the domain without www.. |
title, description | The homepage's title and meta description. |
email, emails | The best email (own domain first), and all of them. |
phone, phones | The first phone number and all of them, in E.164 format (+442079460958). |
linkedin, facebook, instagram, twitter, youtube, tiktok, pinterest, github, telegram, whatsapp, threads, discord | Profile URLs. |
contact_pages | Pages where something was found. |
pages_crawled | Pages read on this website. |
contacts_found | true if at least one email, phone or profile was found (these rows are the ones you pay for). |
crawled_at | UTC timestamp. |
error | Why a website could not be read (HTTP 403, ConnectError: ..., blocked by the site's robots.txt). |
Example row from a real run (a restaurant website taken from OpenStreetMap):
{"input_url": "https://www.armeniantaverna.co.uk/","url": "https://armenianrestaurant.co.uk/","domain": "armeniantaverna.co.uk","title": "Armenian Restaurant in Manchester, by Albert Square | Armenian Restaurant & Bar","description": "Armenian food in Manchester since 1968, facing the Town Hall by Albert Square. ...","email": "contact@armenianrestaurant.co.uk","emails": ["contact@armenianrestaurant.co.uk"],"phone": "+441618349025","phones": ["+441618349025"],"facebook": ["https://facebook.com/armenianrestaurantbar"],"instagram": ["https://instagram.com/armenianrestaurantbar"],"linkedin": [], "twitter": [], "youtube": [], "tiktok": [], "pinterest": [], "github": [],"telegram": [], "whatsapp": [], "threads": [], "discord": [],"contact_pages": ["https://armenianrestaurant.co.uk/"],"pages_crawled": 1,"contacts_found": true,"crawled_at": "2026-10-24T03:01:45+00:00","error": null}
The dataset has three views: Contacts, Leads (one email and phone per row, best for CSV/Excel) and All fields. Export to CSV, Excel or JSON, send it to Google Sheets or a CRM through Apify integrations, or read it from the API:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_API_TOKEN>")run = client.actor("malmurk/website-contact-extractor").call(run_input={"startUrls": ["acme-dental.com", "example-bakery.co.uk"],})for row in client.dataset(run["defaultDatasetId"]).iterate_items():print(row["domain"], row["email"], row["phone"], row["linkedin"])
Pricing
Pay per event:
| Event | Price | Charged for |
|---|---|---|
website | $2 per 1,000 websites ($0.002 each) | A website on which at least one email, phone number or social profile was found. |
| Actor start | $0.00005 per run | Once per run (per GB of memory). |
- Websites that fail to load, are blocked, or have no contact details are free (you still get a row with the reason).
- The price is per website, not per page: reading 5 pages of a website costs the same as reading 1.
- The run stops cleanly at the maximum charge you set for it.
10,000 websites with 6,000 hits cost $12.
FAQ
Why is a website missing an email I can see in my browser?
The address may be drawn by JavaScript, shown as an image, or sit behind a contact form. This Actor reads the HTML the server sends (no browser), which keeps it fast and cheap. Try a higher maxPagesPerWebsite if the email is on a deeper page.
Why are there no phone numbers on my .com websites?
A local number like (212) 555-0182 can only be read if the country is known. .com doesn't say which country, so set defaultCountry. Numbers with a +country prefix and click-to-call links are always read.
Does it use proxies or bypass bot protection? No. A website that blocks it gets an error row and is free. No logins, no CAPTCHA solving.
Is this legal?
It reads publicly published business contact details from websites you choose, and respects robots.txt. Email addresses and phone numbers can still be personal data (GDPR, CCPA, CAN-SPAM, ePrivacy). You are responsible for having a lawful basis to store and use them, and for honouring opt-outs.
Can I feed it the output of another Actor?
Yes. Pass a list of website URLs (from Google Maps, OpenStreetMap, a directory scraper or your CRM) as startUrls, or chain it with an Apify integration.
Limitations
- No JavaScript rendering, so contacts that only appear after scripts run are missed.
- Contact forms, images of emails and PDFs are not read.
- Personal social profiles linked from a website (e.g. a founder's LinkedIn) are reported like company profiles.
Local development
python -m venv .venv && .venv/Scripts/pip install -r requirements-dev.txt # Linux/macOS: .venv/bin/pippython -m pytest -q
Phone parsing uses phonenumbers (Apache-2.0), HTML parsing lxml (BSD), robots.txt Protego (BSD-3).