Website Contact Enricher avatar

Website Contact Enricher

Pricing

from $3.00 / 1,000 website enricheds

Go to Apify Store
Website Contact Enricher

Website Contact Enricher

Turn a list of company websites into one validated record per company: general business emails, phone numbers in international format, contact and about pages and official social profiles, each with its source page and a clear status.

Pricing

from $3.00 / 1,000 website enricheds

Rating

0.0

(0)

Developer

Ahmet Yiğit Sertel

Ahmet Yiğit Sertel

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Security

Limited permissions

Share

Give it a list of company websites. For each one it reads the start page and the contact, about and legal pages it finds from there (up to 5 pages by default), and returns one record per company with:

  • General business emails such as info@, sales@, support@, press@ (English and Turkish role names), plus Turkish KEP (registered e-mail) addresses in their own field, kepEmails.
  • Phone numbers in international format (E.164, for example +902125551234), with the country the number was read in.
  • The contact page and about page URLs, and official social profiles (LinkedIn, Facebook, Instagram, ...).
  • A company name when the site's own data supports one, otherwise null.
  • The source of every value: the page it was read on, how it was found and the text as written.
  • A clear status: complete, partial or failed, with a reason code for failures. "Not on the site" (not_found) is kept apart from "could not check" (unknown).

Typical uses: enriching a CRM or lead list with a general contact, cleaning a company directory, finding the contact page of a list of domains. Turkish and English sites are first-class: İletişim, Hakkımızda, Kurumsal are recognised, names keep their Turkish characters and legacy encodings such as windows-1254 are decoded.

All pages of a website are merged into one record with a stable ID, so re-runs and duplicate start URLs never create duplicate companies.

What it deliberately does not do

  • It does not collect personal employee emails. Addresses that look like a person's name are counted in rejectedCandidates and not returned; a business that publishes only a personal-looking address gets no email.
  • It does not guess addresses (info@domain is never invented) and does not run JavaScript.
  • It reads only the websites you give it. It obeys robots.txt, refuses private network addresses, does not follow redirects to other websites, and does not bypass CAPTCHAs, logins or blocks.

Pricing

Pay per event. One event: website-enriched at USD 0.002 per website (USD 2.00 per 1,000 websites with contact data). Platform usage (compute) is included in that price; there is no separate usage charge.

  • What is charged: one event for each website for which the Actor delivers at least one business email, KEP address or phone number. Company names, social profiles and page links are not charged separately.
  • What carries no event charge: websites that failed, were blocked or refused, or were read without yielding a contact value. They are still delivered as records with their status, so you can see why.
  • Not covered by "no event charge": Apify adds its own small start event, apify-actor-start, to every pay-per-event run: USD 0.00005 per start for up to 1 GB of run memory, plus the same again for each additional GB. It applies to every run, including a run in which all sites fail or are empty. Run the Actor at its default memory to pay it once.
  • No per-record charge: there is no charge per dataset item. Only website-enriched and the Apify start event exist.
  • Spending limit: the maximum total charge you set is never exceeded. When it is reached, websites with contact data that would exceed it are not delivered; their domains are saved in the key-value store record WITHHELD_FOR_BUDGET so you can raise the limit and run just those. Records that cost nothing are still delivered. If the limit cannot pay for even one website, the run ends before crawling.
  • Restarts: a platform restart repeats the work but never writes or charges a website twice.

Input

FieldTypeDefaultNotes
startUrlslistrequiredOne URL per company. https://www.example.com or example.com.
maxPagesPerDomain1-105Start page plus contact/about/legal pages found from it.
maxConcurrency1-5010Parallel requests across all websites.
includeSocialLinksbooleantrueOfficial social profiles from header, footer, contact areas.
includeSourceEvidencebooleantrueEvery page that shows each value, with the original text.
allowSubdomainsbooleanfalseAlso read subdomains of the given sites.
proxyConfigurationobjectnoneOptional. One session per run, never rotated to evade blocks.

Several URLs of the same site count as one company. A run is capped at 100,000 pages, so at the default page budget at most 20,000 websites fit in one run; split larger lists. In practice the run time limit is reached first on long lists: set it to match your list (a 30-website test run took under two minutes at the default concurrency). Input errors stop the run before any request.

Example input:

{
"startUrls": [{ "url": "https://zrfmlaw.com/" }, { "url": "https://lrb.co.uk" }],
"maxPagesPerDomain": 5
}

Output

One dataset item per company. Open the Company contacts view or download the dataset as JSON, CSV or Excel.

FieldDescription
id, domain, startUrls, sourceUrlStable company ID, the site key (host without www.) and the URLs you gave.
companyNamevalue, method, confidence, sourceUrl, supportedBy; null when no candidate is convincing.
emails[]value, confidence, method, sourceUrl, domainMatchesSite, optional evidence[].
kepEmails[]Turkish registered e-mail addresses, kept only when the name relates to the company.
phones[]value (E.164), international, country, confidence, method, sourceUrl, optional evidence[].
contactPage, aboutPageurl, verified (page itself was read) and where the link was seen.
socialProfileslinkedin, facebook, instagram, others[].
extractionStatus, confidencecomplete / partial / failed, and the best confidence among the values.
fieldStatusPer field: found, not_found or unknown.
failurenull or {code, message, url}; codes: out_of_scope, blocked_by_policy, robots_disallowed, access_restricted, http_error, timeout, network_error, invalid_url, unsupported_scheme, unsupported_url, detail_unavailable.
pages, pageFailures, qualityFlagsPages planned/fetched, which pages failed, and notes such as js_navigation_suspected.
rejectedCandidatesCounts of invalid or non-general emails and invalid or ambiguous phones that were left out.
adapterVersion, scrapedAtVersion of the extraction logic and extraction time.

Run-level data is in the key-value store: RUN_REPORT (websites by status, skipped and duplicate inputs, billing), RUN_SUMMARY (requests, retries, warnings) and, when a spending limit stops delivery, WITHHELD_FOR_BUDGET. The run exits with an error only when not a single page could be read.

Example output (shortened; a real record from a test run):

{
"domain": "zrfmlaw.com",
"companyName": {
"value": "Zukowski, Rogers, Flood & McArdle",
"method": "structured_data",
"confidence": "high"
},
"emails": [
{
"value": "info@zrfmlaw.com",
"method": "mailto",
"confidence": "high",
"sourceUrl": "https://zrfmlaw.com/",
"domainMatchesSite": true
}
],
"phones": [
{
"value": "+18154592050",
"international": "+1 815 459 2050",
"country": "US",
"method": "tel_link",
"confidence": "high",
"sourceUrl": "https://zrfmlaw.com/"
}
],
"contactPage": { "url": "https://zrfmlaw.com/contact-us/", "verified": true },
"extractionStatus": "complete",
"failure": null
}

A website that cannot be read still produces a record, so nothing disappears silently. It has no event charge:

{
"domain": "sarniaplumbing.com",
"emails": [],
"phones": [],
"extractionStatus": "failed",
"failure": {
"code": "network_error",
"message": "Start URL refused: Could not resolve sarniaplumbing.com: getaddrinfo ENOTFOUND sarniaplumbing.com (https://sarniaplumbing.com/)",
"url": "https://sarniaplumbing.com/"
}
}

Status fields

FieldValuesMeaning
extractionStatuscomplete / partial / failedcomplete: every planned page was read. partial: some pages were not (see pageFailures). failed: no page was read (see failure).
fieldStatus.*found / not_found / unknownnot_found is claimed only after a complete look at the site. After failures, with the page limit reached, or when navigation looks script-built, absence is unknown.
confidencehigh / medium / low / noneBest confidence among emails and phones. high: mailto, tel: or structured data. medium: text in a contact area. One level lower when the email domain differs from the site's.
contactPage.verifiedbooleantrue: the page itself was read. false: only a link to it was seen.
qualityFlagsstringsFor example page_limit_reached, js_navigation_suspected, email_domain_differs_from_site, multiple_linkedin_profiles.

How to use

  1. Paste your company websites into Company websites (or upload a list).
  2. Leave the defaults for a first run; set a maximum total charge if you want a hard spending limit.
  3. Start the run. Open the Company contacts view or download the dataset as JSON, CSV or Excel.
  4. Filter on extractionStatus and failure.code to see which sites need another look.

How it works

  1. Each start URL defines a company by its host (www. is ignored). Only that host is fetched; subdomains only if you turn on Read subdomains. If a site redirects its own page to one other host of the same registrable domain (gitlab.com to about.gitlab.com), that host is followed and flagged canonical_redirect_followed.
  2. The start page is read. From its links the Actor picks, conservatively, up to two contact pages, up to two legal-notice or privacy pages (where companies state their registered e-mail, data-protection contact and KEP address), one support or press page when the start page shows no address, and one about page, all within the page budget. Blog posts, product pages, links with query strings and other sites are never followed.
  3. If a page shows no menu links at all but looks script-driven, up to four conventional paths (/contact, /iletisim, /about, /hakkimizda) are tried. A page is accepted as a contact page only if its own title or heading says so.
  4. Values are taken from mailto:/tel: links, the organization's structured data (JSON-LD, microdata) and clearly identified contact areas (address, footer, contact sections, contact pages), and role mailboxes on the company's own domain in page text. Other text is ignored.
  5. The pages of a site are merged, de-duplicated and written once, at the end of the run.

Run time limit

The Actor stops shortly before the run's time limit (about 8% of it, between 30 and 120 seconds) and writes what it has read: websites not reached appear as failed with detail_unavailable, and RUN_REPORT.stoppedForTimeLimit is true. Nothing is lost silently. (A run killed for memory still loses its output, because records are written when the crawl ends.)

Known limitations

  • Plain HTTP only: contact details that appear only after JavaScript runs are not seen. The record says so (js_navigation_suspected, unknown) instead of reporting not_found.
  • Only role/general mailboxes are returned. The role-name vocabulary is finite (English, Turkish and some German, French, Spanish, Italian); an unusual name or a mailbox named after a place is missed. A KEP address is kept only when its name relates to the company.
  • Numbers are returned only when the country is certain: from the site's country domain, structured data, page language, an international prefix, or a North American number in its usual written form. Numbers in running body text outside header, footer, contact areas and contact pages are not read.
  • A site that redirects to another domain is not followed (out_of_scope).
  • Sites that block datacenter traffic return 403 and are reported as access_restricted; the Actor does not work around it.
  • A site whose robots.txt cannot be read is not fetched (reported as timeout, network_error or http_error), and can take up to about 50 seconds to fail.
  • Records are written when the run ends, and a platform migration restarts the run from scratch.
  • Tested on lists of up to 100 websites; behaviour on lists of thousands has not been measured. Start with a few thousand websites per run.
  • No accuracy figure is claimed: results are as complete as the websites publish them.

Responsible use

The Actor reads public pages that anyone can open and keeps only company-level contact channels. Business contact data can still be personal data (for example a sole trader's address), and rules such as GDPR, KVKK and e-privacy/anti-spam laws apply to what you do with it. You are responsible for a lawful basis for your use, for each website's terms, and for honouring opt-outs.