Skip to content

About

Extract emails, phone numbers and social profiles from a list of websites in Python (Apify website contact scraper tutorial)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Website Contact Scraper in Python: extract emails, phone numbers and social profiles from any list of domains

A website email extractor, phone number finder and social link scraper for B2B lead generation, called from Python (or Node) with the official apify-client. Give it company domains; get one clean row per website with the best business email, every email found, phone numbers in international format, LinkedIn / Facebook / Instagram / X / YouTube / TikTok profiles, the company name, postal address and contact page URL.

Disclosure: I built this actor; it's a paid tool on Apify (Website Contact Scraper on the Apify Store). This repo is a free tutorial and example code (MIT).

Why this one

  • Priced per website, not per page: $3.00 per 1,000 websites, however many pages it reads (default 6, up to 30).
  • Reads the right pages: homepage plus the most contact-like links (contact, imprint / Impressum, about, support, locations, in nine languages).
  • No headless browser: plain HTTP + Cheerio, so 20 websites finish in about 6 seconds.
  • Clean data: Cloudflare-protected and info [at] acme [dot] com style emails decoded, phones validated with libphonenumber, share buttons and personal LinkedIn profiles ignored.
  • Failed sites are free: a domain that does not resolve or blocks bots comes back as status: "failed" and is not charged.

Quick start (Python)

pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

run = client.actor("rel8ble/website-contact-extractor").call(run_input={
    "websites": ["apify.com", "hetzner.com", "thomann.de", "https://www.joesstonecrab.com"],
    "maxPagesPerWebsite": 6,
})

for site in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(site["domain"], site.get("email"), site.get("phone"), site.get("linkedin"))

Get your token in the Apify Console under Settings > Integrations. The free plan includes $5 of monthly credit (about 1,600 websites).

Quick start (Node.js)

import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('rel8ble/website-contact-extractor').call({
    websites: ['kinsta.com', 'allbirds.com'],
    onlySameDomainEmails: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((s) => console.log(s.domain, s.email, s.phonesE164));

Full example: domain list -> contacts.csv

example.py reads domains.txt (one domain per line, e.g. the website column from a Google Maps or CRM export), runs the actor, and writes contacts.csv with one row per site. It also prints fill rates and the sites that publish no email, so you know which ones need the contact form or a phone call.

pip install -r requirements.txt
set APIFY_TOKEN=<YOUR_APIFY_TOKEN>      # macOS/Linux: export APIFY_TOKEN=...
python example.py

Input

Field Type Default What it does
websites array of strings required Bare domains, hosts or full URLs. One result row per domain (deduplicated).
maxPagesPerWebsite integer 1-30 6 Pages read per site incl. homepage. Price stays per website.
defaultCountry string guessed from TLD Country for phone numbers written without a prefix (US, GB, DE).
scanTextForPhones boolean true Also read validated phone numbers from contact/imprint page text.
onlySameDomainEmails boolean false Keep only @yourdomain emails, drop Gmail/agency addresses.
maxConcurrency integer 1-50 20 Parallel requests.

Output sample (real, trimmed)

From a cloud run on 9 Oct 2026:

{
  "website": "hetzner.com",
  "domain": "hetzner.com",
  "finalUrl": "https://www.hetzner.com/",
  "status": "ok",
  "companyName": "Hetzner",
  "language": "en-US",
  "email": "info@hetzner.com",
  "emails": ["info@hetzner.com", "dsa-authorities@hetzner.com", "tco@hetzner.com"],
  "emailSources": { "info@hetzner.com": "mailto @ /legal/legal-notice/" },
  "phone": "+49 9831 5050",
  "phones": ["+49 9831 5050", "+49 9831 5053"],
  "phonesE164": ["+4998315050", "+4998315053"],
  "linkedin": "https://www.linkedin.com/company/hetzner-online",
  "facebook": "https://www.facebook.com/hetzner.de",
  "instagram": "https://www.instagram.com/hetzner.online",
  "twitter": "https://x.com/Hetzner_Online",
  "youtube": "https://www.youtube.com/user/HetznerOnline",
  "contactPageUrl": "https://www.hetzner.com/legal/legal-notice/",
  "pagesCrawled": 6,
  "scrapedAt": "2026-10-09T17:27:53.266Z"
}

A few rows from a 20-domain test run on the same day (19 loaded, 1 deliberately fake domain failed and was not charged; 6.4 s total):

Domain Best email Phone LinkedIn Contact page
thomann.de info@thomann.de +49 9546 922366 - yes
kinsta.com contact@kinsta.com +1 888 610 2915 yes yes
mcmaster.com eprocurement@mcmaster.com +1 630 833 0300 - yes
sennheiser.com contact@sennheiser.com +49 5130 6000 yes yes
joesstonecrab.com (none published) +1 305 673 0365 - yes
zapier.com (none published) - yes yes

Across that run: 12 of 19 sites had an email, 11 a phone, 16 at least one social profile. On a larger 67-site list, 60% had an email, 33% a phone and 87% a social profile. The actor never guesses addresses; many sites only offer a contact form.

Price and limits

  • $3.00 per 1,000 websites (pay per result, status: "ok" rows only). 100 sites = $0.30, 10,000 = $30.
  • Reads the HTML the server sends; single-page apps that render everything in the browser may return fewer contacts.
  • About 5% of sites use strict bot protection and come back failed (free).
  • Only public, business-published contact details; personal LinkedIn profiles and team pages are skipped. Emails can still be personal data under GDPR and anti-spam laws, so use the results responsibly.

Links

License

MIT

About

Extract emails, phone numbers and social profiles from a list of websites in Python (Apify website contact scraper tutorial)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages