# Google Business Profile Auditor — GMB Audit Tool (`acebuilds/gbp-auditor`) Actor

Audit any Google Business Profile (GMB) in seconds. Get a 0–90 score, letter grade, severity-ranked issues, prioritized fixes, and a nearby-competitor comparison. Built for local SEO agencies, freelancers, and small business owners.

- **URL**: https://apify.com/acebuilds/gbp-auditor.md
- **Developed by:** [Ace](https://apify.com/acebuilds) (community)
- **Categories:** AI, Lead generation, Open source
- **Stats:** 95 total users, 22 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.175 / actor start

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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?

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

## Google Business Profile Auditor

Audit any Google Business Profile in seconds. Input a business name and city — get back a
scored report with severity-ranked issues, specific recommendations, and a competitor
comparison, all in structured JSON.

Built for marketing agencies, freelancers, and anyone who needs to audit local business
listings at scale without doing it manually.

***

### What It Does

1. Searches Google Maps for the target business
2. Extracts profile data using Apify's maintained `compass/crawler-google-places` actor
3. Scores 8 auto-detectable profile factors (max 90 points)
4. Flags manual-check items that cannot be auto-detected
5. Compares the business against nearby competitors
6. Returns a complete audit JSON to your dataset

***

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `businessName` | string | Yes | Business name (exact or approximate) |
| `city` | string | Yes | City where the business is located |
| `state` | string | No | US state abbreviation — narrows results |
| `maxCompetitors` | integer | No | Competitors to include (1–5, default: 3) |

**Example input:**

```json
{
  "businessName": "Joe's Pizza",
  "city": "Austin",
  "state": "TX",
  "maxCompetitors": 3
}
```

***

### Output

All results are saved to the default dataset. One item per run.

#### Top-level fields

| Field | Type | Description |
|-------|------|-------------|
| `businessName` | string | Matched business name from Google Maps |
| `searchedAs` | string | The name you passed as input |
| `city` | string | City from the listing |
| `state` | string | State from the listing |
| `auditDate` | string | ISO timestamp of the audit |
| `mapsUrl` | string | Direct Google Maps URL for the business |
| `placeId` | string | Google Place ID |
| `score` | number | Auto-detected score (0–90) |
| `maxAutoScore` | number | Always 90 — max from auto-detectable fields |
| `grade` | string | A / B / C / D |
| `summary` | string | One-line human-readable summary |
| `profile` | object | Raw profile data (see below) |
| `issues` | array | Ranked issues found |
| `manualChecks` | array | Fields requiring manual verification |
| `recommendations` | array | Specific, actionable fixes — one per issue |
| `competitiveInsights` | array | Head-to-head comparisons against the competitors found |
| `competitors` | array | Nearby competitor summaries, ordered by search rank |
| `poweredBy` | string | Always `GBP Auditor on Apify Store` |
| `dataSource` | string | Always `compass/crawler-google-places` |

#### `profile` object

| Field | Type | Notes |
|-------|------|-------|
| `rating` | number | null | Average star rating (e.g. 4.3) |
| `reviewCount` | number | Total number of reviews |
| `reviewsDistribution` | null | Always `null` — the run requests `maxReviews: 0`, so Google Maps returns no star breakdown. Kept for schema stability. |
| `hasWebsite` | boolean | Whether a website is linked |
| `website` | string | null | Website URL if present |
| `phone` | string | null | Phone number if present |
| `address` | string | null | Full address string |
| `imageCount` | number | Total photos on the listing |
| `categories` | array | Business category tags |
| `permanentlyClosed` | boolean | True if marked permanently closed |
| `temporarilyClosed` | boolean | True if marked temporarily closed |

#### `issues` array items

| Field | Type | Values |
|-------|------|--------|
| `severity` | string | `critical` / `high` / `medium` / `low` |
| `field` | string | Which profile field has the issue |
| `message` | string | Human-readable description of the problem |

**Every point you lose is explained.** If the score is below 90, `issues` names each factor
that cost points and `recommendations` carries a matching fix. A profile that scores full
marks on a factor produces no issue for it, so a 90/90 profile returns `issues: []` —
that empty array means "nothing to fix", not "nothing detected".

Severity is about ranking impact, not about how many points were lost:

| Severity | Meaning |
|----------|---------|
| `critical` | Actively suppresses the listing — e.g. marked permanently closed, or a rating below 3.0 |
| `high` | A missing fundamental — no phone, no website, too few reviews to enter the Local Pack |
| `medium` | Present but underbuilt — partial address, thin photo set, no categories |
| `low` | Already competitive, short of best practice — e.g. a 4.1 rating or 50-99 reviews |

The `summary` field counts `critical` and `high` together as "high-priority".

#### `manualChecks` array items

Fields that cannot be auto-detected (business description, recent posts, services listed,
Q\&A section). Each item has:

| Field | Type | Description |
|-------|------|-------------|
| `field` | string | Field name |
| `message` | string | What to check manually |
| `why` | string | Why it matters for ranking |

#### `competitiveInsights` array items

Always present when at least one competitor was found (empty array if none were). These
compare the target against the competitor set rather than against a fixed benchmark, so
they fire even on a high-scoring profile that has no issues left.

| Field | Type | Description |
|-------|------|-------------|
| `type` | string | `review_gap` / `review_lead` / `photo_gap` / `photo_lead` / `rating_gap` / `rating_lead` / `website_advantage` |
| `message` | string | The comparison, with the competitor named and the gap quantified |
| `action` | string | What to do about it |

#### `competitors` array items

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Competitor business name |
| `rating` | number | null | Their average rating |
| `reviewCount` | number | Their total reviews |
| `hasWebsite` | boolean | Whether they have a website linked |
| `imageCount` | number | Their photo count |
| `rank` | number | null | Their position in the search results |
| `mapsUrl` | string | null | Their Google Maps URL |

Competitors are returned in ascending `rank` order (any unranked result sorts last), and
other locations of the same business are excluded — a shared domain, a matching name, or a
subsidiary that carries the parent brand in its title will not be returned as a rival.
Google re-ranks local results per search, so the specific competitors returned for the same
business can differ between runs.

***

### Scoring Breakdown

| Factor | Max Points | Notes |
|--------|-----------|-------|
| Phone number | 10 | Present or not |
| Website linked | 15 | Present or not |
| Star rating | 15 | Scaled: 4.5+ = full, 4.0+ = 10, 3.5+ = 5, <3.5 = 0 |
| Review count | 20 | Scaled: 100+ = full, 50+ = 15, 20+ = 10, 5+ = 5, <5 = 0 |
| Photo count | 15 | Scaled: 20+ = full, 10+ = 10, 3+ = 5, <3 = 0 |
| Category set | 5 | Present or not |
| Address complete | 10 | Full address = 10, partial = 5, missing = 0 |
| **Total** | **90** | |

**Grade thresholds:** A ≥ 75, B ≥ 55, C ≥ 35, D < 35.

Partial credit on a scaled factor still produces an issue and a recommendation — a listing
with 50 reviews scores 15/20 and gets a `low` severity entry naming the 100-review
threshold it fell short of.

**Fields not auto-scored** (require manual verification):

- Business description text
- Recent Google Business posts
- Services / menu items listed
- Q\&A section populated

***

### Example Output

Generated by running the scorer on a sample profile — the field set, ordering, and summary
wording are exactly what a run writes to the dataset.

```json
{
  "businessName": "Joe's Pizza",
  "searchedAs": "Joe's Pizza",
  "city": "Austin",
  "state": "TX",
  "auditDate": "2026-08-26T14:23:00.000Z",
  "mapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJexampleJoesPizza",
  "placeId": "ChIJexampleJoesPizza",
  "score": 45,
  "maxAutoScore": 90,
  "grade": "C",
  "summary": "\"Joe's Pizza\" scored 45/90 (C). 4 issues found (2 high-priority). 3 competitive insights generated. 4 fields require manual verification.",
  "profile": {
    "rating": 4.1,
    "reviewCount": 18,
    "reviewsDistribution": null,
    "hasWebsite": false,
    "website": null,
    "phone": "(512) 555-0100",
    "address": "1234 S Congress Ave, Austin, TX 78704",
    "imageCount": 7,
    "categories": [
      "Pizza restaurant",
      "Italian restaurant"
    ],
    "permanentlyClosed": false,
    "temporarilyClosed": false
  },
  "issues": [
    {
      "severity": "high",
      "field": "website",
      "message": "No website linked to this listing."
    },
    {
      "severity": "low",
      "field": "rating",
      "message": "Rating is 4.1 — solid, but profiles at 4.5+ earn full marks and win more clicks from the Local Pack."
    },
    {
      "severity": "high",
      "field": "reviewCount",
      "message": "Only 18 reviews. Businesses in the Google Local Pack typically have 20+."
    },
    {
      "severity": "medium",
      "field": "images",
      "message": "Only 7 photos. Listings with 10+ photos get significantly more views."
    }
  ],
  "manualChecks": [
    {
      "field": "businessDescription",
      "message": "Manually verify: does your profile have a 400–750 character business description?",
      "why": "Description is not returned by the Google Maps scraper. Log in to GBP to check."
    },
    {
      "field": "recentPosts",
      "message": "Manually verify: have you posted a Google Business update in the last 7 days?",
      "why": "Recent posts signal activity to Google and appear directly in your listing."
    },
    {
      "field": "servicesOrMenu",
      "message": "Manually verify: are your services, products, or menu items listed in GBP?",
      "why": "Listed services help Google match your business to more relevant queries."
    },
    {
      "field": "questionsAndAnswers",
      "message": "Manually verify: have you pre-populated the Q&A section with common questions?",
      "why": "Pre-answered Q&As appear in your listing and reduce friction for new customers."
    }
  ],
  "recommendations": [
    "Link your website in Google Business Profile → Info. A missing website significantly hurts local search ranking.",
    "You are close to the 4.5 threshold. Ask recent happy customers for a review — at this rating a handful of 5-star reviews moves the average quickly.",
    "Set up an automated review request: send a follow-up text or email 24 hours after every transaction with a direct link to your Google review page.",
    "Add photos of: exterior (for navigation), interior, staff, and your top products/dishes. Use real photos — stock images are flagged by Google."
  ],
  "competitiveInsights": [
    {
      "type": "review_gap",
      "message": "Your top competitor (Tony's Pizzeria) has 312 reviews vs your 18 — a gap of 294.",
      "action": "Set up an automated post-visit review request (SMS or email) to close this gap systematically."
    },
    {
      "type": "photo_gap",
      "message": "Tony's Pizzeria has 47 photos vs your 7 — a gap of 40.",
      "action": "Upload photos of your exterior, interior, staff, and top products. Aim to exceed the competitor count."
    },
    {
      "type": "rating_gap",
      "message": "Tony's Pizzeria has a higher rating (4.6) than you (4.1).",
      "action": "Review your 1-star and 2-star feedback for recurring themes — one fixable issue often drives most negative reviews."
    }
  ],
  "competitors": [
    {
      "name": "Tony's Pizzeria",
      "rating": 4.6,
      "reviewCount": 312,
      "hasWebsite": true,
      "imageCount": 47,
      "rank": 1,
      "mapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJexampleTonys"
    }
  ],
  "poweredBy": "GBP Auditor on Apify Store",
  "dataSource": "compass/crawler-google-places"
}
```

***

### Pricing

**$1.00 per audit — or $0.35 if you are on an Apify Business plan.** One run = one
complete audit. No separate subscription to this Actor.

#### Your price depends on your Apify plan

This Actor offers the Apify Store pricing discount. It is applied automatically at run
time based on the plan your account is already on — there is nothing to claim or enter.

| Your Apify plan | Per audit | 100 audits | 500 audits |
|-----------------|-----------|-----------|-----------|
| Free | $1.00 | $100.00 | $500.00 |
| Starter | $0.70 | $70.00 | $350.00 |
| Scale | $0.50 | $50.00 | $250.00 |
| Business or above | **$0.35** | **$35.00** | **$175.00** |

If you audit profiles in bulk — agency prospecting, client reporting, a monthly re-audit
across a book of business — the Business-plan rate is where this Actor is designed to be
used.

#### How the charge is calculated

This Actor is billed **pay per event**. The charge is Apify's built-in `Actor start`
event, priced **per GB of allocated memory**. This Actor runs at **2 GB**, so one audit
bills 2 events: 2 x $0.50 = $1.00 at the Free rate, down to 2 x $0.175 = $0.35 at the
Business rate. A further **$0.001** covers the single result record written to the dataset.

#### Costs billed separately by Apify

Two things are **not** included in the price above:

1. **Apify platform usage (compute).** This Actor is a lightweight orchestrator — it peaks
   at roughly 55 MB of real memory and finishes in about 20-70 seconds — so platform usage
   is typically a fraction of a cent per run, charged at your plan's rates.
2. **The sub-actor call.** This Actor calls `compass/crawler-google-places` to fetch Google
   Maps data. That Actor bills its own pay-per-event charges (a `place-scraped` event) to
   your account. See its listing for current rates.

#### Reliability, and what happens if a run fails

**No failed or timed-out runs over the last 30 days** — see the run statistics on this
listing, which Apify publishes automatically and the developer cannot edit. (Runs a user
cancels mid-flight are counted separately by Apify as aborted.)

Be aware that `Actor start` is charged **when a run begins**, so a run that fails is still
billed. There is no automatic refund. If a run fails or returns nothing useful, open an
issue on this Actor and I will make it right.

***

### Notes

- **One audit per run.** The input takes a single `businessName` + `city`. To audit a book
  of business, start one run per profile from the Apify API, a scheduled task, or your own
  script — each run writes its own dataset item, so results can be merged downstream
- Results reflect publicly visible Google Maps data at the time of the run
- Business matching uses name + city — add `state` if results are ambiguous
- The `compass/crawler-google-places` sub-actor is maintained by Apify and handles
  proxy rotation and anti-blocking automatically
- Google Maps data structure may change; the sub-actor is updated by Apify when this happens

***

### Support

Questions or issues? Open a GitHub issue or contact via the Apify Store listing page.

# Actor input Schema

## `businessName` (type: `string`):

The exact or approximate name of the business to audit

## `city` (type: `string`):

City where the business is located

## `state` (type: `string`):

US state abbreviation to narrow results

## `maxCompetitors` (type: `integer`):

How many competitors to include in the comparison (1–5)

## Actor input object example

```json
{
  "businessName": "Joe's Pizza",
  "city": "Austin",
  "state": "TX",
  "maxCompetitors": 3
}
```

# Actor output Schema

## `auditResults` (type: `string`):

The audit for the requested business, as a dataset item. Opens on the formatted Audit Results table.

# 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 = {
    "businessName": "Joe's Pizza",
    "city": "Austin",
    "state": "TX"
};

// Run the Actor and wait for it to finish
const run = await client.actor("acebuilds/gbp-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 = {
    "businessName": "Joe's Pizza",
    "city": "Austin",
    "state": "TX",
}

# Run the Actor and wait for it to finish
run = client.actor("acebuilds/gbp-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 '{
  "businessName": "Joe'\''s Pizza",
  "city": "Austin",
  "state": "TX"
}' |
apify call acebuilds/gbp-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,acebuilds/gbp-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/QIdxEfhwbtceaECLX/builds/i6RejKO5suWzMSFN3/openapi.json
