Contact Details Scraper — Emails, Phones, Socials from Websites avatar

Contact Details Scraper — Emails, Phones, Socials from Websites

Pricing

from $6.00 / 1,000 domain with contacts

Go to Apify Store
Contact Details Scraper — Emails, Phones, Socials from Websites

Contact Details Scraper — Emails, Phones, Socials from Websites

Extract emails, phone numbers and social profiles (Facebook, Instagram, LinkedIn, X, YouTube, TikTok, WhatsApp...) from a list of websites or domains: one merged row per domain.

Pricing

from $6.00 / 1,000 domain with contacts

Rating

0.0

(0)

Developer

Trevor Ortega

Trevor Ortega

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Give it a list of websites or domains and get back one clean row per domain with every email address, phone number and social profile the site publishes: Facebook, Instagram, LinkedIn, X/Twitter, YouTube, TikTok, Pinterest, WhatsApp, Telegram, Discord and GitHub, plus the company name, the postal address (when the site publishes it as structured data) and the URL of the contact page.

The scraper reads the start page and then the pages where contacts usually are: Contact, Impressum/Imprint, About, Team, Support and legal pages first, other pages after them. It understands mailto: links, plain text, obfuscated spellings like info (at) acme (dot) com, Cloudflare's email protection and schema.org JSON-LD. Phone numbers come normalized to international E.164 format (+12122473491) next to the way the site writes them.

You pay only for domains where something was found. Dead domains, blocked sites and sites without any contact details cost nothing.

What data does Contact Details Scraper extract?

FieldDescription
domainThe domain the row is about (host without www.), e.g. patsys.com
inputUrl / finalUrlThe URL or domain as you entered it / the start page after redirects
statusok, unreachable, blocked (anti-bot page), httpError, notHtml, blockedByRobotsTxt, socialProfileOnly, platformPage, skipped
errorWhy the site could not be read (only when status is not ok)
companyNameBusiness name from JSON-LD Organization data, og:site_name or the page title (the one matching the domain wins)
addressPostal address from JSON-LD: streetAddress, addressLocality, addressRegion, postalCode, addressCountry, full
emailsEmail addresses, the site's own domain first, then other addresses the site shows (gmail.com, agencies)
phonesValid phone numbers in E.164 format (+493093931290)
phonesRawThe same numbers as written on the site (+49 (0)30 939312-90), in the same order as phones
phonesUncertainNumbers from tel: links that could not be validated for any country
facebooks, instagrams, linkedIns, twitters, youtubes, tiktoks, pinterests, whatsapps, telegrams, discords, githubsProfile / invite links, normalized (share buttons, single posts and template placeholders are dropped)
contactPageUrlThe site's contact page
contactsFoundtrue when at least one email, phone or social profile was found (only these rows are charged)
pagesVisited / visitedUrlsHow many and which pages were read
errorsPages of the site that could not be read (404, timeouts...)
changeType / previousValuesOnly with monitoring on (see below)
scrapedAtWhen the row was made (UTC)

With Also output one row per page on, every visited page also gets a row (type: "page") with url, depth, pageKind (contact, imprint, about, team, legal, other...), httpStatus, title and the contacts found on that page, so you can see where each email was published. Page rows are free.

How to scrape contact details from a list of websites

  1. Add websites under Websites (one URL per line, or upload / link a text or CSV file), or paste a list or a whole CSV export into Or paste a list of domains / URLs. Bare domains like acme.co.uk are fine.
  2. Keep Max pages per domain at 5 (enough for most sites) or raise it for big sites.
  3. Click Start and download the results as CSV, Excel or JSON from the Output tab, or read them via the API.

Several URLs of the same domain are merged into one row; subdomains (shop.acme.com) get their own row.

Input example

{
"startUrls": [{ "url": "https://www.patsys.com/contact.html" }],
"urlsText": "joscoffee.com\neinstein-kaffee.de\nhttps://www.zingermansdeli.com",
"maxPagesPerDomain": 5,
"maxDepth": 1,
"sameDomainEmailsOnly": false,
"proxyConfiguration": { "useApifyProxy": true }
}

Output example

{
"type": "domain",
"domain": "patsys.com",
"inputUrl": "https://www.patsys.com/contact.html",
"finalUrl": "https://www.patsys.com/contact.html",
"status": "ok",
"error": null,
"companyName": "Patsys Italian Restaurant",
"emails": ["anne@patsys.com", "patsysnyc@gmail.com"],
"phones": ["+12122473491"],
"phonesRaw": ["(212) 247-3491"],
"phonesUncertain": [],
"facebooks": ["https://www.facebook.com/patsysitalianrestaurant"],
"instagrams": ["https://www.instagram.com/patsysitalianrestaurant"],
"linkedIns": [],
"twitters": ["https://x.com/patsysitalrest"],
"youtubes": ["https://www.youtube.com/channel/UCaBjym9W_eD6Svq9vKqX5_w"],
"tiktoks": [],
"pinterests": ["https://www.pinterest.com/sharonaburch/patsys-italian-restaurant-ny"],
"whatsapps": [],
"telegrams": [],
"discords": [],
"githubs": [],
"contactPageUrl": "https://www.patsys.com/contact.html",
"address": null,
"contactsFound": true,
"pagesVisited": 3,
"visitedUrls": ["https://www.patsys.com/contact.html", "https://www.patsys.com/", "https://www.patsys.com/ourstory.html"],
"errors": [],
"scrapedAt": "2026-10-12T18:54:33Z"
}

Which pages does it visit?

The start page, then pages linked from it, best first: contact pages (also Kontakt, Contacto, Contatti, Nous contacter...), Impressum / imprint / legal notice, About, Team, support, privacy and terms pages, locations, then any other page of the site. Crawl depth 1 (default) means the start page and the pages it links to; 2 also follows links found on those pages. Only the website's own domain is crawled (with or without www.); contact, imprint, about and team pages on other subdomains are read when the site links to them, and Include subdomains opens the whole subdomain family. robots.txt is respected unless you switch it off. Every site gets at most 3 requests at a time with short pauses, so crawling is polite.

Pricing

EventPriceWhen
Domain with contacts$0.006A domain where at least one email, phone number or social profile was found. Includes up to 5 pages.
Extra page$0.0012Each page beyond the first 5 of a domain with contacts (only if you raise Max pages per domain).

Domains with nothing found, unreachable or blocked sites, and page rows are free. Platform usage is billed by Apify at cost and is small: a run of 98 domains with the default settings read 4.6 pages per site, took 3 minutes and used $0.007 of platform usage (about $0.07 per 1,000 domains). Example: 1,000 domains, 90% with contacts, default settings: 900 × $0.006 = $5.40 plus about $0.07 usage.

Monitor changes on a schedule

Turn on Only new or changed domains to watch a lead list or your competitors' contact pages. The actor remembers what earlier runs of the same input found (emails, phones and social profiles per domain) and outputs only domains that are new or whose contact set changed; unchanged domains are not output and not charged. Save the input as a task and add a Schedule in Apify Console (Schedules → Create, e.g. weekly). Changed rows carry changeType: "changed" and previousValues with the old emails/phones/profiles; first-seen domains carry changeType: "new". A site that is down for one run, or a run where a page fails to load and something disappears, does not count as a change. Use Monitoring key to keep several watches on the same list apart.

How do I extract emails from a CSV or Google Sheet of domains?

Paste the CSV into Or paste a list of domains / URLs: every column that holds a website or domain is used and the other columns (names, cities, emails) are ignored. For a Google Sheet, use File → Share → Publish to web → CSV and add that link under Websites with the "Link remote text file" option.

Why was no email found for a website?

Common reasons: the site shows a contact form instead of an address, the address is in an image, or the page is built entirely by JavaScript (single-page apps); this scraper reads the HTML without a browser, which keeps it fast and cheap. Some sites block datacenter IPs (status: "blocked"): try the residential proxy for those. Rows without contacts are free, so it costs nothing to try.

Does it find emails hidden by Cloudflare or written as "name (at) domain (dot) com"?

Yes. Cloudflare's email obfuscation is decoded, and the common "(at)" / "[at]" / "(dot)" spellings are read. Placeholders (you@example.com, Wix and Sentry ids, image names like logo@2x.png) and no-reply addresses are dropped.

Proxy note

The default Apify datacenter proxy works for most business websites and is included in the platform usage. For lists of sites that block datacenter IPs, choose the Residential proxy group; Apify bills residential traffic per GB.

Integrations and API

Run it from Zapier, Make, n8n or Google Sheets, or call it from Python / Node.js with the Apify client (see the API tab). Use the result to enrich a CRM: the domain field is a stable key.

The actor reads only what websites publish openly to anyone, without logging in, and does not guess or verify addresses. Business contact details are public by intent, but some addresses identify a person (jane.doe@...); if you are in the EU/UK, data protection law (GDPR) applies to how you store and use them, e.g. for cold outreach. You are responsible for complying with the applicable law and the sites' terms.

Support

Found a site where a visible email or phone was missed? Open an issue in the Issues tab with the URL; it is usually answered within a day.