Company Enrichment from Website: Emails, Phones & Tech (Lite) avatar

Company Enrichment from Website: Emails, Phones & Tech (Lite)

Pricing

Pay per usage

Go to Apify Store
Company Enrichment from Website: Emails, Phones & Tech (Lite)

Company Enrichment from Website: Emails, Phones & Tech (Lite)

Free Lite: up to 25 results per run. Full version adds unlimited results, monitor mode and alerts: https://apify.com/cleanyield/company-enrichment-from-website. Email extractor and company enrichment by domain: one clean row per company with emails (generic vs personal), E.164 phones, socials, tech

Pricing

Pay per usage

Rating

0.0

(0)

Developer

Clean Yield

Clean Yield

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 hours ago

Last modified

Security

Limited permissions

Share

Company Enrichment from Website: Emails, Phones & Tech

Free Lite version of Company Enrichment from Website: Emails, Phones & Tech: up to 25 results per run, no monitor mode or alerts. Need more results, scheduled monitoring and alerts? Use the full version — you only pay for valid, complete results.

You only pay for valid, complete results — one fixed price per company, never per page. Sites that are down, parked or blocked are reported and never charged.

Switching from another scraper? Paste your current input as is

startUrls ([{ "url": "https://acme.com" }]), urls and maxRequestsPerStartUrl are accepted unchanged, and outputFormat: "leader-compatible" returns the usual contact-scraper field names (emails, phones, linkedIns, twitters, whatsapps...) next to ours. Your integration keeps working; the bill becomes one fixed price per company.

Typical fieldHere
startUrls[].url / urlsdomains (both accepted)
maxRequestsPerStartUrl / maxPagesPerSitemaxPagesPerDomain
emails, phones (string lists)emails[], phones[] (classified, E.164) + primaryEmail, primaryPhone
linkedIns, twitters, facebooks, instagrams...socialProfiles.linkedin, .x, .facebook, .instagram...

Full mapping in Migrating from other scrapers.

Email extractor and company enrichment by domain. Turn a list of domains or website URLs into one clean row per company: generic and role emails (info@, sales@, ventas@), phone numbers in E.164 (WhatsApp marked), LinkedIn/X/Facebook/Instagram/YouTube/TikTok/GitHub/Pinterest/Threads/Telegram profiles, tech stack (Shopify, HubSpot, WordPress, Intercom...), hiring signals (careers page, ATS, open roles) and the latest blog posts. Built for lead-gen agencies, SDR teams and Clay / n8n / Make users who enrich account lists. Also works as a website contact details scraper, a lead scraper for emails, phone numbers and social media links, a domain enrichment API, a tech stack lookup and a "is this company hiring?" checker.

Want to try first? Use the free Lite version: https://apify.com/cleanyield/company-enrichment-from-website-lite

Why use this email extractor and company enrichment tool?

  • 📇 More contacts, no junk: 86% of companies with at least one email and 58% with a phone on our fixed test set of real sites, from contact, legal notice/Impressum, privacy, about and team pages in 6 languages, page footers, structured data and obfuscated emails — every address validated (real TLD, no placeholders, no regulators or agencies).
  • ✅ One row per company, fixed price, no runaway runs: no surprise bills from crawling thousands of pages of one site. A hard cap of 10 pages and 15 seconds per domain that is always enforced, the maximum cost printed before the run starts, and nothing charged for sites that fail.
  • ☎️ Company phone numbers that actually show up: read from contact, legal notice, footer and structured data, normalized to E.164 with country, deduplicated across formats and with junk (IDs, years, test numbers) filtered out.
  • 🧹 Clean, normalized output: phones in E.164 (deduplicated across formats, vanity numbers like 1-800-4HOTDOG converted), emails classified as generic / role / personal, Cloudflare-protected emails decoded, junk removed (posthog-js@1.8.0, logo@2x.png, 555-01xx, +1 234 567 8910).
  • 📈 Buying signals, not just contacts: is the company hiring (and on which ATS, with open roles counted from Greenhouse, Lever and Ashby public APIs), which tools it uses, what it published last.
  • 🔁 Built for monitoring: schedule it and get only companies that changed — started hiring, added HubSpot, new post, new contact.
  • ⚡ Fast: median 1.8 s per company (p90 3.1 s, max 5.9 s) on 64 real company websites; sync mode for Clay, n8n and Make.
  • 🎯 Pro mode in one checkbox: buying-intent score 0–100, estimated size, fit with your ideal customer, a "why now" line and a one-sentence AI insight per company.
  • 🔒 GDPR-friendly by default: personal emails are omitted unless you turn them on; robots.txt respected; bot challenges never bypassed.
  • 🤖 AI-agent ready: pay-per-result only (no usage fees), works through MCP without an Apify account.

This Actor vs typical alternatives

Measured head-to-head on 2026-11-05: this Actor and a widely used contact scraper, run on Apify with the same 10 real domains from our fixed test set (testset/cases.json: SaaS, restaurants, ecommerce, LATAM), same 10-page budget per site. Our full-set numbers (65 domains) are in "Measured quality".

Same 10 domains, same page budgetTypical alternative (measured)This Actor
Outputone row per site (merged contacts)one row per company + name, country, tech, hiring
Expected fields filled (68 checks)47%100%
Contact fields correct (emails, phones, socials)79%100%
Companies with at least one email / confirmed phone8 / 2 of 1010 / 6 of 10
Unique emails found1127
Phones normalized (E.164), junk filtered✗ (mixed formats, IDs and years as "uncertain" phones)✓
Run time for the 10 companies71 s5 s
Cost per 1,000 companies~USD 16 (pay per page)USD 10, fixed
Monitor mode (only changes) + alerts✗✓

What data can you get from a company website?

FieldExample
companyNamePlausible Analytics (from og:site_name, ld+json Organization, title or domain — slogans and "Inc." removed)
emails[]{ "email": "hello@plausible.io", "type": "generic", "isCompanyDomain": true, "foundOn": "https://plausible.io/contact" }
phones[]{ "phone": "+12122542246", "national": "(212) 254-2246", "country": "US", "source": "tel-link", "isWhatsApp": false }
primaryEmail, primaryPhonethe best email (company-domain generic inbox first) and phone (E.164) — ready for a spreadsheet or CRM column
contactPageUrlthe contact page (or legal notice) that was read
socialProfileslinkedin, x, facebook, instagram, youtube, tiktok, github, pinterest, threads, telegram, discord, reddit — canonical company URLs
addressfrom ld+json: street, locality, region, postal code, ISO country
technologies[]{ "name": "Shopify", "category": "ecommerce" } — ~170 signatures in 14 categories
hiring{ "isHiring": true, "careersUrl": "https://job-boards.greenhouse.io/n26", "atsProvider": "greenhouse", "openRolesCount": 36, "evidence": "36 open role(s) on the public greenhouse job board" }
latestPosts[]{ "title": "...", "url": "...", "publishedAt": "2026-10-22T10:00:00.000Z" }
language, countryen, EE (ISO 639-1 / ISO 3166-1)
sourcesChecked[]every request made (type, URL, HTTP status, ms) — full transparency
changeType, changes[]monitor mode only: new / changed and what changed
proPro mode only: buyingIntentScore (0–100), buyingSignals[], estimatedSize, icpFitScore, whyNow, trend (see Pro mode)
insightPro mode only: one sentence written by AI from this row's facts only
schemaVersion1.1.0 — fields are only added, never renamed (CHANGELOG.md)

What counts as a valid (charged) result

A company is charged only if its website could be read and the row has domain + companyName + at least one contact datum (email, phone, social profile or address) or one signal (careers page/ATS, a blog post, or a business technology beyond hosting/CDN). Everything else is free and listed with its reason in the FAILED_DOMAINS record of the run's key-value store:

StatusMeaning
invalid_domainthe input is not a domain or URL
unreachableDNS does not resolve, invalid TLS certificate, connection error or timeout
blockedHTTP 401/403/429 or a bot challenge (Cloudflare and similar) — never bypassed
not_foundthe home page returns 404/410
parkedthe domain is parked or for sale
robots_disallowedrobots.txt disallows the site for all crawlers
no_datathe site was read but had nothing useful

Example output

A real item from a run of the default input (sourcesChecked and latestPosts shortened):

{
"domain": "plausible.io",
"url": "https://plausible.io/",
"companyName": "Plausible Analytics",
"description": "Plausible is a lightweight and open-source Google Analytics alternative. Your website data is 100% yours and the privacy of your visitors is respected.",
"language": "en",
"country": "EE",
"emails": [
{
"email": "hello@plausible.io",
"type": "generic",
"isCompanyDomain": true,
"foundOn": "https://plausible.io/contact"
},
{
"email": "jobs@plausible.io",
"type": "role",
"isCompanyDomain": true,
"foundOn": "https://plausible.io/jobs/customer-success"
},
{
"email": "privacy@plausible.io",
"type": "role",
"isCompanyDomain": true,
"foundOn": "https://plausible.io/privacy"
}
],
"phones": [],
"primaryEmail": "hello@plausible.io",
"primaryPhone": null,
"contactPageUrl": "https://plausible.io/contact",
"socialProfiles": {
"linkedin": "https://www.linkedin.com/company/plausible-analytics",
"x": "https://x.com/PlausibleHQ",
"facebook": null,
"instagram": null,
"youtube": null,
"tiktok": null,
"github": "https://github.com/plausible",
"pinterest": null,
"threads": null,
"telegram": null,
"discord": null,
"reddit": null
},
"address": {
"streetAddress": "Västriku tn 2",
"locality": "Tartu",
"region": null,
"postalCode": "50403",
"country": "EE",
"formatted": "Västriku tn 2, Tartu, 50403, EE"
},
"technologies": [
{
"name": "Plausible",
"category": "analytics"
},
{
"name": "Jekyll",
"category": "framework"
}
],
"hiring": {
"isHiring": true,
"careersUrl": "https://plausible.io/jobs/customer-success",
"atsProvider": null,
"openRolesCount": null,
"evidence": "Careers page lists openings or asks to apply"
},
"latestPosts": [
{
"title": "Google Analytics pricing: Free GA4 and paid GA360 explained",
"url": "https://plausible.io/blog/google-analytics-pricing",
"publishedAt": "2026-10-22T10:00:00.000Z"
},
{
"title": "Shopify, Squarespace and Webflow analytics: What you can track and what’s missing",
"url": "https://plausible.io/blog/cms-built-in-analytics",
"publishedAt": "2026-10-02T07:00:00.000Z"
},
{
"title": "What is direct traffic? What does it include, and why is its share the highest?",
"url": "https://plausible.io/blog/direct-traffic",
"publishedAt": "2026-09-16T08:00:00.000Z"
},
{
"title": "Web analytics: what it is, how it works and how to use it",
"url": "https://plausible.io/blog/web-analytics",
"publishedAt": "2026-09-11T07:00:00.000Z"
},
{
"title": "Web analytics vs product analytics: core difference and how to choose?",
"url": "https://plausible.io/blog/web-analytics-vs-product-analytics",
"publishedAt": "2026-09-03T01:00:00.000Z"
}
],
"personalEmailsOmitted": 0,
"sourcesChecked": [
{
"url": "https://plausible.io/robots.txt",
"type": "robots",
"status": 200,
"ok": true,
"ms": 292
},
{
"url": "https://plausible.io/",
"type": "home",
"status": 200,
"ok": true,
"ms": 292
},
{
"url": "https://plausible.io/contact",
"type": "contact",
"status": 200,
"ok": true,
"ms": 50
},
{
"url": "https://plausible.io/blog/feed.xml",
"type": "feed",
"status": 200,
"ok": true,
"ms": 63
},
{
"url": "https://plausible.io/privacy",
"type": "privacy",
"status": 200,
"ok": true,
"ms": 65
},
{
"url": "https://plausible.io/about",
"type": "about",
"status": 200,
"ok": true,
"ms": 66
},
{
"url": "https://plausible.io/terms",
"type": "terms",
"status": 200,
"ok": true,
"ms": 23
},
{
"url": "https://plausible.io/sitemap.xml",
"type": "sitemap",
"status": 200,
"ok": true,
"ms": 77
},
{
"url": "https://plausible.io/imprint",
"type": "legal",
"status": 200,
"ok": true,
"ms": 80
},
{
"url": "https://plausible.io/jobs/customer-success",
"type": "careers",
"status": 200,
"ok": true,
"ms": 31
}
],
"pagesFetched": 9,
"durationMs": 556,
"scrapedAt": "2026-11-04T22:27:09.025Z",
"outputVersion": 1,
"schemaVersion": "1.1.0",
"changeType": null,
"changes": [],
"pro": null,
"insight": null
}

Pro mode: who to call first and why

Turn on Pro mode (one checkbox) and every company also gets:

FieldWhat it tells you
pro.buyingIntentScore0–100, easy to sort: hiring (more for 20+ open roles), a post in the last 30 days, sales/marketing tools (HubSpot, Intercom, Calendly...), selling online, being reachable, and in monitor mode started hiring / new tools / new posts. Every point comes with its reason in pro.buyingSignals[].
pro.estimatedSize1-10, 11-50, 51-200, 201-1000 or 1000+, with the public evidence used ("72 open roles", "phones in 5 countries").
pro.icpFitScore0–100 = share of your ideal-customer criteria that the company meets (idealCustomer: countries, languages, technologies to have or not have, hiring, minimum open roles, keywords, sizes, has contact), with icpMatched / icpMissed.
pro.whyNowone line with the concrete reason to reach out today: "72 open roles on lever.", "Started hiring (3 open roles)."
pro.trendin monitor mode, what changed since you started monitoring (up to 90 snapshots per company): open roles +15, tools added, new posts. Nobody else has this history: it grows with every scheduled run.
insightone sentence by AI (Claude Haiku) using only the facts in the row, for example "Blue Bottle Coffee is hiring 72 people on Lever and runs Zendesk — a good moment to pitch support tooling." If the AI is not available the field is null and nothing extra is charged for it.

Pro mode does not read more pages, so it is just as fast. Price: see below.

Use cases

  • Enrich a lead or account list: paste domains from your CRM or a spreadsheet and get emails, phones and LinkedIn per company.
  • Tech-stack prospecting: find which accounts run Shopify, Magento, HubSpot, Intercom or Tiendanube, and target them.
  • Hiring signals: companies hiring (and how many open roles) are buying — route them to your SDRs.
  • Personalized outreach: open with their latest blog post or their new job openings.
  • Account monitoring: weekly alerts when a target account starts hiring, adds a tool or publishes.

Schedule it: monitor companies and get only what changed

  1. Turn on Monitor mode (optionally give it a Monitor state name, e.g. my-accounts-weekly).
  2. Add a Slack webhook or a webhook URL (Zapier, Make, n8n — use it to send emails too) for alerts.
  3. Open Schedules → Create schedule → daily or weekly → select this Actor with your input.

Each run compares every company with its previous profile and outputs only companies that are new or changed (each item has changeType: new or changed, and a changes list). You pay only for those. Detected changes: started_hiring, stopped_hiring, open_roles_changed, technology_added, technology_removed, new_post, contact_added, contact_removed, social_changed, company_info_changed. Hosting/framework changes are ignored, and a contact is only reported as removed if the page where it was seen was read successfully — no false alarms from a flaky page.

With Pro mode, monitored companies also get pro.trend (open roles, tools and posts since the first run, up to 90 snapshots) and buying_intent signals from the changes.

Example Slack alert: Tiendanube (tiendanube.com): Started hiring; Added HubSpot; Removed Intercom.

How much does it cost to enrich companies?

Pay per result (pay per event): no subscription, no platform usage fees, and failed, blocked or empty sites are free. One price per company, no matter how many pages are read (at most maxPagesPerDomain, default 10). The log shows the maximum cost of the run before it starts.

Companies returnedCost
100 companiesUSD 1
1,000 companiesUSD 10
10,000 companiesUSD 100

Pro mode adds USD 0.02 per company (USD 20 per 1,000) on top of the base price, only for companies returned.

Apify subscribers get a lower price per company (tiers on the Pricing tab). In monitor mode you pay only for new or changed companies. Set Maximum cost per run and the Actor stops exactly there.

How to use the company enrichment scraper

  1. Click Try for free.
  2. Paste your domains in Domains or website URLs (e.g. plausible.io, https://www.katzsdelicatessen.com).
  3. Click Start and download JSON, CSV or Excel, or get the data through the API.

Input example

{
"domains": ["plausible.io", "katzsdelicatessen.com", "tiendanube.com"],
"includePersonalEmails": false,
"maxPagesPerDomain": 10
}

Input fields

FieldDescription
domainsCompany domains or website URLs (also accepts emails: their domain is used). Bulk: thousands per run.
sectionsWhich parts to collect: contacts, social, technologies, hiring, posts (default: all). Fewer sections = fewer pages read.
includePersonalEmailsOff by default: only generic/role inboxes; personal-looking emails are omitted and counted in personalEmailsOmitted.
maxPagesPerDomainHard cap of pages per company (default 10): home + contact, legal notice/Impressum, privacy, about, team, terms, careers or ATS API, blog/feed, sitemap — in order of how likely they are to have contacts.
maxPostsLatest blog posts per company (default 5, 0 disables).
domainTimeoutSecsMax time per company (default 15 s); returns what was found when reached.
maxResultsStop after this many companies (cost cap).
maxConcurrencyCompanies processed in parallel (default 10).
respectRobotsTxtOn by default.
monitorMode, monitorStateName, notifyWebhookUrl, notifySlackWebhookUrlMonitor mode and alerts (see Schedule it).
proMode, idealCustomerPro mode extras (see Pro mode); idealCustomer example: { "countries": ["US"], "technologies": ["Shopify"], "mustBeHiring": true, "sizes": ["11-50", "51-200"] }.
savedListName, saveListGive your company list a name and the Actor remembers it for the next (scheduled) runs; new domains are added to it.
outputFormatstandard (default) or leader-compatible (see Migrating from other scrapers).
startUrls, maxRequestsPerStartUrlAliases of domains and maxPagesPerDomain, so the input of other contact scrapers works as is.
proxyConfigurationOptional; off by default (company websites are read directly).

Integrations and code examples

Bulk input is fine: thousands of domains in one run, 10 in parallel by default, a few requests at most per site, strict time limits and your budget cap.

Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("cleanyield/company-enrichment-from-website").call(
run_input={"domains": ["plausible.io", "katzsdelicatessen.com"], "sections": ["contacts", "social", "hiring"]}
)
for company in client.dataset(run["defaultDatasetId"]).iterate_items():
print(company["companyName"], [e["email"] for e in company["emails"]], company["hiring"]["isHiring"])

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('cleanyield/company-enrichment-from-website').call({
domains: ['plausible.io', 'katzsdelicatessen.com'],
sections: ['contacts', 'social', 'technologies'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((c) => [c.companyName, c.phones[0]?.phone, c.technologies.map((t) => t.name)]));

Sync mode (Clay, n8n, Make, HTTP)

One HTTP call returns the company profile directly (typically 2–6 s for one domain, always under ~20 s thanks to the per-domain time limit):

POST https://api.apify.com/v2/acts/cleanyield~company-enrichment-from-website/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>
Content-Type: application/json
{ "domains": ["plausible.io"], "domainTimeoutSecs": 15 }

An empty array means the site could not be enriched (and was not charged).

n8n, Make and Clay

Ready-made templates in this Actor's repository: n8n "Enrich company domains from Google Sheets" and "Weekly buying signals to Slack", Make "Enrich new CRM accounts", Clay "HTTP API enrichment column". To send results to Google Sheets automatically, use the n8n Sheets template (reads domains from a sheet, writes one row per company with email, phone, LinkedIn and hiring) or Make's Google Sheets module after Run an Actor. Monitor mode already sends alerts to Slack or any webhook, and every run saves a visual dashboard (record DASHBOARD, link in SUMMARY) you can open and share. Or use the official Apify node/app → Run an Actor → cleanyield/company-enrichment-from-website.

Migrating from other scrapers

Coming from a contact details scraper? Paste the same input and choose the output you need — your integration keeps working.

Their inputHereTheir output fieldHere (outputFormat: "leader-compatible")Here (standard)
startUrls [{ "url": "https://acme.com" }]same (or domains: ["acme.com"])emails (strings)emailsemails[].email + type, primaryEmail
maxRequestsPerStartUrlsame (= maxPagesPerDomain)phones (strings)phones (E.164)phones[].phone + national, country, isWhatsApp
maxDepth, sameDomainnot needed: same site only, best pages picked for youlinkedIns, twitters, facebooks, instagrams, youtubes, tiktoks, pinterests, discords, threads, telegrams, redditssame names (lists)socialProfiles.linkedin, .x, .facebook...
useBrowsernot needed (HTML first, fast)whatsappswhatsapps (wa.me links)phones[].isWhatsApp
proxyConfigproxyConfigurationurl, domain, originalStartUrl, depthsameurl, domain

Differences you will notice: one row per company instead of one row per page (no deduplication step), fixed price per company instead of per page, phones normalized to E.164, emails classified and validated. With leader-compatible, our extra data (technologies, hiring, posts, emailDetails, phoneDetails) comes in the same row with our own field names.

For developers: use it inside your own Actor

Building an Actor, an agent tool or a SaaS that needs company data? Call this one and focus on your product:

  • Fast and synchronous: run-sync-get-dataset-items answers in seconds per domain, with a hard time limit per domain.
  • Stable schema: fields are only added, never renamed; every item has schemaVersion (CHANGELOG.md).
  • Clear price: a fixed price per valid company, nothing for failures; your users' runs never get surprise bills.
import { Actor } from 'apify';
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('cleanyield/company-enrichment-from-website').call(
{ domains: ['acme.com'], sections: ['contacts', 'social'] },
{ waitSecs: 60 },
);
const { items: [company] } = await client.dataset(run.defaultDatasetId).listItems();
await Actor.pushData({ domain: 'acme.com', email: company?.primaryEmail ?? null, phone: company?.primaryPhone ?? null });

Measured quality

Measured on a fixed test set of 65 real domains (SaaS, ecommerce, restaurants and small businesses, LATAM, US and Europe, plus a domain that does not exist), checked against the raw HTML of each site. Last run: 2026-11-04, version 1.1.0.

MetricResult
Successful runs100% (65/65)
Complete results (all required fields)100%
Key fields correct (426 of 428 checked by hand)99.5%
Companies with at least one email / phone86% / 58%
Time per company (median / p90 / max)1.8 s / 3.1 s / 5.9 s

Every version is measured on the same set before it is published. What changed and when: CHANGELOG.md.

Use with AI agents / MCP

This Actor charges only per result (no usage fees), so AI agents can run and pay for it through the Apify MCP server without an Apify account.

Example request to your assistant:

"For these 20 domains, tell me which companies are hiring, which ATS they use, their tech stack and a generic contact email."

"Find a contact email and phone for bakery-sonnenschein.de, acme.com.ar and joespizzanyc.com and give me a table."

"Which of these 50 accounts should my SDRs call first this week? Use Pro mode and sort by buying intent."

For agents: input domains (list of domains or URLs), optional sections to read fewer pages, proMode: true for scores. One output item per company; read primaryEmail, primaryPhone, socialProfiles, hiring.isHiring, technologies[].name, and in Pro mode pro.buyingIntentScore and pro.whyNow. Companies that could not be read are not returned and not charged.

FAQ

This Actor reads only public pages of the company's own website, without login, respects robots.txt and never bypasses bot challenges. Company contact data (info@, sales@, the main phone) is generally low risk. Emails that identify a person (jane@company.com) are personal data under GDPR, so they are omitted by default; if you turn on includePersonalEmails, you are responsible for having a legal basis (e.g. legitimate interest) and for informing those people where required (GDPR art. 14). Nothing is stored outside your own Apify storage.

What happens if a website fails or comes back incomplete?

It is not charged. Down, parked, blocked (403, Cloudflare challenge) and empty sites are listed with the reason in FAILED_DOMAINS, and every run stores a RUN_STATS record (successes, failures by reason) in its key-value store. One bad domain never fails the whole run.

How many pages does it read per website?

At most maxPagesPerDomain (default 10): the home page, then the pages most likely to have contact data — contact, legal notice/Impressum, privacy policy, about, team and terms, in English, Spanish, Portuguese, German, French and Italian — plus careers (or the ATS public job API), blog or feed and the sitemap, found through same-site links and common paths. Requests to the same site go a few at a time. robots.txt does not count. That is why runs are fast and the price is fixed.

Why is a site "blocked"?

Some sites (often large brands behind Cloudflare) show a bot challenge or refuse automated requests. We report them as blocked and do not try to get around it. In our tests about 14% of large-brand sites and very few small-business sites do this.

Does it work for JavaScript-heavy sites?

It reads the HTML the server sends (fast and cheap, no browser). Most company sites include contacts, links, structured data and scripts in that HTML; content that only appears after JavaScript runs may be missed.

Will the output format change?

No. The output schema is stable and versioned (schemaVersion: "1.1.0", outputVersion: 1): fields are only added, never renamed or removed, and every change is in the CHANGELOG.md.

Why do I see fewer emails than another scraper?

We return one deduplicated row per company and drop what is not a real company inbox: placeholders (your@email.com), image names (logo@2x.png), package versions, regulators and agencies found in legal texts. Personal emails (jane@company.com) are omitted by default for GDPR and counted in personalEmailsOmitted; turn on includePersonalEmails to get them.

How do I know what a run will cost?

The log prints the maximum cost before the run starts (number of companies × price, plus Pro mode if on), and Maximum cost per run stops it exactly there. Failed, blocked and empty sites are free.

Is this an official tool of any of the platforms it detects?

No. Not affiliated with or endorsed by LinkedIn, X, Facebook, Instagram, Greenhouse, Lever, Ashby, Shopify, HubSpot or any other brand named on this page; their names are used only to describe the data found on public company websites.

I found a bug / need a feature

Open an issue in the Issues tab. We usually answer issues within 24 hours.