# Google Maps Geo-Grid Rank Tracker & Competitor Heatmap (`scrapebench/google-maps-geo-grid-rank-tracker`) Actor

See where a business ranks on Google Maps at every point of a grid around it, and where every competitor ranks too: rank per point to 60, average rank, top-3 counts, a rank matrix per rival and an HTML heatmap.

- **URL**: https://apify.com/scrapebench/google-maps-geo-grid-rank-tracker.md
- **Developed by:** [ScrapeBench](https://apify.com/scrapebench) (community)
- **Categories:** SEO tools, Lead generation
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.012 / grid point scanned

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 Geo-Grid Rank Tracker & Competitor Heatmap

### Pain points

- One rank check from one spot says nothing about a local business: rankings change block by block, and a customer two kilometres away sees a different list.
- Knowing your own rank is half the picture: to act on it you need to see which rival beats you at which spot.
- A business that is not on Google Maps' first page for its keyword (in our Oakland test the café was below rank 20 at 20 of 25 grid points) needs ranks to 60 to see any movement.
- Tracking ten keywords for twenty locations one search at a time is two hundred separate checks.

### What we solve

- A rank for every grid point, down to 60, in one flat price per point.
- Every competitor's rank at every point, from the same searches, at no extra charge: who beats you, where, and by how much.
- Several businesses and several keywords in one run.
- The map zoom is chosen by you and stated on every row, so two runs you compare used the same view.

### Summary

Give a business by its name and street address and the keywords its customers type, and the actor searches Google Maps from every point of a grid around it (3x3 to 9x9, any spacing), the way a customer standing at that spot would. Each point and keyword is one row: the business's rank there (down to 60, all three pages included), the top three places at that point, and every other place's rank with its place ID. Each business and keyword also gets a free summary (average rank, points in the top 3, 10 and 20, the rank matrix, the review count of the rivals that beat you) and a free row per competitor that reaches the top 10 anywhere on the grid, with its rank at every point. An HTML heatmap per business and keyword lands in the run's key-value store. Leave the business out and give a location to map who owns a keyword across a neighbourhood or a town. The map zoom is an input and is written on every row, because it changes rankings a lot.

### Who it's for

- Local-SEO agencies reporting rankings to clients
- Multi-location brands and franchises comparing their stores
- Business owners checking a Google Business Profile change
- Franchise developers, investors and brokers sizing up a local market

### How to use

Set the input, run the actor, and collect results from the run's dataset (export to JSON/CSV/Excel, or pull via the Apify API). Example input:

```json
{
  "businesses": [
    "Blue Bottle Coffee 480 9th St Oakland"
  ],
  "keywords": [
    "coffee shop"
  ],
  "gridSize": 5,
  "stepKm": 1,
  "zoomMeters": 3000
}
```

See **Inputs** below for every available field.

### What you get

One row per record:

| Field | Description |
|---|---|
| `row_type` | point (one grid point, charged), summary or competitor (free) |
| `business` | The tracked business as Google Maps names it (empty in market mode) |
| `business_place_id` | Google's place ID for the tracked business |
| `keyword` | The search typed at each point |
| `grid_row` | Point rows: 0 is the northernmost row |
| `grid_col` | Point rows: 0 is the westernmost column |
| `latitude` | Point rows: the grid point's latitude |
| `longitude` | Point rows: the grid point's longitude |
| `zoom_m` | Width in metres of the Google Maps view searched |
| `rank` | Point rows: the business's rank there; empty when not in the ranks scanned |
| `scanned_to` | Point rows: how many places were read at the point |
| `top3` | Point rows: the first three places at the point |
| `competitors` | Point rows: every other place read there, with its rank and place ID |
| `average_rank` | Summary and competitor rows: average rank over the points where it ranks |
| `average_total_rank` | Summary rows: average over every point, an unranked point counted as the deepest rank + 1 |
| `points_ranked` | Summary and competitor rows: points where it ranks |
| `top3_points` | Summary and competitor rows: points where it is in the top 3 |
| `top10_points` | Summary rows: points in the top 10 |
| `top20_points` | Summary rows: points in the top 20 |
| `share_of_top3` | Summary and competitor rows: top-3 points divided by all points |
| `top3_average_reviews` | Summary rows: average review count of the rivals that reach the top 3 anywhere |
| `rank_matrix` | Summary and competitor rows: rank at every point, north row first |
| `place_id` | Competitor rows: the rival's Google place ID |
| `name` | Competitor rows: the rival's name |
| `rating` | Google rating of the business (summary) or rival (competitor) |
| `review_count` | Google review count of the business (summary) or rival (competitor) |

Sample:

```json
{
  "row_type": "summary",
  "business": "Blue Bottle Coffee",
  "business_place_id": "ChIJLRIbILeAj4ARyWoMWFQmkSY",
  "business_address": "480 9th St, Oakland, CA 94607",
  "keyword": "coffee shop",
  "grid_size": 5,
  "step_km": 1.0,
  "zoom_m": 3000,
  "points": 25,
  "points_ranked": 20,
  "average_rank": 35.95,
  "average_total_rank": 40.96,
  "top3_points": 0,
  "top10_points": 1,
  "top20_points": 5,
  "share_of_top3": 0.0,
  "rating": 4.4,
  "review_count": 396,
  "top3_average_reviews": 231.3,
  "rank_matrix": [
    [
      46,
      56,
      null,
      null,
      null
    ],
    [
      33,
      17,
      38,
      49,
      null
    ],
    [
      24,
      14,
      6,
      39,
      59
    ],
    [
      24,
      12,
      13,
      22,
      51
    ],
    [
      54,
      50,
      54,
      58,
      null
    ]
  ],
  "source_url": "https://www.google.com/maps/search/?api=1&query=Blue%20Bottle%20Coffee&query_place_id=ChIJLRIbILeAj4ARyWoMWFQmkSY"
}
```

### Inputs

| Field | Required | Type | Default | Description |
|---|---|---|---|---|
| `businesses` | no | array | `["Blue Bottle Coffee 480 9th St Oakland"]` | Each business by its name and street address, as you would type it into Google Maps, e.g. "Blue Bottle Coffee 480 9th St Oakland". The grid is centred on the business, and each row names the place it resolved to. Leave empty to map a keyword's competitors around a location instead (below). |
| `business` | no | string | — | A single business, for API calls and saved tasks. Combined with the list above. |
| `keywords` | no | array | `["coffee shop"]` | What a customer types into Google Maps, e.g. "coffee shop" or "emergency plumber". Each keyword is one grid scan per business. |
| `keyword` | no | string | — | A single keyword, for API calls and saved tasks. Combined with the list above. |
| `gridSize` | no | integer | `5` | Points per side: 3, 5, 7 or 9 (3 = 9 points, 5 = 25, 7 = 49, 9 = 81). Any other number is rounded to the nearest of these. Each point is one charge per keyword. |
| `stepKm` | no | number | `1` | Kilometres between neighbouring grid points. 1 km suits a city neighbourhood; 3-5 km a suburb or a service-area business. |
| `zoomMeters` | no | integer | `3000` | How wide the Google Maps view is at each point, which is the map's zoom. It changes rankings a lot (in our test one business ranked 2nd with a 1,000 m view and 33rd with a 30,000 m view from the same spot), so keep it the same between runs you compare. 3,000 m is about a neighbourhood on a phone screen. |
| `maxRank` | no | integer | `60` | Look for the business down to this rank: 20, 40 or 60 (Google Maps pages hold 20 places). Included in the price per point. Beyond it the point reads "not ranked". |
| `includeCompetitorMatrix` | no | boolean | `true` | Adds a free row for every competitor that reaches the top 10 at any point: its rank at every point, average rank, top-3 count, rating and reviews. |
| `location` | no | string | — | With no business above, the grid is centred here and you get the competitor rows only, e.g. "Downtown Oakland, CA". |
| `centerLatitude` | no | number | — | With no business, centre the grid on these coordinates instead of a location name. |
| `centerLongitude` | no | number | — | Pairs with the centre latitude. |
| `maxGridPoints` | no | integer | — | Optional spend cap for the WHOLE run (all businesses and keywords together): scanning stops at this many points and a free row says how many were left. |
| `proxyConfiguration` | no | object | `{"useApifyProxy": true}` | Apify's datacenter proxy by default, which is what this actor was built for. |

### Pricing (Pay Per Event)

You pay per result (`grid-point`) — **no charge for empty runs**. Example: **25 grid points** at *$0.012/result* ≈ **$0.30**.

A 5x5 grid is 25 points ($0.30) per keyword; 7x7 is 49 ($0.59). Searches go through Apify Proxy, billed by Apify as platform usage on your plan, like compute.

### Use cases

- Monthly client report: one business, three keywords, a 7x7 grid 1 km apart; send the heatmaps and the average rank.
- Agency prospecting: a 3x3 grid for each prospect's main keyword; open the pitch with their weak spots.
- Franchise comparison: one keyword for fifteen locations at the same grid and zoom; find the weakest stores.
- Market entry: no business, just a location and a keyword; the competitor rows show who owns "dentist" in each part of town.
- Before and after: run the same grid before and after changing a business's Google categories or name, and compare the rank matrices.

### Why this actor

- The full competitor matrix: every rival's rank at every point, its average rank and its top-3 count, free with the scan.
- Ranks to 60 included: one flat charge per point, whichever page the business is on.
- Batch: several businesses times several keywords in one run, with a run-wide cap on grid points.

### Limitations & updates

Ranks are read from Google Maps (the maps search), not from the map pack on Google's web search results, and sponsored places are not marked. A rank is where the business appears in Google Maps' answer for the map view centred on each point; it is not a GPS-located phone search, and results vary by 1 to 4 places between internet connections (measured 2026-11-06). The business is found by searching its name and address, and every row names the place it resolved to; check it on the first run. Built and tested on US businesses.

### FAQ

**How is a grid point searched?**

The actor searches Google Maps for your keyword with the map view centred on the point, the way the Google Maps site does when you move the map there. It does not fake a phone's GPS. The rank is the business's position in that answer, counting each place once.

**Why does the map zoom matter?**

Google Maps ranks places inside the visible map, so a wider view brings in more competitors. In our test (2026-11-06) Blue Bottle Coffee in Oakland ranked 2nd for "coffee shop" with a 1,000 m view, 6th at 3,000 m, 16th at 10,000 m and 33rd at 30,000 m, all from the same spot. Pick one zoom and keep it between runs you compare. The default, 3,000 m, is about a neighbourhood on a phone screen.

**Will my ranks match another rank tracker's?**

Not exactly. Google's answer varies a little with where a search comes from: the same grid read from two different internet connections differed by 1 to 4 places at the same points in our test. Compare runs of this actor with each other, at the same zoom.

**What are the competitor rows?**

Every place that reaches the top 10 at any point of the grid gets a free row: its rank at every point (rank_matrix), average rank, points in the top 3, rating and review count. They come from the same searches as your ranks, so they cost nothing extra. Turn them off with "Competitor rows".

**Can I map a market without a business?**

Yes. Leave the business empty and give a location ("Downtown Oakland, CA") or centre coordinates. You get the point rows and the competitor rows; there is no summary, since there is no business to rank.

**How am I charged?**

$0.012 per grid point per keyword, with every page to rank 60 included. A 5x5 grid for one keyword is $0.30; a 7x7 grid $0.59. Summary, competitor and notice rows are free, and so is a point Google refused. Set "Maximum grid points for the run" to cap the spend.

**Where is the heatmap?**

In the run's key-value store, one HTML page per business and keyword, named heatmap-<business>-<keyword>: the grid as coloured squares (green top 3, light green top 10, yellow top 20, red beyond), north at the top, the business at the centre.

**How long does a run take?**

Measured on 2026-11-06: a 5x5 grid for one keyword in about 10 seconds. Each point takes one to three Google Maps searches, five points at a time.

### Which actor to choose

Works with:

- **Google Maps Place Matcher: Business Lookup by Name & Address** — You have a messy list of businesses and want each one's Google place ID, address and phone before you track them.

### Guides & use cases

Written up on **[scrapebench.dev](https://scrapebench.dev)** — the bench that runs and verifies this actor against the live source every night:

- **How-to:** [How to run Google Maps Geo-Grid Rank Tracker & Competitor Heatmap](https://scrapebench.dev/guides/how-to-google-maps-geo-grid-rank-tracker/)
- **Use case:** [Google Maps rank grid: coffee shop around a café in Oakland](https://scrapebench.dev/use-cases/coffee-shop-rank-grid-oakland/)

More actors, coverage and nightly verification results: **[scrapebench.dev](https://scrapebench.dev)**

### Works with AI assistants (MCP)

Callable as an MCP tool, so Claude, Cursor, VS Code Copilot and other MCP clients can run it directly. Grab the config from the [MCP tab](https://apify.com/scrapebench/google-maps-geo-grid-rank-tracker/api/mcp) on this page — Apify hosts the server and keeps that snippet current, and OAuth signs you in on first connect, so no API token goes in your config file.

Then just ask:

> "Where does Blue Bottle Coffee at 480 9th St, Oakland rank on Google Maps for "coffee shop" across a 5x5 grid, 1 km apart, and who beats it?"

A 5x5 grid for one keyword is 25 charged points ($0.30); start there before a 7x7 or several keywords. Runs started this way bill exactly like any other run.

### Data & lawful use

This actor collects publicly available information that Google (Google Maps) publishes for public
lookup — licence records, business listings and the contact details professionals list there.
Some of it identifies individuals (sole proprietors, licence holders, agents), so treat any
names, phone numbers and emails in your results as personal data.

When you run this actor, you decide what is collected and what it is used for, so you are the
controller of that data and we act only as your processor (Apify Standard Actor Contract).
You need a lawful basis for your use, must honour opt-out and deletion requests, and must
follow the marketing rules that apply to you (CAN-SPAM, TCPA, GDPR, CCPA and similar).
Don't use the output for unsolicited bulk messaging, for screening covered by the FCRA, or
for anything the source's own terms forbid.

Your results are stored in your own Apify account. We don't keep a copy of your results and
don't sell or share them.

### Disclaimer

This is an independent tool. It is not affiliated with, endorsed by or sponsored by
Google (Google Maps). Names and marks are used only to describe the data source. Records are
provided as published by the source and may be out of date or incomplete. For anything
consequential (hiring, a permit, a legal decision), confirm the record with the issuing
agency. You are responsible for making sure your use complies with applicable law and the
source's terms.

### Need help?

Open an issue on this actor's **Issues** tab and we'll respond there. Bug reports, missing
fields and feature requests are all welcome. Guides and examples: [scrapebench.dev](https://scrapebench.dev).

If you are a person named in this data and want your record removed from our copy, email
apify-help@scrapebench.dev; the source agency remains the official record.

Your run may be used to improve our services. If something looks off, or you want this actor to fit your use better, please share your run data with us: [Apify Console → Settings → Security → Share run data with developers](https://console.apify.com/settings/security). It lets us see exactly what went wrong and keep improving the actor.

# Actor input Schema

## `businesses` (type: `array`):

Each business by its name and street address, as you would type it into Google Maps, e.g. "Blue Bottle Coffee 480 9th St Oakland". The grid is centred on the business, and each row names the place it resolved to. Leave empty to map a keyword's competitors around a location instead (below).

## `business` (type: `string`):

A single business, for API calls and saved tasks. Combined with the list above.

## `keywords` (type: `array`):

What a customer types into Google Maps, e.g. "coffee shop" or "emergency plumber". Each keyword is one grid scan per business.

## `keyword` (type: `string`):

A single keyword, for API calls and saved tasks. Combined with the list above.

## `gridSize` (type: `integer`):

Points per side: 3, 5, 7 or 9 (3 = 9 points, 5 = 25, 7 = 49, 9 = 81). Any other number is rounded to the nearest of these. Each point is one charge per keyword.

## `stepKm` (type: `number`):

Kilometres between neighbouring grid points. 1 km suits a city neighbourhood; 3-5 km a suburb or a service-area business.

## `zoomMeters` (type: `integer`):

How wide the Google Maps view is at each point, which is the map's zoom. It changes rankings a lot (in our test one business ranked 2nd with a 1,000 m view and 33rd with a 30,000 m view from the same spot), so keep it the same between runs you compare. 3,000 m is about a neighbourhood on a phone screen.

## `maxRank` (type: `integer`):

Look for the business down to this rank: 20, 40 or 60 (Google Maps pages hold 20 places). Included in the price per point. Beyond it the point reads "not ranked".

## `includeCompetitorMatrix` (type: `boolean`):

Adds a free row for every competitor that reaches the top 10 at any point: its rank at every point, average rank, top-3 count, rating and reviews.

## `location` (type: `string`):

With no business above, the grid is centred here and you get the competitor rows only, e.g. "Downtown Oakland, CA".

## `centerLatitude` (type: `number`):

With no business, centre the grid on these coordinates instead of a location name.

## `centerLongitude` (type: `number`):

Pairs with the centre latitude.

## `maxGridPoints` (type: `integer`):

Optional spend cap for the WHOLE run (all businesses and keywords together): scanning stops at this many points and a free row says how many were left.

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

Apify's datacenter proxy by default, which is what this actor was built for.

## Actor input object example

```json
{
  "businesses": [
    "Blue Bottle Coffee 480 9th St Oakland"
  ],
  "keywords": [
    "coffee shop"
  ],
  "gridSize": 5,
  "stepKm": 1,
  "zoomMeters": 3000,
  "maxRank": 60,
  "includeCompetitorMatrix": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

row_type point: rank at one grid point; summary: average rank and rank matrix; competitor: a rival's rank at every point.

## `heatmaps` (type: `string`):

One HTML heatmap per business and keyword, keyed heatmap-<business>-<keyword>.

# 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 = {
    "businesses": [
        "Blue Bottle Coffee 480 9th St Oakland"
    ],
    "keywords": [
        "coffee shop"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapebench/google-maps-geo-grid-rank-tracker").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 = {
    "businesses": ["Blue Bottle Coffee 480 9th St Oakland"],
    "keywords": ["coffee shop"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapebench/google-maps-geo-grid-rank-tracker").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 '{
  "businesses": [
    "Blue Bottle Coffee 480 9th St Oakland"
  ],
  "keywords": [
    "coffee shop"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapebench/google-maps-geo-grid-rank-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapebench/google-maps-geo-grid-rank-tracker"
        }
    }
}
```

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/j0igj3QmZXjbyfwWT/builds/KT32kMSfCdlI05FLg/openapi.json
