# Google Maps Rank Tracker (Geo-Grid Heatmap) (`fringed_jebel/geogrid-rank-tracker`) Actor

Google Maps rank tracker for local SEO. Enter a business and a keyword to see its rank at every point of a 3x3 to 9x9 geo-grid, with the top 3 competitors per point, average rank, % in the map pack and a heatmap PNG. Pay per point, no subscription.

- **URL**: https://apify.com/fringed_jebel/geogrid-rank-tracker.md
- **Developed by:** [Jamie Fitzgerald](https://apify.com/fringed_jebel) (community)
- **Categories:** SEO tools, Lead generation, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$10.00 / 1,000 grid point checkeds

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

A business can rank #2 on Google Maps at its own front door and not show up at all two miles away, where most of its customers are. Checking from one spot hides that. This Google Maps rank tracker checks the rank at every point of a grid across the service area and draws it as a heatmap, so you can see where the business actually shows up and who beats it everywhere else.

Give it a business, one or more keywords and a grid from 3x3 to 9x9. You get the rank at each point, the top 3 businesses there, a summary and a PNG heatmap. It does the same job as Local Falcon or BrightLocal grids, but you pay $0.01 per point checked. No subscription, no credits that expire.

![Sample geo-grid heatmap](https://api.apify.com/v2/key-value-stores/znnOHe51pIwTd288C/records/sample-heatmap.png?signature=G9VHs8ohr9TdQ9SrWnkJ)

*Sample: an HVAC company, "furnace repair", 5x5 grid, 1 mile apart. It ranks #2 at its own address but isn't in the top 20 at 17 of 25 points.*

### How to use the Google Maps rank tracker

1. Type the business name as it shows on Google Maps, with the town (e.g. "Joe's Plumbing Austin TX").
2. Add one or more keywords customers search for (e.g. "plumber", "water heater repair").
3. Pick a grid size. 3x3 costs $0.09 per keyword, 5x5 costs $0.25.
4. Run it. The heatmap and summary are in the Storage tab (key-value store), and the rank at every point is in the dataset.

Run it with no input and it does a small 3x3 demo grid so you can see the output first.

### What you get

- **Rank at every grid point** (1-20, or up to 60), or `null` if the business isn't in the results there.
- **The top 3 businesses at each point**, with name, place ID, rating and review count.
- **Summary per keyword**:
  - average rank (not found counts as 21)
  - average rank where found
  - % of points in the top 3 (map pack) and top 10
  - points found
- **Top 10 competitors across the grid**, ranked by how many points they hold in the top 3.
- **A heatmap image (PNG)** you can drop into a report, a client pitch or an email, plus an **HTML report** with the image and tables.
- **Multiple keywords in one run.** Each keyword gets its own grid, summary and heatmap.

### Who uses a geo-grid rank tracker

- Local SEO agencies running Local Falcon-style audits and monthly client reports.
- Business owners checking whether they show up in the neighbourhoods they actually serve.
- Lead generation: run a grid on a prospect and send them the heatmap.

### Input

| Field | What it does |
|---|---|
| `businessName` | Name as shown on Google Maps, ideally with the town, e.g. "Brewer Heating & Air Conditioning West Chester PA". |
| `placeId` | Optional. Google place ID (ChIJ...). The most exact way to match the business. |
| `keywords` | What customers type into Maps, e.g. "hvac repair". One grid per keyword. |
| `lat`, `lng` or `address` | Optional grid centre. Defaults to the business's pin. |
| `gridSize` | 3 to 9 points per side: 3 = 9 points, 5 = 25, 7 = 49, 9 = 81. |
| `spacing` or `radius` | Distance between points, or from the centre to the edge. Default is 1 mile. |
| `distanceUnit` | `mi` or `km`. |
| `maxRank` | How many results to check per point: 20 (default), 40 or 60. |
| `viewportMeters` | How zoomed-in the simulated Maps search is at each point. Default 3000. |

Example:

```json
{
  "businessName": "Brewer Heating & Air Conditioning West Chester PA",
  "keywords": ["hvac repair", "furnace repair"],
  "gridSize": 5,
  "spacing": 1,
  "distanceUnit": "mi"
}
```

### Output

The dataset has one row per keyword per grid point:

```json
{
  "business": "RJM Systems",
  "placeId": "ChIJe-OxE7X7xokRxpPHnzvxfhE",
  "keyword": "hvac repair",
  "row": 1, "col": 0,
  "lat": 39.965489, "lng": -75.6464,
  "rank": 1,
  "resultsChecked": 20,
  "top3": [
    { "rank": 1, "name": "RJM Systems", "placeId": "ChIJe-OxE7X7xokRxpPHnzvxfhE", "rating": 4.6, "reviews": 132 },
    { "rank": 2, "name": "Brewer Heating & Air Conditioning", "placeId": "ChIJB9I16WbxxokRFhJ0VpWfU-4", "rating": 4.9, "reviews": 257 },
    { "rank": 3, "name": "Brandywine Valley Heating & Air Conditioning", "placeId": "ChIJj_UAuNvzxokRh1ntAuAcwYI", "rating": 4.9, "reviews": 622 }
  ]
}
```

Row 0 is the north edge and column 0 is the west edge. Rows may arrive out of order, so sort by `row` and `col`.

The key-value store holds:

- `SUMMARY`: summary and competitor list for every keyword.
- `HEATMAP-<keyword>.png`: heatmap image.
- `HEATMAP-<keyword>.html`: report with the image, the competitor table and every point.

### Pricing

**$0.01 per grid point checked.** One keyword at one point is one charge. Platform usage is included.

| Grid | Points | Cost per keyword |
|---|---|---|
| 3x3 | 9 | $0.09 |
| 5x5 | 25 | $0.25 |
| 7x7 | 49 | $0.49 |
| 9x9 | 81 | $0.81 |

Set a maximum cost per run in Apify and the actor stops cleanly when it hits it.

### How accurate is geo-grid rank tracking?

- **It's a simulated search, not a real phone.** At each point the actor runs a Google Maps search with the map centred there. This is the same approach Local Falcon and similar tools use. It is not a GPS reading from a real device, and Google personalises results by search history, device and exact location. Real customers can see somewhat different results.
- **Expect about ±2 positions of noise.** Running the same grid twice, or from a different server, moved ranks by 1-2 places at some points in our tests. Look at the pattern across the grid, not at one number.
- **Results change over time.** Google reshuffles local results often. Re-run on a schedule to track trends.
- **Ads are not flagged.** Results appear to be organic listings, but sponsored pins aren't marked separately.
- **Name matching is strict.** If the business isn't found, give its `placeId`. With only a name, the actor uses the top Google Maps match near the centre and logs which business it picked.
- **Plain background.** The heatmap draws the grid on a plain background with a scale bar and a north marker, not on a street map.

### Tips

- Start with a 5x5 grid at 0.5-1 mile spacing for a town, or 2-3 miles for a metro service area.
- Track the 2-3 keywords that actually bring in calls, and re-run weekly or monthly with an Apify schedule.
- The competitor list shows who to study: their categories, review counts and how close they are to each point.

### About the author

I'm Jamie FitzGerald. I write Python and JavaScript, and most of my public projects are machine-learning models for markets, like LSTM models that predict SPY's next-day move.

Code and other projects: [github.com/jfitz001](https://github.com/jfitz001)

If a business won't match, a grid looks wrong, or you want a feature, open an issue on the Issues tab. I read them.

### FAQ

**Do I need a Google API key?** No.

**Does it work outside the US?** Yes. Set `country` (Google `gl` code, e.g. `gb`) and `language` (`hl`, e.g. `en`).

**Can I scan a service-area business with no public address?** Yes. Give its name or place ID and a centre with `lat`/`lng` or `address`.

# Actor input Schema

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

Name as it appears on Google Maps, ideally with the town, e.g. "Brewer Heating & Air Conditioning West Chester". Used to find the place ID and pin. Optional if you give a place ID.

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

What customers type into Google Maps, e.g. "hvac repair" or "plumber near me". Each keyword runs the full grid and is charged per point.

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

3 = 3x3 (9 points, $0.09 per keyword), 5 = 25 ($0.25), 7 = 49 ($0.49), 9 = 81 ($0.81). Start with 3 or 5.

## `spacing` (type: `number`):

In the unit below. Default 1 mile. Leave empty to use radius instead.

## `distanceUnit` (type: `string`):

Miles or kilometres.

## `placeId` (type: `string`):

Most exact match, e.g. ChIJ.... If set, ranks are matched on this ID.

## `address` (type: `string`):

Used if lat/lng are empty. Looked up on Google Maps.

## `lat` (type: `number`):

Defaults to the business pin.

## `lng` (type: `number`):

Defaults to the business pin.

## `radius` (type: `number`):

Alternative to spacing: the grid edge sits this far from the centre.

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

20, 40 or 60. If the business is not in this many results, the point shows as not found (20+).

## `viewportMeters` (type: `integer`):

How zoomed-in the simulated Maps search is at each point. Smaller = more local.

## `language` (type: `string`):

Google hl code.

## `country` (type: `string`):

Google gl code.

## `heatmap` (type: `boolean`):

Save PNG + interactive HTML heatmaps to the key-value store.

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

Apify datacenter proxy (default) works. Google SERP proxy does not support this Maps endpoint.

## Actor input object example

```json
{
  "businessName": "Brewer Heating & Air Conditioning West Chester PA",
  "keywords": [
    "hvac repair"
  ],
  "gridSize": 3,
  "spacing": 1,
  "distanceUnit": "mi",
  "maxRank": 20,
  "viewportMeters": 3000,
  "language": "en",
  "country": "us",
  "heatmap": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

One row per keyword per grid point

## `files` (type: `string`):

SUMMARY JSON and heatmap PNG/HTML files

# 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": "Brewer Heating & Air Conditioning West Chester PA",
    "keywords": [
        "hvac repair"
    ],
    "gridSize": 3,
    "spacing": 1,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fringed_jebel/geogrid-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 = {
    "businessName": "Brewer Heating & Air Conditioning West Chester PA",
    "keywords": ["hvac repair"],
    "gridSize": 3,
    "spacing": 1,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("fringed_jebel/geogrid-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 '{
  "businessName": "Brewer Heating & Air Conditioning West Chester PA",
  "keywords": [
    "hvac repair"
  ],
  "gridSize": 3,
  "spacing": 1,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call fringed_jebel/geogrid-rank-tracker --silent --output-dataset

```

## MCP server setup

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