# Web Design Leads: Local Businesses With No or Weak Websites (`transparent_meteorite/website-redesign-leads`) Actor

Find local businesses on Google Maps with no website or a weak one (no HTTPS, not mobile-friendly, outdated, broken, parked, Facebook-only). Each lead gets a 0-100 opportunity score with plain-English reasons, phone and address. No login. $4 per 1,000 leads.

- **URL**: https://apify.com/transparent_meteorite/website-redesign-leads.md
- **Developed by:** [Detroit Web Agent - Open Data](https://apify.com/transparent_meteorite) (community)
- **Categories:** Lead generation, SEO tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Web Design Leads: Local Businesses With No or Weak Websites

![Web Design Leads: features, price and a real output row](https://api.apify.com/v2/key-value-stores/9BAn0msnxToXlD9TO/records/sf-website-redesign-leads-banner-10140227.png)

[![Run it free on Apify](https://api.apify.com/v2/key-value-stores/9BAn0msnxToXlD9TO/records/sf-cta.png)](https://console.apify.com/actors/bo1Fxt39hUhREwHhQ/input)

| Price per 1,000 leads | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| **This Actor** | **$4.00** | **$3.50** | **$3.00** | **$2.50** |
| Most-used alternative | $9.90 | $7.90 | $5.90 | $4.90 |

<sub>Pay per event, no monthly rental. Prices read from each Actor's public Apify pricing on 2026-10-15; check the Pricing tab for the current figure.</sub>

![Web Design Leads sample output table from a real run](https://api.apify.com/v2/key-value-stores/9BAn0msnxToXlD9TO/records/sf-website-redesign-leads-output-10140227.png)

Find local businesses on Google Maps that need a new website, and know why before you call. Search any business type in any city; each business is checked and gets an **opportunity score from 0 to 100** with plain-English reasons, plus its phone, address, rating and Google Maps link. Only the leads are returned and charged.

### What does Web Design Leads do?

For every Google Maps result it answers one question: does this business need a website or a redesign?

- **No website** on the Google Maps listing (score 90-95)
- **Social-only**: the "website" is a Facebook, Instagram, Linktree or Yelp page (score 80-85)
- **Parked or lapsed**: domain for sale, "coming soon", or a web vendor's "this website is no longer available" page (score 85-90)
- **Broken**: domain does not resolve, server down, homepage returns an error (score 75-80)
- **Weak website**: the homepage is visited once and checked for
  - HTTPS missing, not enforced, or an expired / self-signed certificate
  - no mobile viewport (not mobile-friendly)
  - copyright year 2+ years old
  - slow response (3 s and 6 s thresholds)
  - no contact form, booking link or email
  - missing page title or meta description, very thin homepage
  - Flash, default template text, DIY builder (Wix, Squarespace, GoDaddy, Weebly, Duda and more)

Businesses with 10+ Google reviews get a small boost and an "Active business" reason, because an established business with a bad website is the best lead.

### Who is it for?

- Web design and SEO agencies building a call list
- Freelancers looking for local clients in their city
- Cold-email and lead-gen teams that want a website-quality filter on Maps data
- Anyone selling to small businesses that need a better online presence

### Input

| Field | What it does | Default |
|---|---|---|
| `searchTerms` | Business types, e.g. `plumber`, `dentist`, `hair salon` | `["plumber"]` |
| `locations` | Cities, ZIP codes or areas | `["Detroit, MI"]` |
| `startUrls` | Google Maps search URLs to check exactly those results | none |
| `maxPlacesPerSearch` | Businesses checked per search (1-120) | 20 |
| `onlyLeads` / `minScore` | Return only businesses scoring at least this much | on / 40 |
| `includeNoWebsite` | Include businesses with no website at all | on |
| `checkWebsites` | Visit each homepage (off = only no-website and social-only leads) | on |
| `maxItems` | Stop after this many leads in total (0 = no limit) | 0 |
| `maxTotalChargeUsd` | Stop before this spend | none |
| `onlyNew` | Monitor mode: skip businesses already delivered by earlier runs | off |
| `proxyConfiguration` | Proxy for Google Maps requests | Apify Proxy |

Running with an empty input `{}` checks 20 plumbers in Detroit, MI.

#### Example inputs

Roofers in three Ohio cities, hot leads only:

```json
{ "searchTerms": ["roofing contractor"], "locations": ["Toledo, OH", "Akron, OH", "Dayton, OH"], "maxPlacesPerSearch": 60, "minScore": 70 }
```

Dentists and chiropractors with a website that needs work (no "no website" rows):

```json
{ "searchTerms": ["dentist", "chiropractor"], "locations": ["Austin, TX"], "includeNoWebsite": false, "maxPlacesPerSearch": 100 }
```

Weekly monitor of new leads for one city (schedule it; only new businesses are charged):

```json
{ "searchTerms": ["landscaper", "pressure washing", "house cleaning"], "locations": ["Tampa, FL"], "onlyNew": true, "monitorKey": "tampa-home-services" }
```

### Output sample

One row per lead (real row from a Detroit plumber search, trimmed):

```json
{
  "name": "Flawless Plumbing & Flooring LLC",
  "opportunityScore": 90,
  "leadTier": "hot",
  "reasons": [
    "Website domain is parked or not built (website is no longer available)",
    "Active business: 106 Google reviews at 4.8 stars"
  ],
  "websiteStatus": "parked",
  "category": "Plumber",
  "phone": "(313) 753-6813",
  "website": "http://www.flawlessplumbingandflooring.com/",
  "rating": 4.8,
  "reviewsCount": 106,
  "claimedByOwner": true,
  "mapsUrl": "https://www.google.com/maps/search/?api=1&query=Flawless%20Plumbing%20%26%20Flooring%20LLC&query_place_id=ChIJpz14dFtAVgwRfy66SvuD5zQ",
  "placeId": "ChIJpz14dFtAVgwRfy66SvuD5zQ",
  "lat": 42.3611,
  "lng": -83.1444059,
  "https": true,
  "sslValid": false,
  "sslError": "CERT_HAS_EXPIRED",
  "mobileViewport": false,
  "builder": "WordPress",
  "hasContactForm": false,
  "responseMs": 354
}
```

`websiteStatus` is one of `none`, `social_only`, `parked`, `unreachable`, `ok` (site checked) or `not_checked` (the site blocks automated visits or its robots.txt asks us not to fetch it; these score 0 and are not returned as leads by default). Other fields: `address`, `city`, `state`, `postalCode`, `categories`, `domain`, `finalUrl`, `httpStatus`, `httpsEnforced`, `copyrightYear`, `yearsStale`, `builderVersion`, `hasBookingLink`, `hasTitleAndMetaDescription`, `pageTitle`, `metaDescription`, `pageBytes`, `emails`, `websitePhones`, `socials`, `searchString`, `rank`, `checkedAt`.

The dataset has two views: **Leads** (score, reasons, website checks) and **Call list** (name, phone, emails, address).

### Pricing

Pay per lead. Businesses that are checked but not returned (good websites, below your minimum score, closed, duplicates, already seen in monitor mode, sites we could not check) are never charged.

| Apify plan | Price per lead | Per 1,000 leads |
|---|---|---|
| Free | $0.004 | $4.00 |
| Bronze | $0.0035 | $3.50 |
| Silver | $0.003 | $3.00 |
| Gold and above | $0.0025 | $2.50 |

Plus a $0.00005 start fee per run. A typical 20-business search returns 2 to 8 leads, so it costs a few cents.

### Use it from code, Zapier or an AI agent

```bash
curl -X POST "https://api.apify.com/v2/acts/transparent_meteorite~website-redesign-leads/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms":["plumber"],"locations":["Detroit, MI"],"maxPlacesPerSearch":20}'
```

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('transparent_meteorite/website-redesign-leads').call({ searchTerms: ['dentist'], locations: ['Austin, TX'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

AI agents (Claude, Cursor, ChatGPT) can call it through the Apify MCP server: `https://mcp.apify.com/?actors=transparent_meteorite/website-redesign-leads`.

### Honest limits

- One homepage request per business (15 s timeout, 1 MB cap). The score reflects the homepage, not every page of the site.
- Sites that block automated visits (Cloudflare challenges, 403s) or disallow crawlers in robots.txt are marked `not_checked` and are not scored or charged.
- Google Maps shows up to about 120 results per search. For a whole metro area, split it into several locations or ZIP codes.
- Service-area businesses often hide their street address on Google Maps; for them `address` is empty but the phone is present.
- Speed is measured from Apify's servers in the US, a single sample, not a lab test like Lighthouse.
- Emails are only those shown on the homepage. Emails on parked or lapsed pages belong to the registrar or host and are dropped.

### Run in 30 seconds

1. Open the Actor page and click **Try for free** (or **Start** if you are signed in).
2. Paste this example into the JSON tab of the input:

```json
{
  "searchTerms": ["plumber"],
  "locations": ["Detroit, MI"],
  "maxPlacesPerSearch": 20,
  "minScore": 40
}
```

3. Click **Start**. Twenty businesses are looked up on Google Maps and their homepages are checked, which usually takes under two minutes.
4. Open the **Output** tab. Switch between the **Leads** view (score and reasons) and the **Call list** view (name, phone, emails, address), then use **Export** for CSV, Excel or JSON.

### How the opportunity score is read

The score runs from 0 to 100 and each row carries a `leadTier`. As a rule of thumb, 40 and above is a warm lead and 70 and above is a hot lead. A business with no website at all scores 90 to 95. A social-only listing scores 80 to 85, a parked or lapsed domain 85 to 90, and a broken site 75 to 80. A site that loads but has weaknesses scores according to how many of the weaknesses listed above were found.

Two practical consequences follow:

- **The score is a sorting aid, not a verdict.** Always open the `reasons` array before you call. It lists, in plain English, what was found, such as an expired certificate or a missing mobile layout.
- **Reviews matter.** An established business with 10 or more reviews gets a small boost and an "Active business" reason, because a business that already has customers and a poor website is the easiest one to help.

### Who uses it and for what

- **Web design agencies** run the same trade across several cities every week and hand the Call list view to a sales rep. Hot leads with expired certificates or no mobile layout make a concrete opening line: "your site shows a security warning in the browser".
- **Freelancers** pick a trade they enjoy working with, such as roofers or dentists, and build a local pipeline from the no-website and social-only rows.
- **Local SEO consultants** filter for businesses with many reviews and a weak site, since search visibility is the next problem for them.
- **Website builders and hosting resellers** target the DIY-builder rows (Wix, Squarespace, GoDaddy, Weebly, Duda) with an upgrade pitch.
- **Cold-email teams** use the `emails` and `websitePhones` fields as a first filter and add their own verification step before sending.
- **Researchers** run `onlyLeads` off to measure what share of local businesses in a city have no site, an insecure site or an outdated one.

### Input fields explained

- `searchTerms` and `locations` are combined, so 3 terms across 2 locations are 6 searches. If `locations` is empty, the terms are searched as typed.
- `startUrls` accepts Google Maps search URLs when you want exactly the results of a search you refined by hand.
- `maxPlacesPerSearch` accepts 1 to 120, the most Google Maps shows per search. It limits how many businesses are checked, not how many are returned.
- `onlyLeads` (default on) returns only businesses at or above `minScore`. Turn it off to receive every checked business, each returned row being charged.
- `minScore` accepts 0 to 100 and defaults to 40.
- `includeNoWebsite` turns the no-website rows off, which is useful when you only want to pitch a redesign.
- `checkWebsites` visits each homepage once. With it off, only no-website and social-only leads are scored, which is faster.
- `maxItems` stops the run after that many leads. `maxTotalChargeUsd` is an optional spend limit, given as a text value such as `"1"`.
- `onlyNew` and `monitorKey` control monitor mode. The same key keeps the same memory even if you change other inputs.
- `language` and `countryCode` set the Google Maps language and country. `proxyConfiguration` applies to Google Maps requests only; business websites are visited directly so load times are real.

### More worked examples

**Hot leads only, across a metro area split by ZIP code**

```json
{
  "searchTerms": ["roofing contractor", "hvac contractor"],
  "locations": ["78701", "78702", "78704"],
  "maxPlacesPerSearch": 50,
  "minScore": 70
}
```

**Only weak websites, skipping businesses with no site, for a redesign pitch**

```json
{
  "searchTerms": ["law firm"],
  "locations": ["Columbus, OH"],
  "includeNoWebsite": false,
  "minScore": 50,
  "maxPlacesPerSearch": 40
}
```

**Fast scan of no-website and social-only businesses**

```json
{
  "searchTerms": ["food truck", "mobile detailing"],
  "locations": ["Phoenix, AZ"],
  "checkWebsites": false,
  "maxPlacesPerSearch": 60
}
```

### Fields in the output, grouped

| Group | Fields |
|---|---|
| Score | `opportunityScore`, `leadTier`, `reasons`, `websiteStatus` |
| Business | `name`, `category`, `categories`, `phone`, `address`, `city`, `state`, `postalCode`, `rating`, `reviewsCount`, `claimedByOwner` |
| Google Maps | `mapsUrl`, `placeId`, `lat`, `lng`, `searchString`, `rank` |
| Website checks | `website`, `domain`, `finalUrl`, `httpStatus`, `https`, `httpsEnforced`, `sslValid`, `sslError`, `mobileViewport`, `builder`, `builderVersion`, `copyrightYear`, `yearsStale`, `responseMs`, `pageBytes` |
| Page content | `pageTitle`, `metaDescription`, `hasTitleAndMetaDescription`, `hasContactForm`, `hasBookingLink` |
| Contacts | `emails`, `websitePhones`, `socials` |
| Meta | `checkedAt` |

Fields that depend on a homepage visit are absent for businesses with no website.

### Tips to keep runs cheap and useful

1. Start with `maxPlacesPerSearch` of 20 for each new trade and city. If a few leads come back, raise it.
2. Keep `onlyLeads` on. You pay for leads only, and checked-but-fine businesses cost nothing.
3. Use `minScore` of 70 for a short call list and 40 for a wider pipeline.
4. Use `maxItems` or Max total charge on scheduled runs.
5. Use `onlyNew` with a `monitorKey` per city so each weekly run delivers only businesses you have not seen.
6. Check the `reasons` of your first ten rows before scaling up, to confirm the lead type matches what you sell.

### Call it from Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("transparent_meteorite/website-redesign-leads").call(run_input={
    "searchTerms": ["dentist"],
    "locations": ["Austin, TX"],
    "minScore": 60,
    "maxPlacesPerSearch": 40,
})
for lead in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(lead["opportunityScore"], lead["name"], lead.get("phone"), lead["reasons"][:2])
```

### More questions

**What counts as a parked website?** A domain for sale, a "coming soon" page, or a web vendor's "this website is no longer available" page. Such rows have `websiteStatus` set to `parked`.

**What does `not_checked` mean?** The site blocks automated visits, or its robots.txt asks crawlers not to fetch it. These businesses are not scored and are not returned by default.

**Does the actor email the businesses?** No. It only reads public Google Maps listings and homepages.

**How many leads can I expect?** A typical 20-business search returns a few leads, depending on the trade and city. Trades with many small owner-run shops produce more.

**Which website builders are detected?** Common DIY builders such as Wix, Squarespace, GoDaddy, Weebly and Duda, plus others listed in the README above. WordPress is reported in `builder` when found.

**Can I use it outside the US?** Yes. Set `countryCode`, `language` and a local location. Google Maps coverage decides the rest.

**Why is `address` empty for some plumbers or cleaners?** Service-area businesses often hide the street address on Google Maps. The phone is still present.

**How current are the results?** Each row has a `checkedAt` timestamp. Run again to refresh the website checks.

### FAQ

**Why did a business with a website get a high score?** Read its `reasons`: each point in the score has a reason, for example an expired certificate or no mobile layout.

**Why is a business I know missing?** Either its website looks fine (score below your minimum), it was permanently closed, or Google did not list it in the first `maxPlacesPerSearch` results. Set `onlyLeads` to off to see every checked business with its score.

**Is the website check polite?** Yes. robots.txt is read first and respected, there is one homepage request per business, at most two requests at a time per website, and the crawler identifies itself as `WebDesignLeadsBot`.

**Can I run it every week?** Yes. Turn on `onlyNew` and schedule it; each run returns and charges only businesses not delivered before.

### Legal note

This actor collects publicly available business information from Google Maps listings and public business homepages. It does not log in, solve CAPTCHAs or collect personal data beyond what businesses publish for customer contact. You are responsible for using the data in line with applicable laws (for example CAN-SPAM, GDPR and telemarketing rules) and the terms of the sites involved.

New to Apify? [Create a free account](https://console.apify.com/sign-up?fpr=rnmakg) and run this Actor in under a minute. Some links are affiliate links.

# Actor input Schema

## `searchTerms` (type: `array`):

What to search on Google Maps, one per line, for example plumber, roofing contractor, dentist, hair salon. Each term is searched in every location below.

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

Cities, ZIP codes or areas, one per line, for example Detroit, MI or 78701. Leave empty to search the terms as typed.

## `startUrls` (type: `array`):

Paste Google Maps search URLs (google.com/maps/search/...) to check exactly those results. Used in addition to the searches above.

## `maxPlacesPerSearch` (type: `integer`):

How many Google Maps results to check for each search (1-120). Only the leads among them are returned and charged.

## `onlyLeads` (type: `boolean`):

On: return only businesses scoring at or above the minimum score. Off: return every checked business with its score (each returned row is charged).

## `minScore` (type: `integer`):

0-100. 40+ = warm lead, 70+ = hot lead. No website scores 90-95.

## `includeNoWebsite` (type: `boolean`):

Off: return only businesses that have a website (weak, outdated, broken or social-only).

## `checkWebsites` (type: `boolean`):

Visit each business homepage once (robots.txt respected) to check HTTPS, mobile layout, copyright year, builder, contact form, speed. Off: only no-website and social-only leads are scored.

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

Stop after this many leads across all searches. 0 = no limit.

## `maxTotalChargeUsd` (type: `string`):

Optional. The run stops returning leads before this amount is reached. Leave empty for no extra cap (the run's own max charge still applies).

## `onlyNew` (type: `boolean`):

Remember every business returned for these searches and skip it on later runs, so a schedule delivers (and charges) only new leads.

## `monitorKey` (type: `string`):

Name for the memory used by monitor mode. Same name = same memory across different inputs.

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

Language code for Google Maps results, for example en, es, de.

## `countryCode` (type: `string`):

Two-letter country code for Google Maps, for example us, gb, ca.

## `proxyConfiguration` (type: `object`):

Used for Google Maps requests only. Business websites are visited directly so load times are real.

## Actor input object example

```json
{
  "searchTerms": [
    "plumber"
  ],
  "locations": [
    "Detroit, MI"
  ],
  "startUrls": [],
  "maxPlacesPerSearch": 20,
  "onlyLeads": true,
  "minScore": 40,
  "includeNoWebsite": true,
  "checkWebsites": true,
  "maxItems": 0,
  "onlyNew": false,
  "language": "en",
  "countryCode": "us",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `leads` (type: `string`):

No description

## `callList` (type: `string`):

No description

## `summary` (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 = {
    "searchTerms": [
        "plumber"
    ],
    "locations": [
        "Detroit, MI"
    ],
    "startUrls": [],
    "maxPlacesPerSearch": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("transparent_meteorite/website-redesign-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 = {
    "searchTerms": ["plumber"],
    "locations": ["Detroit, MI"],
    "startUrls": [],
    "maxPlacesPerSearch": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("transparent_meteorite/website-redesign-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 '{
  "searchTerms": [
    "plumber"
  ],
  "locations": [
    "Detroit, MI"
  ],
  "startUrls": [],
  "maxPlacesPerSearch": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call transparent_meteorite/website-redesign-leads --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,transparent_meteorite/website-redesign-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/bo1Fxt39hUhREwHhQ/builds/O1Fsvt1DHuGR3OsxV/openapi.json
