# Bad Website Finder — Local Businesses With Broken Sites (`trustworthy_lugworm/bad-website-finder`) Actor

Find local businesses whose website is dead, parked, not mobile-friendly, or missing HTTPS. Every site is actually fetched, then ranked worst-first with name, phone and address.

- **URL**: https://apify.com/trustworthy\_lugworm/bad-website-finder.md
- **Developed by:** [Fox Parker](https://apify.com/trustworthy_lugworm) (community)
- **Categories:** Lead generation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 prospect founds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Bad Website Finder — local businesses with broken, dated, or invisible sites

Give it a city. Get back a ranked list of local businesses whose website is
actually broken — with the owner's phone number and a one-line description of
what is wrong that you can read out on a cold call.

Every site in the output was fetched and checked. Nothing is inferred from a
directory listing, and nothing is guessed.

### Who this is for

**Web designers, agencies, and freelancers who sell websites.** The hard part of
that business is not building sites, it is finding someone who needs one and
knows it. This gives you a list of people whose site is demonstrably failing.

It works because *demonstrable* beats *persuasive*. "You need a new website" is
an opinion the owner can argue with. "Your site shows a Not Secure warning on
your customers' phones, here, look" is a fact they can check while you are
standing there.

Also used by: local SEO consultants, hosting resellers, and anyone doing
territory research before a sales push.

### What it checks

Each site is fetched and scored. Higher score = worse site = better prospect.

| Finding | Score | Why it matters |
|---|---|---|
| Dead domain (DNS does not resolve) | 40 | Nothing loads at all |
| Domain parked / listed for sale | 40 | They lost the registration |
| No HTTPS at all | +8 | Every visitor sees a "Not Secure" warning |
| Not mobile-friendly (no viewport tag) | +5 | Phones render the desktop layout, zoomed out |
| Uses Flash | +5 | Dead since 2020 |
| Table-based layout | +4 | Pre-2010 markup |
| Site error (404/500/TLS failure) | 6 | Broken, worth a look |
| No HTTPS by default | +3 | Missing redirect — minor but sloppy |
| Almost no content (<2.5 KB) | +3 | A placeholder, not a website |
| No structured data | +2 | No hours or call button in Google's panel |
| No tap-to-call link | +2 | Phone number is not clickable |
| Slow (>4s) | +2 | Visitors leave |
| No page title | +2 | Shows the raw URL in search results |
| DIY builder page (Wix/Weebly/GoDaddy) | +1 | Often an abandoned trial |

Healthy sites are excluded by default — a business with a good website is not a
prospect. Flip `includeHealthySites` on if you want a full census instead.

### Sample output

Real rows from a run over Burbank, Inglewood, and Tulare, California:

| score | name | type | phone | problems |
|---|---|---|---|---|
| 40 | Soo Raa Thai-American | restaurant | +1-818-505-8216 | dead domain — search the name, they may have moved |
| 40 | Jack's Family Kitchen | restaurant | +1 323-296-5215 | domain PARKED/FOR SALE — search the name |
| 12 | Media City Auto Repair | car repair | +1-818-842-5757 | NO HTTPS AT ALL (browsers show 'Not Secure'); table-based layout (pre-2010) |
| 12 | Los Amigos Bar & Grill | restaurant | +1 818-842-3700 | not mobile-friendly; no HTTPS by default (missing redirect); no structured data for Google; no tap-to-call link |
| 9 | The Magnolia Grille | restaurant | +1 818-766-8698 | not mobile-friendly; no structured data for Google; no tap-to-call link |

One full record:

```json
{
  "score": 12,
  "verdict": "problems",
  "name": "Media City Auto Repair",
  "category": "car repair",
  "phone": "+1-818-842-5757",
  "website": "http://mediacityauto.com",
  "problems": [
    "NO HTTPS AT ALL (browsers show 'Not Secure')",
    "table-based layout (pre-2010)"
  ],
  "problems_text": "NO HTTPS AT ALL (browsers show 'Not Secure'); table-based layout (pre-2010)",
  "street_address": "329 North Victory Boulevard",
  "city": "Burbank",
  "searched_place": "Burbank, California",
  "latitude": 34.1766098,
  "longitude": -118.3184194,
  "http_status": 200,
  "final_url": "http://mediacityauto.com",
  "load_seconds": 0.31,
  "page_bytes": 100336,
  "fetch_error": "",
  "osm_id": 4504065891
}
```

Export as JSON, CSV, or Excel, or pull it from the API.

### Input

| Field | What it does |
|---|---|
| **Cities or neighbourhoods** | One per line. Be specific — `Burbank, California`, not `Burbank`. Sweep a whole metro in one run. |
| **Business categories** | Food, beauty, trades, health, retail, insurance, professional. Leave empty for all. |
| **Search radius** | Kilometres from the centre of each place. 4 km ≈ a neighbourhood, 15 km ≈ a small city. |
| **Max sites per place** | Caps the cost of a run. |
| **Minimum problem score** | `0` = anything with a flaw, `5` = serious only, `40` = dead and parked domains only. |
| **Include healthy sites** | Off by default. |
| **Include sites that blocked the check** | Off by default — see below. |

### Two things worth knowing before you call anyone

**A dead domain does not mean the business has no website.** It means *that
domain* is gone. In testing, three of four dead domains belonged to businesses
that had simply rebuilt somewhere else and left a stale listing behind. Search
the business name before you pitch — it costs you one search and saves you an
embarrassing phone call. The same applies to parked domains.

**Some sites block automated checks.** They return a 403 to anything that is not
a human, and spot-checking showed they usually open fine in a real browser.
Reporting those as broken would send you to pitch someone whose website is
perfectly good, so they are excluded by default and labelled `inconclusive` if
you turn them on. Turning on Apify Proxy reduces how often this happens.

### Where the data comes from

Business listings come from **OpenStreetMap** — free, open, and worldwide, with
no API key and no per-record fee. That is why this Actor is cheap to run.

The trade-off is honest: OSM coverage varies. Dense urban areas are well mapped;
small towns are thin. A business is only included if a contributor recorded a
website for it, so the count you get back is not every business in town. Chains
are filtered out where OpenStreetMap tags them as such, but a franchise that
nobody tagged will occasionally slip through.

Where the platform allows it, results for each area are cached for 30 days
within your own account, so re-running the same area skips the lookup entirely —
faster for you, and easier on a service that runs on donations. If caching is
unavailable the run simply queries fresh; nothing fails.

### Coverage

Anywhere OpenStreetMap has data, which is everywhere. Coverage is strongest in
North America and Europe.

# Actor input Schema

## `places` (type: `array`):

One per line. Be specific — 'Burbank, California' not 'Burbank'. Each place is searched separately, so you can sweep a whole metro in one run.

## `categories` (type: `array`):

Leave empty to search all of them. Trades and professional offices tend to have the worst websites and the most money to fix them.

## `radiusKm` (type: `integer`):

How far from the centre of each place to search. 4 km covers a neighbourhood; 15 km covers a small city.

## `maxSitesPerPlace` (type: `integer`):

Caps the cost of a run. Businesses without a website tag in OpenStreetMap are never fetched and never counted.

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

Only return sites this bad or worse. 0 returns everything with any problem, 5 returns only serious ones, 40 returns only dead and parked domains.

## `includeHealthySites` (type: `boolean`):

Off by default — a business with a good website is not a prospect. Turn on if you want a full census of the area rather than a lead list.

## `includeInconclusive` (type: `boolean`):

Some sites sit behind a bot filter and return 403. Spot-checks showed these usually open fine in a real browser, so they are excluded by default rather than reported as broken.

## `concurrency` (type: `integer`):

Higher is faster and cheaper. Lower it if you are seeing timeouts.

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

Optional. Used only for fetching business websites — never for OpenStreetMap, which is told our real identity. A residential proxy reduces how many sites block the check.

## Actor input object example

```json
{
  "places": [
    "Burbank, California",
    "Glendale, California"
  ],
  "categories": [],
  "radiusKm": 4,
  "maxSitesPerPlace": 40,
  "minScore": 0,
  "includeHealthySites": false,
  "includeInconclusive": false,
  "concurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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 = {
    "places": [
        "North Hollywood, Los Angeles"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("trustworthy_lugworm/bad-website-finder").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 = { "places": ["North Hollywood, Los Angeles"] }

# Run the Actor and wait for it to finish
run = client.actor("trustworthy_lugworm/bad-website-finder").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 '{
  "places": [
    "North Hollywood, Los Angeles"
  ]
}' |
apify call trustworthy_lugworm/bad-website-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,trustworthy_lugworm/bad-website-finder"
        }
    }
}

```

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/q7S8gJZAwofWd6Meb/builds/9HfSlWDDNgL5hn6te/openapi.json
