No-Website Leads (Verified) avatar

No-Website Leads (Verified)

Pricing

from $1.00 / 1,000 result delivereds

Go to Apify Store
No-Website Leads (Verified)

No-Website Leads (Verified)

Local business leads with no live website, a social-only presence, a dead/parked domain, or a DIY-builder site -- every candidate is actually fetched and checked, not just filtered on an empty Google Maps field.

Pricing

from $1.00 / 1,000 result delivereds

Rating

0.0

(0)

Developer

Adam Aly

Adam Aly

Maintained by Community

Actor stats

0

Bookmarked

5

Total users

2

Monthly active users

5 days ago

Last modified

Share

Finds local businesses with no live website, a social-only presence, a dead/parked domain, or a DIY-builder site -- ready-to-pitch leads for web design agencies, SEO consultants, and local lead-gen teams.

What makes this different from other "no website" scrapers

Every competing Actor we researched on Apify Store determines "no website" purely by checking whether the Google Maps listing's website field is empty. That produces a lot of false positives (a business can have a perfectly good site that just isn't linked on Maps) and misses the very common case of a business whose only real web presence is a Facebook page.

This Actor instead:

  1. Actually fetches and checks every candidate website (src/classify.js) -- classifying it as healthy, outdated/DIY-builder, a dead link, a parked/for-sale domain, a directory profile, or genuinely absent -- instead of trusting an empty Maps field.
  2. Defaults to "healthy" on any ambiguous case. A false "you have no website" pitch to a business that has a working site is the fastest way to burn a buyer's trust.
  3. Cross-sources Facebook (src/facebook.js) for businesses whose Maps listing has no website field but who do have a linked Facebook Page -- a lead type every competitor we found misses entirely.
  4. Attempts email recovery for site-less leads (src/emailRecovery.js), the #1 complaint about this whole category of tool ("there are no websites to scrape for emails").
  5. Deduplicates across runs, not just within one run (src/dedupe.js), so a customer running the same search next month doesn't get billed for leads they already have.
  6. Bills pay-per-event, not pay-per-result -- a small charge for every business checked, a larger charge only for confirmed qualified leads. See "Pricing" below.

Project structure

.actor/
actor.json Actor manifest
input_schema.json Defines the input form shown in Apify Console
src/
main.js Entry point / orchestration
classify.js Live website verification (the core differentiator)
facebook.js Facebook cross-sourcing (best-effort)
emailRecovery.js Email recovery for site-less leads
scoring.js 0-100 lead scoring
dedupe.js Cross-run deduplication via a named key-value store
test/
run-unit-tests.js Unit tests for the network-independent logic
Dockerfile
package.json

How it works, end to end

  1. You give it a category (e.g. "plumber") and one or more locations (e.g. ["Austin, TX"]).
  2. It calls a base Google Maps scraper Actor (default: compass/crawler-google-places, a well-established public Actor) to get the raw list of businesses. You need your own Apify credits to cover that call -- this Actor doesn't reimplement Maps scraping itself, it adds a verification and enrichment layer on top of an existing scraper, which is both faster to build correctly and easier to keep working if Google changes Maps' page structure.
  3. For each business, it classifies the website status, scores the lead, attempts Facebook cross-sourcing and email recovery, and skips anything already delivered to this same customer in a prior run.
  4. Qualified leads are written to the Actor's dataset with a lead score and a plain-English reason for the classification.

Setting this up for real (steps you need to do -- I can't do these from here)

  1. Create a free Apify account at apify.com if you don't have one (no card required, $5/month in free platform credit).
  2. Install the Apify CLI locally: npm install -g apify-cli
  3. From this project folder, run:
    apify login
    apify push
    This uploads and builds the Actor under your account.
  4. Open it in Apify Console and go to the Publication tab to fill in the Store listing (title, description, category tags) -- see "Launch checklist" below.
  5. Set up monetization (Publication tab -> Monetization -> Pay per event):
    • Add an event named exactly business-checked -- suggested price $0.001
    • Add an event named exactly qualified-lead-delivered -- suggested price $0.012-$0.015
    • These event names must match the strings used in src/main.js (Actor.charge({ eventName: ... })) exactly, or billing won't fire.
    • Note: significant pricing changes take a 14-day notice period on Apify before they apply, so set your initial pricing carefully.
  6. Enable a free trial (a handful of free qualified leads) in the Monetization settings -- with zero reviews at launch, letting people see real output before paying is your main trust signal.
  7. Submit for publish. Apify reviews new Actors before they appear in Store search.

Known issue found and fixed during real testing (read this)

The first real test run against Google Maps data for "plumber" in Austin, TX flagged 4 leads out of 20 checked. Manual verification found 3 of those 4 actually have real, working websites (aaa-auger.com, rooterman.com, radiantplumbing.com) -- only 1 (a business whose only linked presence was a Facebook page) was a genuinely correct lead. That's a 75% false-positive rate on that batch, traced to this cause:

Small business websites are increasingly protected by bot-detection services (Cloudflare and similar) that block automated requests from cloud servers like Apify's, even though the site works completely normally for a real visitor in a browser. The original code treated any failed automated fetch as UNREACHABLE (a qualifying lead), which is wrong -- a bot-block and a truly dead site look similar to a simple fetch, but they are not the same thing.

The fix, already applied in this version:

  • Added a new COULD_NOT_VERIFY status, used when a fetch fails with a status code (403/429/503) or page content commonly associated with bot detection/challenge pages, rather than a real dead-site signal.
  • COULD_NOT_VERIFY is hard-blocked from ever counting as a qualified lead in scoring.js itself (not just via the input schema), so it can't be miscounted even by a direct API call that bypasses the Console UI.
  • Each classification attempt now retries once with a different simulated browser before giving up, since a single failed request against a real live site is common and often transient.
  • UNREACHABLE was removed from the default qualifying statuses -- it's still available as an opt-in, but given what this test found, treat it as a weaker, higher-false-positive signal than the others until you've validated it further.

This does not mean the false-positive problem is now fully solved -- it means the most identifiable cause found in one real test batch was addressed. Re-run the same manual verification process (see below) against the new build before trusting it with real customers, and expect to keep tightening this over time as new failure patterns turn up.

Testing before you rely on it

I validated everything I could without live access to Google Maps or arbitrary business websites (this sandbox's network is restricted to a fixed allowlist). What was verified:

  • All unit tests pass (npm test) -- covers every classification branch that doesn't require reaching an arbitrary external site (empty website, Facebook/Instagram/Yelp short-circuit detection), plus all of the scoring logic.
  • The live HTTP fetch pipeline itself (request, redirect handling, timeout/error handling, HTML parsing) was verified end-to-end against real reachable URLs and correctly produces HEALTHY for a working page and UNREACHABLE for a non-existent domain.

What was not and could not be verified from here:

  • Real classification accuracy against actual Google Maps data and real business websites -- you should run a small test batch (10-20 businesses in your own city) right after deploying and manually check a sample of the classifications before opening it up to customers.
  • The Facebook cross-sourcing module's real-world hit rate. Facebook aggressively rate-limits and blocks non-browser traffic; this module is deliberately best-effort and fails closed. Expect a real miss rate, and expect to revisit it as Facebook's page structure changes.
  • The compass/crawler-google-places base actor's current exact output field names. I've defensively mapped a couple of common variants in main.js, but confirm the actual field names in a real run and adjust the mapping if needed.
  • Parked-domain and DIY-builder detection patterns will need periodic maintenance as hosting providers and parking-page templates change over time -- this is a known, expected maintenance task, not a one-time build.

Pricing rationale

Existing competitors charge $1.50-$5.00 per 1,000 raw results, regardless of how many are actually qualified -- and since typically only 1-5% of businesses lack a website, buyers end up effectively paying far more than the sticker price per usable lead. Charging per verified qualified lead instead means the price you show is the price they actually pay per usable result, which is both more transparent and (per the research) a real, provable gap versus every incumbent found.

  • Scraping public, logged-out Google Maps listing data has supportive precedent for the "not a computer-crime" question (hiQ v. LinkedIn; Meta Platforms v. Bright Data), but it still violates Google's own Maps Platform Terms of Service. The practical risk is IP blocking/rate-limiting, not criminal liability -- but don't represent this Actor as fully ToS-compliant with Google.
  • The real legal exposure sits downstream, with the buyer's outreach: CAN-SPAM and TCPA penalties are real and significant. Consider adding a short, visible disclaimer in your Store listing about buyer responsibility for compliant outreach.
  • This Actor does not send any messages itself -- it only produces a data list. Keep it that way for v1; sending messages is a materially different compliance situation.

Explicitly out of scope for this version

  • No cold-email or SMS sending
  • No CRM integration
  • No AI-generated outreach message drafting
  • No guaranteed Facebook detection or email discovery -- both are best-effort by design, documented above

Extending this later

  • Swap the search-guessing approach in facebook.js for a more thorough Facebook Page lookup once you've validated demand for it.
  • Add a vertical-specific mode (e.g. contractor license lookups) as a separate higher-priced pay-per-event tier rather than folding it into the base price.
  • Add a CRM/Zapier/Make output integration once you have paying customers asking for it.