# Businesses Without Websites: Local Leads for Web Designers (`datagleaner/businesses-without-websites-leads`) Actor

Find local businesses with no website: pick a trade and a city, get name, phone, address, rating and reviews for places that list no website on Google Maps, cross-checked on Bing, with a flag for businesses that only have a Facebook or Instagram page. Pay only for qualified leads: $0.004 each.

- **URL**: https://apify.com/datagleaner/businesses-without-websites-leads.md
- **Developed by:** [Data Gleaner](https://apify.com/datagleaner) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 leads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Businesses Without Websites: local leads for web designers and agencies

Find local businesses that have no website. Give it a trade and a city (`plumber` in `Austin, TX`) and it returns the businesses that Google Maps lists **with no website**, each with **name, phone, address, category, rating, review count and coordinates**, so you can call or visit the ones that need a site. Businesses whose only web presence is a **Facebook, Instagram or link-in-bio page** are included and flagged (`hasOnlySocialPage`), because they are usually the easiest to sell to. One row per business, deduplicated. **$0.004 per qualified lead; everything we reject is free.**

**To try it:** click Start with the prefilled input (10 plumbers in Austin with no website), or with an empty input to run a built-in example (up to 5 leads).

### What it does

1. **Searches Google Maps** for every trade in every location you give, 20 places per page, until it has `maxLeadsPerSearch` qualified leads or Google has no more (about 150 to 210 places for a big city and a common trade).
2. **Keeps a place only when it has no own website.** Places that list a real website are dropped and not billed. A listed Facebook, Instagram, LinkedIn, TikTok, Linktree or similar link, or a directory page (Yelp, Yellow Pages, Google Business sites), does not count as a website; the business is kept and the link is returned in `listedLink`.
3. **Cross-checks the web** (on by default). For each candidate it guesses likely domains from the name and keeps a guess only when its home page shows the same phone number, and drops the business when such a site is found that Google Maps does not show. No search engine is queried. Each guessed domain's robots.txt is read first (a disallowed page is not fetched, and a Crawl-delay is honoured) and certificates are verified. Dropped businesses are not billed. If the run runs out of time before a check, the lead is kept and marked `websiteCheck: unverified`.
4. **Dedupes** by place and by phone number (or name and address), within a run, so overlapping searches never bill the same business twice.

### Use cases

- Web designers and freelancers building a prospect list for "I noticed you don't have a website" outreach.
- Agencies selling websites, local SEO or Google Business Profile set-up to small local businesses.
- Website-builder and hosting resellers looking for new customers in one trade or one city.
- AI agents that need "businesses of type X in city Y with no website, with a phone" in one call.

### Input

```json
{
  "trades": ["plumber", "hair salon"],
  "locations": ["Austin, TX", "Leeds, UK"],
  "maxLeadsPerSearch": 50,
  "requirePhone": true,
  "includeSocialOnly": true,
  "minReviews": 5,
  "verifyWithBing": true
}
```

- `trades` x `locations`: every trade is searched in every location. `searches` adds full free-text Google Maps searches.
- `maxLeadsPerSearch` (default 50): qualified leads per search. `maxItems`: hard cap for the run. One search returns up to about 200 places, so split big cities into districts.
- `requirePhone` (default on): keep only businesses with a phone number. `includeSocialOnly` (default on). `minRating`, `minReviews`: keep established businesses only.
- `findDomains` and `enrichContacts` (both default off), `domainMinConfidence` (default 0.7): chain the leads into Company Domain Finder and Website Contact Details Scraper. See "Optional chain" below; it adds little for this list.
- `verifyWithBing` (default on). `language`, `country` (regional bias, e.g. `gb`, `au`), `delaySecs`. No proxy is used: a rate limit from Google stops the search instead of being routed around.

### Output

```json
{
  "placeId": "ChIJ...",
  "name": "Example Plumbing",
  "category": "Plumber",
  "address": "123 Example St, Austin, TX 78701",
  "city": "Austin",
  "state": "TX",
  "country": "US",
  "phone": "(512) 555-0100",
  "phoneE164": "+15125550100",
  "rating": 4.6,
  "reviewCount": 38,
  "hasOnlySocialPage": true,
  "socialPlatform": "facebook",
  "listedLink": "https://www.facebook.com/exampleplumbing",
  "listedLinkType": "social",
  "websiteCheck": "domain-no-match",
  "mapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJ...",
  "input": "plumber in Austin, TX",
  "scrapedAt": "2026-11-01T12:00:00+00:00"
}
```

(Illustrative values: names, numbers and coordinates in your dataset come straight from Google Maps.)

`websiteCheck`: `domain-no-match` (the domain check found no own site), `unverified` (the run ran out of time before the check) or `off`. A per-search summary is saved in the key-value store as `SEARCH_SUMMARY`.

### How much does it cost?

Pay per event, no start fee: **one `lead` event of $0.004 per qualified business returned.** Places with a website, places without a phone (when required), duplicates and businesses dropped by the domain check are free.

Worked example: 50 plumbers in Austin with no website cost 50 x $0.004 = **$0.20**, however many places had to be scanned to find them. A first run of 10 leads costs **$0.04**. Platform usage is small and is included in the run.

### Limits

- **Up to about 200 places per search**, so the number of leads depends on the trade and city. Split big areas.
- **A site Google does not list can still exist.** The domain check removes some of these; others remain. Check a business's name online before you pitch it.
- **Unofficial source.** Places come from the same public JSON that Google Maps loads; Google can throttle it. The Actor paces requests, backs off, retries the same request a few times and stops with a clear status if Google keeps refusing. It never solves captchas.
- **No emails by default.** Phone and address only; the optional chain above adds an email for about 0 to 4% of leads. These businesses rarely have an email to find (2 to 4% in our test), so plan on phone, visit or social-page outreach.

### FAQ

**Why would a business have a Facebook page but "no website"?** Many small businesses run on a Facebook or Instagram page. They have a web presence but no site of their own, which is the pitch for a web designer.

**Does it include chains?** Chains almost always list a website, so they drop out.

**Can I run it for another country?** Yes. Set `country` (for example `gb`) and write the location in the local form (`Leeds, UK`).

### Workflow: phone and Maps-profile outreach

Every row has a phone number, a Google Maps link and a rating, and that is the outreach path: call, visit, or message the business on the Facebook or Instagram page returned in `listedLink`. Businesses with no website almost never publish an email, so there is no reliable email step. We tested it: of 54 leads from three example lists (Austin plumbers, Leeds hair salons, Toronto restaurants), running the names through [Company Domain Finder](https://apify.com/datagleaner/company-domain-finder) with contacts enabled returned a website for 18 (33%), but only about 8 of those were the right business (about 15% of the list; most of these are small sites Google Maps does not list, the rest were wrong matches such as booking platforms, directories and same-named businesses elsewhere), and an email was found for only about 1 to 2 of 54 (2 to 4%). Treat a found domain as a lead to check by hand, not as an email source.

### Optional chain: possible website and contacts

`findDomains` runs each lead through Data Gleaner's [Company Domain Finder](https://apify.com/datagleaner/company-domain-finder), and `enrichContacts` also reads the websites it finds with the [Website Contact Details Scraper](https://apify.com/datagleaner/website-contact-details-scraper). Both run as sub-runs on your own Apify account and bill their own events there ($0.0008 per website found, $0.002 per website with an email); this Actor charges only the $0.004 lead. Found data is added to the lead row (`foundWebsite`, `foundWebsiteConfidence`, `bestEmail`, `emailList`, `phoneList`, `contactForms`, `contactSocials`, `contactsNote`); leads are pushed in batches after each search, so rows appear later in the run.

Be clear what this does and does not do. These leads were chosen because Maps lists no website and a guessed-domain check found none, so the finder rarely has anything left to find: on a 50-lead run (hair salons and plumbers in Leeds, UK) it found a website for 2 leads, one of them a correct small site (a barber) and one a wrong match (a hair salon matched to a musical's site), and one email came back, from the wrong match. Earlier we measured the same on 54 leads from three lists: about a third got a domain, under half of those right, and 2 to 4% an email. Treat a found website as a hint that the lead may already have a site, which changes your pitch from a first website to a redesign, and check it by hand. There is no reliable email path for this list; the phone number is still the way to reach these businesses. These counts were measured with an earlier version of the finder that also read search pages; the current finder uses Wikidata, national registers and the domains a name spells, and found 39% of 59 local and long-tail businesses in our own test (96% of 50 well-known companies), so expect fewer small local businesses to resolve.

### Responsible use

Only public business listings are returned. You are responsible for how you contact businesses, including local rules on cold calling, do-not-call lists and marketing messages.

**Screen before you call.** Phone numbers of sole traders and small businesses can be covered by do-not-call registers, and the duty to screen is yours as the caller. Check every number before any call: in the UK against the TPS (and CTPS for companies), in Australia against the DNCR, in the US against the National DNC Registry and state lists, and use the equivalent register in any other country. Do not call numbers that are registered, and honour any request to stop.

**Purpose limit.** Use the data only for your own lawful B2B or legitimate-interest purposes in the recipient's country. Do not resell bulk lists, and do not run cold outreach where the law or an objection forbids it. You are the controller of what you do with the data.

**Email privacy.** With `enrichContacts`, personal-looking email addresses (free-mail or first.last@ style) are not returned; role and business addresses are. A row with withheld addresses carries a `privacyNotice`.

### Removal requests

If you run a business listed in the results, or are the person behind one, and do not want this Actor to return your email, domain or name, email **datagleaner@gmail.com** (Keencrest, the publisher; replies come from an AI agent, Crest, and the business is owner-operated) with the email, domain or business name. We add it to a blocklist (`src/optout_dids.txt`, loaded at the start of every run) within 30 days, usually much sooner, answer you, and keep a dated record of the request and our answer. Personal-looking addresses are never returned, and a site's advertising objection is honoured.

# Actor input Schema

## `trades` (type: `array`):

What kind of business, as you would type it into Google Maps: `plumber`, `hair salon`, `restaurant`, `electrician`. Every trade is searched in every location. Leave trades and locations empty to run a small built-in example (up to 5 leads).

## `locations` (type: `array`):

City, area or postcode to search in, with the state or country when the name is common: `Austin, TX`, `Leeds, UK`. One search returns up to about 200 places, so split a big city into districts for more leads.

## `searches` (type: `array`):

Full Google Maps searches added to the trade x location list, e.g. `nail salon near 78701`. Optional.

## `maxLeadsPerSearch` (type: `integer`):

Stop each search after this many qualified leads. Every lead returned is one billed `lead` event ($0.004). Prefill 10 keeps a first run small (about $0.04); the default of 50 applies when omitted.

## `maxItems` (type: `integer`):

Hard cap on billed leads for the whole run, across all searches.

## `requirePhone` (type: `boolean`):

Keep only places that show a phone number, so every lead can be called. Turn off to also get places with an address only.

## `includeSocialOnly` (type: `boolean`):

Google Maps sometimes lists a Facebook, Instagram or link-in-bio page in the website slot. Those businesses have no real website and are often the warmest leads; they come back with `hasOnlySocialPage: true`. Turn off to get only businesses with nothing listed.

## `verifyWithBing` (type: `boolean`):

Guess likely domains from each candidate's name and city and drop the business when a guessed domain's home page shows the same phone number (not billed). Cuts false leads; adds a few seconds per candidate. No search engine is queried. The input key keeps its old name for compatibility.

## `findDomains` (type: `boolean`):

Off by default. Each lead is looked up by name, city, phone and postal code with Data Gleaner's Company Domain Finder, run on your own Apify account, and the result is added as `foundWebsite` with its confidence. Be aware that a lead with a found website probably has one, so this mostly tells you which leads to treat as redesign rather than first-site prospects. The finder bills its own $0.0008 per website found (not this Actor). In a test about a third of leads got a domain and fewer than half of those were right.

## `enrichContacts` (type: `boolean`):

Off by default; implies the website lookup. Websites found for your leads are then read for emails, phones, contact forms and socials with Data Gleaner's Website Contact Details Scraper, also on your own account, which bills its own $0.002 per website where an email is found. Leads with no website found get no contacts. Expect emails for only a few percent of leads: a business with no website rarely publishes one.

## `domainMinConfidence` (type: `number`):

Only with the website lookup. A found website is kept (and billed by the finder) only at or above this confidence. 0.8 is high precision; lower values return more guesses.

## `minRating` (type: `number`):

Keep only places rated at least this many stars (0 = no filter). Established, well-reviewed businesses are the ones who pay for a site.

## `minReviews` (type: `integer`):

Keep only places with at least this many Google reviews (0 = no filter).

## `language` (type: `string`):

Two-letter language code for Google Maps (`en`, `de`, `fr`, `es`...). Affects category names and address format.

## `country` (type: `string`):

Two-letter country code Google uses to rank results (`us`, `gb`, `de`, `au`, `ca`...). Put the city in `locations`; this only sets the regional bias.

## `delaySecs` (type: `number`):

Pause between two requests to Google Maps.

## Actor input object example

```json
{
  "trades": [
    "plumber"
  ],
  "locations": [
    "Austin, TX"
  ],
  "maxLeadsPerSearch": 10,
  "maxItems": 1000,
  "requirePhone": true,
  "includeSocialOnly": true,
  "verifyWithBing": true,
  "findDomains": false,
  "enrichContacts": false,
  "domainMinConfidence": 0.7,
  "minRating": 0,
  "minReviews": 0,
  "language": "en",
  "country": "us",
  "delaySecs": 0.5
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "trades": [
        "plumber"
    ],
    "locations": [
        "Austin, TX"
    ],
    "maxLeadsPerSearch": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("datagleaner/businesses-without-websites-leads").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "trades": ["plumber"],
    "locations": ["Austin, TX"],
    "maxLeadsPerSearch": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("datagleaner/businesses-without-websites-leads").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "trades": [
    "plumber"
  ],
  "locations": [
    "Austin, TX"
  ],
  "maxLeadsPerSearch": 10
}' |
apify call datagleaner/businesses-without-websites-leads --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datagleaner/businesses-without-websites-leads"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/EBJyFdtSvaN5Muh4z/builds/qVKq9pSnIOq1690qA/openapi.json
