Website Contact & Email Extractor — Verified Emails + Socials avatar

Website Contact & Email Extractor — Verified Emails + Socials

Pricing

from $8.00 / 1,000 contact records

Go to Apify Store
Website Contact & Email Extractor — Verified Emails + Socials

Website Contact & Email Extractor — Verified Emails + Socials

Turn a list of website URLs into clean CRM rows: verified emails (MX-checked, role-tagged), E.164 phones, and social profiles — one de-duplicated row per company. You pay only per company we find a real contact for; empty and failed sites are free. Junk addresses and numbers are filtered out.

Pricing

from $8.00 / 1,000 contact records

Rating

0.0

(0)

Developer

Blackcube Agency AB

Blackcube Agency AB

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

9 hours ago

Last modified

Share

Feed it a list of websites. Get back one clean CRM row per company — verified emails, phone numbers, and social profiles.

Point it at any list of company websites (a Google Maps export, a Shopify store list, a jobs-board scrape, or a CSV your sales team already has) and it returns a deduplicated contact record for each one: every email it can find, MX-verified and tagged by role, phone numbers normalized to E.164, and canonical LinkedIn / X / Facebook / Instagram / YouTube / TikTok / GitHub profile URLs.

You pay per company we actually reach a contact for. Not per page crawled. A website that returns nothing, times out, or no longer exists costs you nothing — it still appears in your results with a reason, so gaps in your list stay auditable. A record has to carry a real email or phone number to be billable: if all we could find was a social profile link, you still get the record, free.

Unofficial — not affiliated with, endorsed by, or connected to any of the websites it visits. It reads only data those sites already publish publicly to any browser.


Why this one

1. Predictable billing — per company, not per page

Most contact scrapers bill per page crawled, so a headline like "$2 per 1,000" turns into a much larger invoice once a single site with deep navigation gets crawled for hundreds of pages. That mismatch is the most common complaint in this category.

Here, the unit you are billed for is the unit you care about: one company record. A company whose contact details take 12 pages to find costs exactly the same as one that gives them up on the homepage. And because the total page budget is bounded by websites × maxPagesPerDomain before the run even starts, a runaway crawl is structurally impossible.

This actorTypical per-page scrapers
Billing unit1 company record1 page crawled
Website with no contactsFreeCharged for every page
Dead / timed-out domainFree, with a reasonCharged for the attempt
Record with no email or phoneFreeCharged as a result
Cost of a deep siteUnchangedMultiplies without warning
Email verificationIncludedUsually a paid add-on

2. Clean data — the junk is filtered out, not shipped

Regex-only extractors are notorious for reporting things like //cdn.jsdelivr.net/npm/bootstrap@5.2.3 as an email address, and for emitting "uncertain" phone columns full of digit noise like 035500000000002. This actor validates before it delivers:

  • Emails are checked against the public suffix list, so image filenames (logo@2x.png), CDN package specs (bootstrap@5.2.3), Sentry DSNs, build hashes, and placeholder addresses (you@example.com, firstname.lastname@…) never reach your dataset.
  • Phone numbers are parsed with Google's libphonenumber and returned only if they are genuinely valid, normalized to E.164 (+14155550132). Order numbers, dates, and prices are dropped. There is no "uncertain" column, because uncertain data is not a deliverable.
  • Social links are canonicalized to real profile URLs; share and intent widgets (facebook.com/sharer, twitter.com/intent/tweet) are discarded.

3. It finds addresses other scrapers miss

Contact details are deliberately hidden from naive scrapers. This one reads all of it:

  • mailto: links and Cloudflare-obfuscated addresses (data-cfemail / /cdn-cgi/l/email-protection, XOR-decoded)
  • HTML-entity obfuscation (info@acme.com) and bracketed forms (info [at] acme [dot] com)
  • JSON-LD structured dataOrganization, LocalBusiness, ContactPoint, and sameAs blocks, which carry the authoritative company name, email, phone, and postal address
  • Plain text and footers

And it looks in the right places. With a limited page budget, which pages get crawled decides whether you get a decision-maker's address or nothing, so pages are ranked before fetching: legally-mandated imprint pages first (/impressum, /mentions-legales — in the EU these always carry a real address), then contact, about, team, support, press. Non-English paths are recognized, so a German or French site does not come back empty.

4. Verified emails, honestly described

Every address is checked and labelled — included in the per-record price, not a paid add-on:

statusMeaning
validThe domain publishes a working mail exchanger and the address is well-formed.
invalidThe domain has no mail exchanger, or the address is malformed. It will bounce.
disposableA throwaway-inbox provider (mailinator, guerrillamail, …). Worthless as a lead.
riskyReal, but lower confidence: a free consumer provider (gmail, outlook) or a shared role inbox (info@, support@).
unknownThe DNS lookup itself failed. Never reported as invalid — you are not told an address is dead when we simply could not check.

What this check is: syntax validation, MX/DNS resolution, and disposable / free-provider / role-address classification. What it is not: a per-mailbox SMTP probe. Apify's infrastructure blocks outbound port 25, so no actor running natively on the platform can perform a true RCPT-TO handshake — and even where it is possible, Gmail, Outlook, and Yahoo tarpit those probes from datacenter IPs. Tools that advertise "SMTP verified" from inside Apify are describing a check they cannot run. This one tells you exactly what it did, and the reason field on every result says why it reached that status.

5. Role tagging and decision-maker filtering

Every email is classified: executive (founder, CEO, owner), personal (a named individual), sales, marketing, support, hr, finance, legal, info (generic inbox), or other. Use Only these contact roles to keep just the ones you want, or flip Decision-makers only to drop every generic inbox in one click.

Each record also carries a primaryEmail — the single best contact, picked by ranking the company's own domain above third-party addresses, then executives above named people above sales above generic inboxes. When you need exactly one address per company for a mail merge, that field is it.

6. One row per company, already deduplicated

Paste acme.io, https://www.acme.io/, and https://acme.io/pricing?utm_source=news and you get one record, charged once. Deduplication happens by registrable domain, across the whole run, on both input and output. No second "merge & deduplicate" actor required.


Input

FieldTypeDefaultWhat it does
startUrlsarrayThe company websites. Full URLs or bare domains both work. Several URLs for one company collapse into one row.
domainsarray of strings(empty)Alternative plain-text list of domains, merged with the above.
maxPagesPerDomaininteger10Hard cap on pages per company (1–50). Total run pages can never exceed websites × this.
verifyEmailsbooleantrueRun the MX/DNS deliverability check. Included in the price.
roleFilterarray(empty)Keep only these roles. Empty returns every address.
excludeGenericEmailsbooleanfalseDecision-makers only: keep executive and personal, drop every generic inbox.
skipDomainsWithoutContactsbooleanfalseOmit the free "nothing found" rows entirely. They are never charged either way.
respectRobotsTxtbooleanfalseSkip pages disallowed by robots.txt. Off by default, because many sites disallow crawlers wholesale for pages any browser can open.
maxConcurrencyinteger20Pages fetched in parallel across all websites.
proxyConfigurationobjectApify Proxy (auto)Automatic selection picks a datacenter IP — enough for ordinary company sites, and what keeps the price low. Switch to residential only for hardened targets.

Output

One JSON record per company. See .actor/sample-output.json for complete records from a real run.

// abridged from a real run — see .actor/sample-output.json for full records
{
"domain": "fastmail.com",
"companyName": "Fastmail",
"primaryEmail": "press@fastmail.com",
"emails": [
{
"address": "press@fastmail.com",
"role": "marketing",
"foundOn": "https://www.fastmail.com/company/about/",
"verification": {
"status": "risky",
"hasMx": true,
"isDisposable": false,
"isFreeProvider": false,
"isRoleAddress": true,
"reason": "roleAddress"
}
}
],
"phones": ["+61294750859"],
"socials": {
"linkedin": "https://www.linkedin.com/company/fastmail",
"twitter": "https://x.com/Fastmail",
"facebook": "https://www.facebook.com/Fastmail",
"github": "https://github.com/fastmail"
},
"emailCount": 5,
"phoneCount": 1,
"pagesCrawled": 8,
"errorReason": null
}
FieldTypeNotes
domainstringRegistrable domain. The dedup key and the billing unit.
startUrl, inputUrlsstring, arrayWhere the crawl began, and every input line that mapped to this company.
companyName, description, addressstring | nullFrom JSON-LD, og:site_name, or the page title/meta.
primaryEmailstring | nullThe single best contact. Identical to emails[0].address.
emailsarray{ address, role, foundOn, verification }, best contact first.
phonesarrayValid numbers only, E.164.
socialsobjectCanonical profile URLs: linkedin, twitter, facebook, instagram, youtube, tiktok, github.
emailCount, phoneCountintegerFor filtering a CSV export without parsing arrays.
pagesCrawled, crawledUrlsinteger, arrayExactly which pages were fetched, in order.
scrapedAtstringISO-8601.
errorReason, errorMessagestring | nullnull on a billed record; set on a free row.

Dataset views

Contacts and Skipped & failed (free). Views project columns but cannot filter rows, so both kinds of row appear in both views with the other's columns blank. To separate them in code, split on errorReason: it is null on a contact record and set on a free row.


Why a website returns no contacts

Every one of these is a free row — never billed — and tells you what happened, so a gap in your lead list is explained rather than silent.

errorReasonMeaning
noContactsFoundCrawled fine, but the site publishes no contact details (or your filters removed them all).
domainNotResolvedThe domain does not resolve — expired, parked, or mistyped.
connectionFailedConnection refused, or an unusable TLS certificate.
timeoutThe site did not respond in time.
notFoundThe start URL returned 404/410.
accessDeniedThe site refused the request (401/403).
serverErrorThe site returned a 5xx.
blockedRate-limited or stopped by bot protection. Try residential proxies.
invalidInputThat input line is not a usable website URL or domain.
unknownSomething else; errorMessage carries the detail.

Tips for better results

  • Raise maxPagesPerDomain for stubborn sites. The default of 10 is tuned for cost. Large corporate sites that bury contact details behind several navigation levels do better at 20–30. The cost per company does not change.
  • Use excludeGenericEmails for outreach, leave it off for coverage. Generic inboxes are often the only published address for small businesses.
  • Switch to residential proxies only if you see blocked rows. Datacenter is cheaper and sufficient for the large majority of company websites.
  • Chain it after any list-building actor. Google Maps, Shopify, jobs boards, directories — anything that produces website URLs feeds straight in.
  • Raise the run timeout for very large lists. The default is 1 hour, which comfortably covers roughly 1,500–2,000 websites at the default page budget. Results are written and billed per company as each one finishes, so even if a run does hit its timeout you keep every company already completed — you never lose (or pay twice for) finished work.

This actor collects only publicly published business contact information — the addresses and numbers companies put on their own websites specifically so customers can reach them. It does not log in, bypass authentication, or touch private data.

Contact data about identifiable people is personal data under the GDPR and similar laws. You are the controller for how you use it: you are responsible for having a lawful basis (commonly legitimate interest for B2B outreach), for honoring opt-outs and suppression lists, and for meeting the disclosure and marketing rules in your recipients' jurisdictions. The role and isRoleAddress fields exist partly to help you target shared business inboxes rather than individuals where that is the safer choice.


Unofficial project. Uses only publicly available data. Not affiliated with any website it visits.