Website Contact & Company Data Scraper with AI Lead Scoring
Pricing
from $3.00 / 1,000 domain crawleds
Website Contact & Company Data Scraper with AI Lead Scoring
Crawl company domains for emails, phones, and socials, then add an AI company profile and ICP fit score. Flat price per domain, not per page.
Pricing
from $3.00 / 1,000 domain crawleds
Rating
0.0
(0)
Developer
Crafted in Code
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
20 days ago
Last modified
Categories
Share
Paste a list of company domains, or pass a dataset from a Google Maps scraper, and get a company profile for each site: emails, phone numbers, social profiles, a few tech hints, and an optional AI lead score against your ideal customer profile.
You pay a flat price per domain, not per page. The crawl is limited to the homepage plus contact, about, team, careers, and legal pages (8 pages max). Dead, parked, and blocked domains are returned with a status and are not charged.
Built for agencies, SDRs, and Clay / n8n / Make workflows that already have a lead list and need qualification without a surprise crawl bill.
What data can you extract?
| Field | What it is |
|---|---|
domain, finalUrl, companyName, title | The site that was crawled, after redirects, and the name from the page title or structured data |
status | ok, dead, parked, blocked, or error |
emails | Role inboxes such as info@ and sales@ first. Named personal emails are omitted unless you opt in |
phones | Numbers from tel: links, structured data, and formatted numbers on the page |
socials | LinkedIn, Facebook, Instagram, X, YouTube, TikTok |
techHints | Signatures such as Shopify, WordPress, HubSpot, GA4, Stripe, Intercom |
contactPageUrl, addressHint, countryHint, hasCareersPage | Contact page, a short address when the site publishes one, careers link |
pagesCrawled, pageUrls | How many pages were actually fetched |
aiSummary, industry, subIndustry, businessModel | Short description and category. Industry is one of a fixed list |
offerings, targetCustomers, pricingModelHint | What they sell, who they sell to, and a pricing hint (demo, quote, ecommerce, …) |
companySizeSignal, companySizeEvidence | solo, SMB, mid, enterprise, or unknown, plus the phrase that suggested it |
icpFitScore, icpFitReasons, personalizationLine | 0–100 against your ICP text, the reasons, and one outreach opener |
aiConfidence, aiMock, aiError | How sure the model was, whether the offline mock was used, and why AI was skipped |
error, chargedEvents, source | Failure message, which events were billed, and original columns from a source dataset |
icpFitScore is null when you do not provide an ICP. The model is instructed to use only the crawled text. It can still be wrong. Use aiConfidence and companySizeEvidence before you trust a score.
How to use it
- Open the Actor and paste domains (
acme.comorhttps://acme.com/pricing) or pick a dataset and the column that holds the website (websitefor Google Maps). - Set Max domains. This is a hard stop.
- Optionally set Max charge (USD). The run also respects the maximum charge you set on the Apify run itself. The lower limit wins.
- Leave AI company profile on, and describe your ideal customer in plain English. Example:
B2B SaaS, 20–500 employees, sells to HR teams in the US and UK. - Run. Each
okdomain is onedomain-crawledevent. Each AI profile is oneai-profileevent. The status message explains when a cap stops the run.
The same domain is crawled once, even if you paste both www.acme.com and https://acme.com/about. Deep links are fetched in addition to the homepage, still inside the page cap.
Free plan: at most 25 AI profiles per run and 100 per UTC month. The crawl can continue without AI after that, until the domain or charge cap. The row's aiError says which cap applied.
How much does it cost to scrape company websites?
Pricing is pay-per-event. You are not billed per page, and you are not billed for a domain that did not resolve, looks parked, or blocked the crawler.
| Event | Free plan | Paid plans (Bronze and up) | What you pay for |
|---|---|---|---|
domain-crawled | $0.004 | $0.003 | One domain with a usable page |
ai-profile | $0.030 | $0.010 | One AI company profile |
| Start event | platform default | platform default | A small platform charge when the run starts |
Paid-plan examples:
| Run | Price |
|---|---|
| 100 domains, contacts only | $0.30 |
| 100 domains with AI profiles | $1.30 |
| 1,000 domains, contacts only | $3.00 |
| 1,000 domains with AI profiles | $13.00 |
| 1 domain, the prefilled test | $0.003 + $0.01 if AI runs |
Free-plan example: 25 domains with AI is 25 × $0.004 + 25 × $0.03 = $0.85, and further AI in that UTC month is skipped after 100 profiles.
Set a max charge on the run if you want a hard dollar stop. maxDomains stops the crawl even when money remains. Rows already in flight in the current small batch are finished; new domains are not started.
Flat price vs per-page contact scrapers
| This Actor | Typical per-page contact scraper | |
|---|---|---|
| Unit | One domain, up to 8 pages | Every page fetched |
| A 5-page site | $0.003 on paid plans | Often $0.002 × 5 pages, plus browser or proxy add-ons |
| A site that explodes into hundreds of URLs | Stopped by the page cap | Can become a large bill and a huge dataset |
| Dead or parked domain | Output row, $0 | Often still billed per request |
| Lead qualification | Optional AI profile and ICP score | Usually not included |
| Google Maps dataset | Pass the dataset id, keep original columns | Usually a separate copy-paste step |
The $13 / 1,000 fully enriched domains figure is the paid-plan list price above. It is not a per-page estimate.
Input example
{"domains": ["acme.com", "https://example.co.uk/about"],"datasetId": "yourGoogleMapsDatasetId","websiteField": "website","maxDomains": 100,"maxPagesPerDomain": 5,"maxChargeUsd": 5,"aiEnrichment": true,"icpDescription": "B2B SaaS, 20-500 employees, sells to HR teams in the US and UK","aiLanguage": "en","maxAiDomains": 100,"includePersonalEmails": false}
The saved default input is a single fast site (https://crawlee.dev) so the daily Store check finishes in under five minutes.
Output example
One dataset item per domain. A full local sample, produced with the offline mock model against live sites, is in examples/sample-output.json. Real runs set aiMock to false and aiProvider to gemini or openai.
{"domain": "acme.com","finalUrl": "https://acme.com/","status": "ok","companyName": "Acme Payroll","emails": ["sales@acme.com"],"personalEmailsOmitted": 1,"phones": ["+1-415-555-0134"],"socials": { "linkedin": "https://www.linkedin.com/company/acme", "x": null, "facebook": null, "instagram": null, "youtube": null, "tiktok": null },"techHints": ["HubSpot", "Google Analytics 4"],"hasCareersPage": true,"pagesCrawled": 3,"aiSummary": "Acme sells payroll software to HR teams.","industry": "Software","businessModel": "B2B","companySizeSignal": "SMB","icpFitScore": 78,"icpFitReasons": ["Sells to HR teams", "B2B software"],"personalizationLine": "Noticed Acme is focused on HR payroll.","aiConfidence": 0.7,"aiMock": false,"chargedEvents": ["domain-crawled", "ai-profile"],"error": null,"aiError": null}
A blocked site looks like { "status": "blocked", "error": "The site blocked the crawler or returned a bot challenge.", "chargedEvents": [] } and does not add an AI charge.
Run totals, including an estimated LLM cost versus AI-event revenue, are stored as the key-value record RUN_STATS.
FAQ
Is scraping public company websites legal? This Actor fetches pages a browser could open and extracts business contact details the site publishes. It does not log in, bypass paywalls, or solve CAPTCHAs. Laws differ by country. You are responsible for having a lawful basis, especially under GDPR, and for following CAN-SPAM, CASL, and similar rules if you email anyone. This is not legal advice.
Why are some emails missing?
By default only role-based addresses are returned (info@, sales@, hello@, and similar). A named inbox such as jane.doe@ is personal data and is counted in personalEmailsOmitted instead. Turn on Include personal emails only when you have a reason to process that data. Addresses that are clearly placeholders, or that belong to vendors such as Sentry, are dropped.
Why is there no phone number?
Numbers are taken from tel: links, structured data, and formatted phone patterns. A number that is only drawn as an image, or only rendered by JavaScript, will not be found. This Actor uses HTTP and Cheerio, not a browser, so a site that ships an empty HTML shell may come back thin or blocked.
What does the ICP score mean?
It is a 0–100 judgment of the crawled text against the ICP description you typed. It is not a credit score and it is not headcount. If the pages are thin, aiConfidence is low. If you leave the ICP blank, the score is null.
What happens at the free-plan limit?
The run does not crash. Further AI calls are skipped, aiError explains the cap, and contact rows that were already allowed are still saved. Raising maxAiDomains does not override the free-plan cap.
Can I connect it to Make, n8n, Zapier, or an AI agent?
Yes. Run it from the Apify API, a saved task, or an integration. Map the dataset id of a Google Maps actor into datasetId. The output dataset is a normal Apify dataset, so Make, n8n, Zapier, and the Apify MCP can read the items. The Actor uses limited permissions and pay-per-event pricing, which is what agent checkout flows expect.
Does it charge when the model fails?
No. ai-profile is charged only after the response parses as a profile. A timeout or invalid JSON sets aiError and does not bill that event. The crawl event is also skipped unless status is ok.
Integrations
- Apify API: start a run with the input JSON above and read the default dataset.
- Google Maps scrapers: pass their dataset id. The website column is picked up automatically.
- Make, n8n, Zapier: use the Apify integration and map
emails,icpFitScore, andpersonalizationLine. - Clay: call the run-sync endpoint and spread the dataset items into rows.
- MCP / agents: limited permissions, no Standby mode, pay-per-event only.
Limits and honest gaps
- JavaScript-only sites may return little text. There is no Playwright fallback, on purpose: a browser would make the flat domain price unpredictable.
- The crawler is polite (robots.txt, a short delay, low concurrency) and will record
blockedinstead of hammering a site. - Industry and company size are model judgments.
unknownis a valid size signal. - Email and phone coverage will miss obfuscated addresses (
info [at] acme.comwritten as an image, or assembled by JavaScript).
Local development
npm installnpm testnpm run e2e
npm run e2e crawls a few public sites with the mock model and refreshes examples/sample-output.json. Set GEMINI_API_KEY (and optionally OPENAI_API_KEY) to use a real model. See PUBLISHING.md for Store setup, secret env vars, and the exact pay-per-event prices.