Website Contacts API
Pricing
from $10.50 / 1,000 website with contacts
Website Contacts API
Extract e-mails, phone numbers, WhatsApp, Telegram, social profiles, contact page and address from any list of websites. One clean row per website, priced per website, not per page. No API key, no proxy, no browser.
Pricing
from $10.50 / 1,000 website with contacts
Rating
0.0
(0)
Developer
Matvey
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
7 hours ago
Last modified
Categories
Share
What is Website Contacts API?
Website Contacts API turns a list of websites into a contact list: one clean row per website with the company's e-mail addresses, phone numbers, WhatsApp and Telegram links, LinkedIn, Facebook, Instagram, X, YouTube and TikTok profiles, contact page, contact form and postal address — each contact with the page it was found on and how it was found. Paste domains from Google Maps, a CRM export or a spreadsheet, press Start, and download the contacts as JSON, CSV or Excel, or pull them through the API into n8n, Make, Zapier, Google Sheets or an AI agent.
It reads the home page and the few pages that actually carry contacts (contact, about, team, imprint, legal), decodes Cloudflare-protected addresses and name [at] domain [dot] com obfuscation, reads schema.org markup, validates every phone against the international numbering plan, and even reads the hidden search index of Framer sites that show nothing without JavaScript. No API key, no login, no proxy, no browser.
The price is per website, not per page. You pay one flat event for every website where at least one e-mail or phone was found. Websites with nothing found, unreachable or blocked websites are free, and the number of pages read never changes your bill — so a 1,000-row list costs what the pricing tab says, not twenty times more.
What data does Website Contacts API extract?
| Field | Example | Where it comes from |
|---|---|---|
bestEmail, bestEmailType | hello@apify.com, role | The best address: the site's own domain first, general inboxes (info@, hello@, contact@) over side mailboxes, mailto links over page text |
emails[] | {email, type, isOwnDomain, isFree, source, sourceUrl} | Every distinct address with its type (role, personal, generic), whether it is on the website's own domain, whether it is a free mailbox (gmail.com …), and the source: mailto, schema.org, cloudflare or text |
bestPhone, bestPhoneFormatted | +18884827768, +1 888 482 7768 | Best number in E.164, tel links first |
phones[] | {phone, formatted, country, kind, source, sourceUrl} | Every valid number with country, kind (mobile, fixed_line, toll_free …) and source (tel, schema.org, whatsapp, text) |
whatsapp, telegram | https://wa.me/628179677771 | Messenger links; the WhatsApp number is also added to phones |
linkedin, facebook, instagram, x, youtube, tiktok, pinterest, threads, github | https://linkedin.com/company/apify | Normalised profile URLs; share buttons, login pages and hashtags are ignored |
contactPageUrl, contactFormUrl | https://apify.com/contact | The contact page that was read and the page with a contact form |
addressStreet, addressCity, addressRegion, addressPostalCode, addressCountry | 2 Canal Park, Cambridge, MA, 02141, US | schema.org PostalAddress; addressText falls back to an <address> tag |
companyName | HubSpot | schema.org Organization, og:site_name or the page title |
status, note | ok, javascript_rendered_site | ok, no_contacts, unreachable, blocked; the note tells you when a site is an empty JavaScript shell |
pagesScanned, pagesScannedCount, durationMs, fetchedVia | […], 3, 1497, fetch | What was read and how long it took |
summary | HubSpot: +18884827768 (2 e-mails, 25 phones); linkedin, facebook, instagram, x, youtube, tiktok. | One sentence for agents, Slack alerts and spreadsheets |
emailChecks[], bestEmailStatus | deliverable | Only with the e-mail check add-on: DNS-level deliverability, mail provider, disposable / role / free flags, typo suggestion |
Every row also carries inputUrl, website (final URL after redirects), domain, language, phoneCountryUsed and scrapedAt. Failed websites come back as type: "error" rows with status, httpStatus and error, so a batch never silently loses a domain.
How much does it cost to extract contacts from websites?
Website Contacts API uses pay-per-event pricing with two events:
| Event | Price | When it is charged |
|---|---|---|
| 🌐 Website with contacts | $15.00 per 1,000 websites ($0.015 each) | Once per website where at least one e-mail or phone number was found |
| ✅ Add-on: e-mail check | $2.00 per 1,000 addresses | Only when you switch the add-on on, once per checked address (up to 20 per website) |
What is free: websites with no contacts, unreachable domains, websites behind an anti-bot wall, and every page read. Apify platform usage is included in the price — you do not pay separately for compute, and there is no proxy cost because none is used by default.
For comparison, page-priced contact scrapers charge $1–2 per page and read 20 pages per site by default, which is why their users report bills of $20–70 per 1,000 sites. Here a list of 1,000 websites costs at most $15, usually less, because empty sites are not billed. Apify's free plan includes $5 of usage every month, enough to scan roughly 300 websites for free.
How to extract contacts from a list of websites
- Open Website Contacts API on Apify and click Try for free (an Apify account is created in a minute).
- Paste your websites into 🌐 Websites — one per line, with or without
https://— or bare domains into 🌐 Domains. You can also upload a text file or link a Google Sheet through the ⬇️ Input editor. - Optionally set 📞 Default phone country (used only for numbers written without a country code; a site's own country domain always wins) and switch on the ✅ e-mail check add-on.
- Click Start. A hundred websites take about a minute.
- Open the Contacts tab to read the results, or Export them as CSV, Excel, JSON or XML.
⬇️ Input
{"startUrls": [{ "url": "https://www.apify.com" },{ "url": "https://www.basecamp.com" }],"domains": ["hubspot.com", "legalindonesia.id"],"maxPagesPerSite": 6,"defaultCountry": "US","checkEmails": false,"onlyOwnDomainEmails": false,"stopEarly": true}
| Field | Default | What it does |
|---|---|---|
startUrls | 5 example sites | Websites as URLs; the Apify editor accepts a pasted list, a file or a Google Sheet |
domains | [] | Bare domains, one per line — for lists exported from Google Maps or a CRM |
maxPagesPerSite | 6 | Home page plus the most contact-like pages; 6 finds nearly everything on a normal business site |
defaultCountry | US | Two-letter country used for phone numbers without a country code |
checkEmails | false | Add-on ($): DNS deliverability, provider, disposable / role / free flags per address |
onlyOwnDomainEmails | false | Drop addresses on other domains (an agency, a partner, a gmail.com mailbox) |
stopEarly | true | Skip remaining pages once an own-domain e-mail and a phone were found |
maxWebsites, concurrency, pageTimeoutSecs, proxyConfiguration | 1000, 10, 15, off | Safety cap, parallelism, per-page timeout, optional Apify Proxy |
🌐 Which pages are read?
The home page first. Then links whose path or text says contact, kontakt, contacto, contatti, impressum, imprint, about, team, support, legal, privacy, locations and their equivalents in a dozen languages, best-scored first. If the navigation is rendered by JavaScript and no such links exist, the well-known paths /contact, /contact-us, /about, /about-us, /impressum and /kontakt are tried. Blog posts, product pages, carts and search pages are never read.
⬆️ Output
One row per website:
{"type": "website","inputUrl": "https://www.bmw.de","website": "https://www.bmw.de/de/index.html","domain": "bmw.de","companyName": "BMW","status": "ok","bestEmail": "kundenbetreuung@bmw.de","bestEmailType": "generic","bestPhone": "+4989125016000","bestPhoneFormatted": "+49 89 125016000","emailCount": 2,"phoneCount": 2,"emails": [{ "email": "kundenbetreuung@bmw.de", "type": "generic", "isOwnDomain": true, "isFree": false, "source": "mailto", "sourceUrl": "https://www.bmw.de/de/footer/metanavigation/legal-notice-pool/imprint.html" },{ "email": "bmw-connecteddrive@bmw.de", "type": "generic", "isOwnDomain": true, "isFree": false, "source": "mailto", "sourceUrl": "https://www.bmw.de/de/footer/metanavigation/legal-notice-pool/imprint.html" }],"phones": [{ "phone": "+4989125016000", "formatted": "+49 89 125016000", "country": "DE", "kind": "fixed_line", "source": "tel", "sourceUrl": "https://www.bmw.de/de/footer/metanavigation/legal-notice-pool/imprint.html" },{ "phone": "+4989125016010", "formatted": "+49 89 125016010", "country": "DE", "kind": "fixed_line", "source": "tel", "sourceUrl": "https://www.bmw.de/de/footer/metanavigation/legal-notice-pool/imprint.html" }],"whatsapp": null,"telegram": null,"linkedin": null,"facebook": "https://facebook.com/BMWDeutschland","instagram": "https://instagram.com/bmwdeutschland","x": null,"youtube": "https://youtube.com/user/BMWDeutschland","tiktok": null,"contactPageUrl": "https://www.bmw.de/de/footer/metanavigation/legal-notice-pool/imprint.html","contactFormUrl": null,"addressStreet": "Petuelring 130","addressCity": "Munich","addressRegion": null,"addressPostalCode": "80788","addressCountry": "DE","language": "de","phoneCountryUsed": "DE","pagesScanned": ["https://www.bmw.de/de/index.html", "https://www.bmw.de/de/footer/metanavigation/legal-notice-pool/imprint.html"],"pagesScannedCount": 2,"fetchedVia": "fetch","durationMs": 1349,"summary": "BMW: kundenbetreuung@bmw.de, +4989125016000 (2 e-mails, 2 phones); facebook, instagram, youtube.","scrapedAt": "2026-09-06T00:58:12.415Z"}
An unreachable or blocked website:
{ "type": "error", "inputUrl": "https://www.zalando.de", "domain": "zalando.de", "status": "blocked", "httpStatus": 403, "error": "anti_bot_challenge", "scrapedAt": "2026-09-06T00:53:27.239Z" }
📧 How e-mails are found and cleaned
mailto:links, schema.orgemail, Cloudflare-protected addresses (data-cfemailand/cdn-cgi/l/email-protectionlinks are decoded), and addresses written in the page text, includingname [at] company [dot] comandname (at) company.com.- Addresses found only in prose are kept only when their domain really exists (a DNS check), which removes the "partner@YC. He…" class of false matches that page-text scrapers produce.
- Placeholders (
example@,yourname@,test@), template domains (example.com,yourdomain.com), error-tracker addresses (sentry.io,wixpress.com) and image file names that contain an@are dropped. typeisrolefor function mailboxes (info@, sales@, support@, privacy@ …),personalforfirst.last@shapes, andgenericfor everything else (a country office, a brand word, a first name alone). The distinction is a heuristic, not a promise.
📞 How phones are validated
Every candidate goes through the international numbering plan (libphonenumber): the number must be valid for its country, with the country taken from a + prefix, else from the site's country domain (.de, .co.uk, .id …), else from 📞 Default phone country. Numbers in page prose need at least seven national digits, so order numbers and dates are not reported as phones. WhatsApp links (wa.me/…) are read as phone numbers as well.
Use cases for website contact data
🎯 Lead generation and outreach
Export businesses from Google Maps, a directory or a trade-show list, run the domains through Website Contacts API and get the general inbox, a direct phone and the LinkedIn page for each — with the page each contact came from, so an SDR can check it in one click.
🧹 CRM enrichment
Fill the empty e-mail, phone and social columns of an existing account list. isOwnDomain and isFree tell you whether an address is the company's own or a sole trader's gmail.com mailbox before it enters a sequence.
🤖 AI agents and workflows
One flat row per website with a ready summary sentence is what an agent needs: give it a company website, get back who to contact. The Actor is listed for the Apify MCP server, and its input needs nothing but a list of websites.
🔎 Research and due diligence
Registered addresses from schema.org markup, imprint pages and the contact channels a company actually publishes — useful for supplier checks, partnership research and compliance reviews.
Integrations
- API — start a run and read the dataset with any HTTP client, or with the Apify client for Python and JavaScript.
- n8n, Make, Zapier — use the Apify node/module, pick this Actor, map
startUrlsordomainsfrom your sheet, and writebestEmail,bestPhone,linkedinback. Rows are flat, so mappers show every field. - Google Sheets — schedule a run and export the dataset as CSV, or use the Apify Google Sheets integration.
- MCP server — call it from Claude, ChatGPT or any MCP-capable agent through the Apify MCP server;
summaryis written for that. - Webhooks — get notified when a run finishes and fetch the dataset in your own code.
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("lergassy/website-contacts-api").call(run_input={"domains": ["basecamp.com", "hubspot.com", "legalindonesia.id"],"checkEmails": True,})for row in client.dataset(run["defaultDatasetId"]).iterate_items():print(row["domain"], row["bestEmail"], row["bestPhone"], row["linkedin"])
Limits and honest notes
- JavaScript-only sites. A home page that is an empty shell (some Wix apps, single-page apps) yields
status: no_contactswithnote: javascript_rendered_site. Framer sites are handled through their search index. A browser mode may be added if enough users ask — open an Issue. - Anti-bot walls. Large sites behind Akamai, DataDome or Cloudflare challenges come back as
status: blocked, free of charge. Business websites rarely have them; the Chrome-fingerprint fallback clears the simpler ones without a proxy. - E-mail check is domain-level. It tells you whether the domain accepts mail, who hosts it, and whether the address is disposable or a role account. It cannot confirm that a specific mailbox exists — no service can do that reliably without sending mail, and the ones that claim to are guessing for Gmail and Microsoft 365 domains.
- Public data only. The Actor reads what the website publishes to any visitor. It never logs in, never guesses addresses and never pulls data from third-party databases.
❓ FAQ
Is it legal to extract contact details from websites?
The Actor collects only information that the website publishes to every visitor, which is generally lawful to collect. What you may do with it — cold outreach, storage, profiling — is governed by laws such as GDPR, CAN-SPAM and local rules on unsolicited messages. Consult a lawyer for your use case; personal e-mail addresses in particular carry obligations.
Why does it cost less than page-priced contact scrapers?
Because it reads only the pages that carry contacts and charges once per website. Reading five pages instead of twenty makes it both faster and cheaper, and empty sites are not billed at all.
Can I pass bare domains without https://?
Yes — put them in 🌐 Domains, one per line. The 🌐 Websites field needs full URLs because it also accepts files and Google Sheets.
Why is bestEmail info@ when a personal address was also found?
The general inbox is the address a company chooses to publish as its contact, so it ranks first. Personal addresses are still in emails[] with type: "personal". Set your own rule in your workflow if you prefer them.
Does it work through the Apify API and the MCP server?
Yes. Every run can be started through the API, and the Actor is listed for the Apify MCP server so AI agents can call it directly.
Can I integrate it with Google Sheets, n8n or Make?
Yes — see Integrations above. Rows are flat, so every field maps to a column.
Does the run stop when I run out of budget?
Set Max total charge on the run (or in the n8n node). The Actor charges only after a website has been written to the dataset, so you always keep what you paid for.
Your feedback
Found a site where a published address was missed or a wrong number was reported? Open an Issue on this page with the URL — every report is answered, and fixes ship within days. Reviews help other buyers find the Actor.
You might also like
| Actor | What it does |
|---|---|
| Email & Phone Verifier | Deliverability signals, disposable and role flags, US area code, state and time zone for a list of e-mails and phones |
| US Business Leads | Newly registered US businesses from state registries with owner contacts and a lead score |
| Website Business Evidence | Is a website a real, active business? Signals from the site itself |
| Google Flights Scraper | Flight prices and itineraries from Google Flights |