# Google Maps Website Audit and Agency Pitch (`seasons-ai/local-business-site-audit`) Actor

Find local businesses that need a better website: audits each Google Maps place's site, scores the opportunity 0-100 and writes a ready agency pitch.

- **URL**: https://apify.com/seasons-ai/local-business-site-audit.md
- **Developed by:** [Seasons AI](https://apify.com/seasons-ai) (community)
- **Categories:** AI, Lead generation, Marketing
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 audited websites

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

## Google Maps Website Audit and Agency Pitch

Find the local businesses that need a better website. Pick your **Google Maps Scraper** dataset and get a ranked list: every business website checked, a 0-100 opportunity score, 3 concrete reasons and a ready 3-sentence pitch per business.

Built for web design, SEO and local marketing agencies doing outreach.

### What you can do

- **Qualify leads in bulk**: audit up to 2,000 places per run and sort them by opportunity.
- **Spot businesses with no real website**: no site at all, only a Facebook/Instagram page, only a booking-platform profile or a directory listing.
- **Get the facts for the call**: HTTPS, mobile-friendly, page weight, title and meta tags, online booking, contact page, outdated copyright year, broken links.
- **Send outreach faster**: a short pitch per business based only on what was found, in the language you choose.
- **Work in your tools**: CSV for Excel / Google Sheets, Markdown report, or the dataset through the API, n8n, Make or an AI agent.

### How to use

1. Run **Google Maps Scraper** (`compass/crawler-google-places`) for a category and area, for example "hair salon in Boise".
2. Open this Actor and pick that run's dataset in **Google Maps Scraper dataset**. No dataset? Paste businesses as JSON or give a CSV/JSON link.
3. Optionally describe what your agency offers, so the pitch fits your service.
4. Start the run. Results are in the **Output** tab, sorted by opportunity, plus `report.csv` and `report.md`.

### What it checks

For every business in the dataset:

| Situation | What you get |
|---|---|
| No website on Google Maps | flagged `none` |
| The "website" is a Facebook/Instagram page, a booking-platform profile (Booksy, Vagaro, Fresha…) or a directory listing | flagged `social-only` / `booking-platform-only` / `directory-only` |
| Domain does not resolve, server down, homepage returns an error | flagged `unreachable`, with the reason |
| Site blocks automated checks (robots.txt, HTTP 403/429) | flagged `blocked`, no claims about its quality |
| Own website loads | `audited`, with the checks below |

Checks on an own website:

- **HTTPS**: final URL is HTTPS, and whether the SSL certificate is valid
- **Mobile**: `width=device-width` viewport tag
- **Speed and weight**: time to download the homepage HTML, HTML size, number of scripts, stylesheets and images
- **Search basics**: title tag (missing / too short / too long), meta description, H1 count
- **Online booking or ordering**: about 50 booking, ordering and shop widgets (Calendly, Square, Vagaro, Booksy, Fresha, Mindbody, OpenTable, Resy, Toast, DoorDash, Shopify…) plus "Book now" / "Order online" buttons
- **Contact page** or contact form
- **Outdated copyright year** in the footer
- **Broken main links**: up to 6 navigation links (404, 410, 5xx, dead host)
- **Website builder** (WordPress, Wix, Squarespace…) when visible

Then a language model writes the opportunity score, 3 reasons and the pitch from those facts only. The pitch is addressed to the business (by its name or "your team"), never to a person.

### Input

| Field | What to enter |
|---|---|
| **Google Maps Scraper dataset** | Pick the dataset of your `compass/crawler-google-places` run (or paste its ID). The Actor gets read-only access to that one dataset. |
| Businesses (JSON) | Instead of a dataset: an array with `title`/`name`, `categoryName`/`category`, `website`, `totalScore`/`rating`, `reviewsCount`/`reviews`. |
| File URL | Instead of a dataset: a public JSON or CSV link with the same columns. |
| Max businesses | Default 50, up to 2000 per run. |
| Skip closed places | Default on. Skipped places are free. |
| What your agency offers | Optional one line to tailor the pitch. |
| Pitch language | Default English. |

API example:

```json
{ "datasetId": "YOUR_SCRAPER_RUN_DATASET_ID", "maxBusinesses": 200, "agencyOffer": "websites and online booking for salons" }
```

### Output

One dataset row per business, plus two files in the run's key-value store, both sorted by opportunity:

- `report.csv`: one line per business with score, reasons, pitch and every check (opens in Excel or Google Sheets)
- `report.md`: a summary table and all pitches, ready to read or paste into a doc

```json
{
  "name": "Example Salon",
  "category": "Hair salon",
  "city": "Boise",
  "website": "http://www.example-salon.com/",
  "websiteStatus": "audited",
  "opportunityScore": 35,
  "reasons": [
    "The footer copyright says 2021, so the site looks unmaintained.",
    "130 reviews at 4.8 stars: a busy salon whose site undersells it.",
    "Booking works, so the main gap is freshness, not function."
  ],
  "pitch": "Your website's footer still shows 2021, which can make Example Salon look less current to new clients. A quick refresh would match the site to the reputation your 130 reviews show. We'd be glad to share a few ideas if that's useful.",
  "checks": {
    "https": true, "mobileViewport": true, "loadMs": 237, "htmlKb": 513.1,
    "title": "HOME | Example Salon | Boise, Idaho", "metaDescription": "Hair salon and makeup studio…",
    "onlineBooking": true, "bookingSignals": ["button: \"BOOK NOW\""],
    "contactPage": true, "copyrightYear": 2021, "brokenLinks": [], "platform": "Wix.com Website Builder"
  },
  "rating": 4.8,
  "reviewsCount": 130,
  "eventCharged": "site-audited"
}
```

### Pricing

Pay per business. Businesses that fail, closed places and duplicates are free.

| Event | When | Price |
|---|---|---|
| `site-audited` | an own website was loaded, checked and scored | **$0.006** |
| `place-flagged` | no own website, a social/booking/directory page only, site down, or site not checkable; scored and pitched | **$0.002** |

Worked examples:

| Run | Price |
|---|---|
| 1 business with a website | $0.006 |
| 50 businesses, 35 with a website, 15 without | 35 × $0.006 + 15 × $0.002 = **$0.24** |
| 1,000 businesses, all with a website (worst case) | $6.00 |

Before it starts, the Actor checks that the run's maximum cost covers every business at its price (each listed website counted as a full audit) and stops with a clear message if not. You are never charged more than that maximum.

Available on paid Apify plans.

### Privacy and politeness

- **Business level only.** Phone numbers, emails, owner names, reviewer data and social profiles from the scraper dataset are dropped on read and never appear in the output. Emails and phone numbers on web pages are masked before scoring.
- Each site gets `robots.txt` first and is skipped if it disallows us. Then the homepage, at most 2 internal pages (contact and booking) and at most 4 more main links with lightweight HEAD requests, one at a time, with at least 0.3 s between requests (or the site's crawl-delay, up to 5 s).
- User-Agent: `LocalBusinessSiteAudit/0.1 (+https://apify.com/seasons-ai)`. Timeouts of 10-20 s per request and 75 s per site.

### Limits

- Load time is the time to download the homepage HTML from an Apify data centre, not a full browser render. Page weight is the HTML document size, not counting images and scripts.
- Pages are read as served, without running JavaScript. On sites that build their content in the browser, booking widgets or copyright lines can be missed; such sites are marked `jsRendered: true` and the pitch avoids claims about missing content.
- A copyright year inserted by JavaScript is not visible, so `copyrightYear` is then empty, not "outdated".
- The score is grounded in the checks: a rule-based baseline is computed first and the model's score is kept within 10 points of it. Reasons and pitches are written by a model. Read them before sending.
- Up to 2000 businesses per run. Businesses that share one homepage (chains) are checked once.

### Good to know

- On the free Apify plan the Actor returns a short preview with an example result and prices instead of running.
- Read every pitch before sending it: reasons and pitches are written by a language model from the checks.
- Results and files stay in the run's storage for your plan's retention period. Download `report.csv` if you need to keep it.
- Questions or a missing check? Open an issue on the Actor page.

# Actor input Schema

## `datasetId` (type: `string`):

Pick the dataset of your Google Maps Scraper (compass/crawler-google-places) run, or paste its dataset ID. The Actor gets read-only access to this one dataset. Leave empty to use Businesses (JSON) or File URL below instead.

## `items` (type: `array`):

Used when no dataset is picked. An array of objects with a business name (title or name), category (categoryName or category), website, rating (totalScore or rating) and reviews count (reviewsCount or reviews). Other fields are ignored.

## `fileUrl` (type: `string`):

Used when neither a dataset nor businesses are given: a public link to a JSON array or a CSV export with the same columns (for example a dataset export link).

## `maxBusinesses` (type: `integer`):

Audit at most this many businesses (first rows of the source, after removing duplicates and closed places). Hard limit 2000 per run.

## `skipClosed` (type: `boolean`):

Skip places marked permanently or temporarily closed on Google Maps. Skipped places are not charged.

## `agencyOffer` (type: `string`):

Optional. One line about your services, used to tailor the pitch, e.g. "websites, local SEO and online booking setup for salons". Max 300 characters.

## `pitchLanguage` (type: `string`):

Language for reasons and pitch.

## Actor input object example

```json
{
  "items": [
    {
      "title": "Example Bakery",
      "categoryName": "Bakery",
      "city": "Springfield",
      "website": "https://example.com/",
      "totalScore": 4.6,
      "reviewsCount": 128
    },
    {
      "title": "Corner Barber Shop",
      "categoryName": "Barber shop",
      "city": "Springfield",
      "website": null,
      "totalScore": 4.8,
      "reviewsCount": 57
    }
  ],
  "maxBusinesses": 50,
  "skipClosed": true,
  "pitchLanguage": "English"
}
```

# Actor output Schema

## `opportunities` (type: `string`):

No description

## `reportCsv` (type: `string`):

No description

## `reportMarkdown` (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 = {
    "items": [
        {
            "title": "Example Bakery",
            "categoryName": "Bakery",
            "city": "Springfield",
            "website": "https://example.com/",
            "totalScore": 4.6,
            "reviewsCount": 128
        },
        {
            "title": "Corner Barber Shop",
            "categoryName": "Barber shop",
            "city": "Springfield",
            "website": null,
            "totalScore": 4.8,
            "reviewsCount": 57
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("seasons-ai/local-business-site-audit").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 = { "items": [
        {
            "title": "Example Bakery",
            "categoryName": "Bakery",
            "city": "Springfield",
            "website": "https://example.com/",
            "totalScore": 4.6,
            "reviewsCount": 128,
        },
        {
            "title": "Corner Barber Shop",
            "categoryName": "Barber shop",
            "city": "Springfield",
            "website": None,
            "totalScore": 4.8,
            "reviewsCount": 57,
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("seasons-ai/local-business-site-audit").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 '{
  "items": [
    {
      "title": "Example Bakery",
      "categoryName": "Bakery",
      "city": "Springfield",
      "website": "https://example.com/",
      "totalScore": 4.6,
      "reviewsCount": 128
    },
    {
      "title": "Corner Barber Shop",
      "categoryName": "Barber shop",
      "city": "Springfield",
      "website": null,
      "totalScore": 4.8,
      "reviewsCount": 57
    }
  ]
}' |
apify call seasons-ai/local-business-site-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,seasons-ai/local-business-site-audit"
        }
    }
}
```

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/bshk7EU8ABG61a7tL/builds/1p6rf47gt4y2awMd0/openapi.json
