# Local Business Website Audit - Verifiable Defects, Not Scores (`champ1918/local-business-website-auditor`) Actor

Website audit for lead generation. Finds local businesses with broken tap-to-call links, dead contact emails, stale copyright years and placeholder text. Every finding is verifiable in the page source - no A-F scores. Sites we can't assess are marked inconclusive, never guessed.

- **URL**: https://apify.com/champ1918/local-business-website-auditor.md
- **Developed by:** [Mark Capodilupo](https://apify.com/champ1918) (community)
- **Categories:** Lead generation, Automation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $100.00 / 1,000 results

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

## Local Business Website Auditor

Scan any list of local business websites and get back a list of **specific, provable defects** — dead tap-to-call links, email links that go nowhere, contact addresses pointing at the wrong domain, stale copyright years, missing titles, insecure resources, and more.

Built for one job: **finding local businesses whose websites are visibly broken, so you can sell them a fix.**

Every finding is something the business owner can see in their own page source in about ten seconds. No scores, no grades, no black box.

***

### What this Actor does

Give it a list of URLs. It reads each page's HTML and returns a structured list of defects with a plain-English explanation of why each one costs the business money.

It runs **23 checks**, weighted heavily toward the ones that break the path between a customer and a sale:

**Contact-path defects (high severity)**

| Check | What it catches |
|---|---|
| `broken_tel_link` | `tel:` links with the wrong digit count — the encoded-space bug, `tel:` tags with no number at all |
| `broken_mailto` | Email links that display one address and send to another, or point at a placeholder like `temp@dentist.com` |
| `foreign_domain_email` | Contact email on a domain that isn't the business's — inbound enquiries routed to a different company |
| `inconsistent_phone_numbers` | Several different phone numbers wired into call links on one page |
| `phone_not_tappable_ios` | `format-detection: telephone=no` set with no call links to compensate |
| `phone_not_clickable` | Phone number present as plain text with no `tel:` link anywhere |
| `site_unreachable` | The site does not load at all |
| `parked_or_placeholder_domain` | Domain parked, for sale, or still on a default server page |

**Credibility and maintenance defects**

`dead_google_plus_link` · `stale_copyright` · `placeholder_text` · `flash_content` · `default_wp_tagline` · `dead_facebook_link` · `personal_email_as_business` · `outdated_jquery` · `no_https` · `insecure_resources`

**Search-visibility defects**

`missing_title` · `title_too_long` · `missing_meta_description` · `missing_h1` · `no_structured_data` · `no_open_graph` · `images_missing_alt` · `no_analytics` · `placeholder_schema`

***

### What this Actor deliberately does NOT do

**It will not guess.**

Sites rendered entirely in JavaScript — Wix, Squarespace, many React builds — return an empty HTML shell to any non-browser request. A naive scanner reports those as missing everything and hands you a page of false positives.

This Actor detects that case and returns `inconclusive: true` with a plain explanation, rather than counting the page as defective.

That matters more than it sounds. If you are using this output to open a conversation with a business owner, **one false claim ends the conversation**. Better to hand back 59 real findings than 66 with 7 lies in them.

It also does not extract personal data, does not follow links, and does not touch anything behind a login.

***

### Why use it

**For web design and SEO agencies.** Every cold-outreach playbook written for agencies says the same thing: the only opener that works is a specific, verifiable flaw on the prospect's site. Generic "I noticed your site could rank better" gets deleted. *"All 14 of your call buttons are dead, here is the exact character causing it"* gets a reply.

This Actor manufactures that opener at scale.

**Real numbers from a production run:** 85 local service businesses across 7 US metros — auto repair, plumbing, HVAC, dental, roofing, salons.

- **59** had at least one verifiable defect
- **6** had tap-to-call links that were completely non-functional
- One had **14 dead call buttons on a single page**
- Cost to run that same scan yourself: **$8.50**

**For freelancers and consultants** pitching website work, **for agencies auditing a client portfolio** after acquiring an account, and **for anyone doing competitive research** on how well-maintained a market's websites actually are.

***

### How much does it cost?

Pricing is **pay per result: $100.00 per 1,000 results.** One result equals one URL audited. Platform usage — compute, proxies, bandwidth — is included, so there is nothing else to pay.

The Apify Free plan includes **$5 in credits every month**, which is **50 websites audited per month, free.**

For scale: auditing an entire metro's worth of local businesses — 500 sites — costs $50.00.

***

### How to audit a list of business websites

1. Click **Try for free** at the top of this page.
2. Open the **Input** tab and paste your list of website URLs. Use **Bulk edit** to paste hundreds at once, one per line.
3. Click **Start**. A 50-site run finishes in well under a minute.
4. Open the **Output** tab when the run completes.
5. Export as **CSV, JSON, Excel, or HTML**, or pull the results from the Apify API.

Sort the output by `highSeverity` descending. Those rows are your call list.

***

### Input

Local Business Website Auditor takes a single input field — `startUrls`, a list of website URLs. Click the **Input** tab for the full schema.

```json
{
  "startUrls": [
    { "url": "https://example-plumbing.com/" },
    { "url": "https://example-hvac.com/" },
    { "url": "https://example-dental.com/" }
  ]
}
```

### Output

You can download the dataset in **JSON, CSV, Excel, or HTML**. One row per URL.

```json
{
  "url": "https://example-plumbing.com/",
  "scannedAt": "2026-08-02T14:02:38.000Z",
  "findingsCount": 3,
  "highSeverity": 2,
  "findings": [
    {
      "check": "broken_tel_link",
      "severity": "high",
      "detail": "tel: link \"tel:%20615-298-2995\" has 11 digits after an encoded space - phones reject it, so the tap does nothing"
    },
    {
      "check": "broken_mailto",
      "severity": "high",
      "detail": "Email link shows \"temp@example.com\" on screen but actually sends to \"info@exampl-plumbing.com\" - the two do not match"
    },
    {
      "check": "stale_copyright",
      "severity": "medium",
      "detail": "Footer copyright says 2019 - site looks unmaintained (7 years old)"
    }
  ],
  "summary": "tel: link has 11 digits after an encoded space | Email link display and target do not match | Footer copyright says 2019"
}
```

Pages that cannot be honestly audited return:

```json
{
  "url": "https://example-js-rendered-site.com/",
  "findingsCount": 0,
  "inconclusive": true,
  "summary": "Inconclusive - page content is rendered by JavaScript, so the raw HTML cannot be audited. Not counted as defective."
}
```

***

### Is it legal to audit business websites?

Yes. This Actor reads only what a browser reads when it loads a public page — the same HTML any visitor receives. It does not log in, does not bypass any access control, does not extract personal data such as private email addresses or user records, and does not collect anything the business has not chosen to publish.

It reads business contact details that are published on the business's own website for the purpose of being contacted.

If you use the output for outreach, normal marketing rules still apply in your jurisdiction — CAN-SPAM in the US, GDPR in the EU. You should not contact people who have opted out, and if you are unsure whether your use case is legitimate, ask a lawyer.

***

### Tips

- **Feed it a clean list.** Include the protocol (`https://`). The Actor follows redirects but does not guess at malformed URLs.
- **Sort by `highSeverity`, not `findingsCount`.** A site with one dead call button is a much better lead than a site with six missing alt tags.
- **Treat `inconclusive` rows as unknown, not clean.** They may well be broken; the raw HTML just cannot prove it. Open those by hand if the lead matters.
- **Re-verify before you send.** Before quoting a finding to a business owner, open the page and confirm it. Sites change. This is the single habit that keeps the approach credible.
- **Pair it with a contact extractor** to attach an email address to every defect you find, and you have a complete outreach list.

***

### Related

**[Website Contact & Email Extractor](https://apify.com/champ1918/website-contact-email-extractor)** — feed it the same URL list and get back emails, phone numbers, addresses, and social profiles, one clean row per company. The two together turn a list of domains into a list of qualified, reachable leads.

# Actor input Schema

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

URLs to start with.

## `maxRequestsPerCrawl` (type: `integer`):

Maximum number of requests that can be made by this crawler.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://crawlee.dev"
    }
  ],
  "maxRequestsPerCrawl": 100
}
```

# 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 = {
    "startUrls": [
        {
            "url": "https://crawlee.dev"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("champ1918/local-business-website-auditor").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 = { "startUrls": [{ "url": "https://crawlee.dev" }] }

# Run the Actor and wait for it to finish
run = client.actor("champ1918/local-business-website-auditor").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 '{
  "startUrls": [
    {
      "url": "https://crawlee.dev"
    }
  ]
}' |
apify call champ1918/local-business-website-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,champ1918/local-business-website-auditor"
        }
    }
}

```

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/rSwR9hSSmYeK7Tnpb/builds/9DVdAhUIGGLHyw37h/openapi.json
