# Google Maps Rank Tracker: Local SEO Geo-Grid Heatmap (`vncntlabs/google-maps-rank-tracker`) Actor

Local rank tracker for Google Maps: see your ranking at every point of a geo-grid (up to 15 x 15) around your business. Rank per point, top competitors, average rank (ARP/ATRP), share of top 3 (SoLV), a competitor leaderboard and an HTML heat map. Pay per grid point, no monthly seat.

- **URL**: https://apify.com/vncntlabs/google-maps-rank-tracker.md
- **Developed by:** [vncntlabs Labs](https://apify.com/vncntlabs) (community)
- **Categories:** SEO tools, Marketing, MCP servers
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 grid points

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 Rank Tracker: local rank grid and geo-grid heatmap for local SEO

**A local rank tracker for Google Maps.** See where your business ranks on Google Maps from every part of town: the Actor lays a geo-grid of GPS points around your business (3 x 3 up to 15 x 15, square or circle, km or miles), runs the Maps search for each keyword at every point and returns your rank at each point, the top competitors there, average rank (ARP / ATRP), share of the top 3 and an HTML heat map. Pay per grid point, with no monthly seat per client.

Built for:

- **Local SEO agencies**: grid scans for client reports and pitches, scheduled weekly, without a monthly SaaS seat per client.
- **Multi-location brands and franchises**: the best-ranked branch at every point of a city ("any location" mode).
- **Local businesses**: dentists, plumbers, lawyers, HVAC, restaurants, car dealers. Find the neighbourhoods where you drop out of the map pack and who takes your place.

You pay **per grid point scanned**. Points where Google Maps shows no businesses, failed requests and the business lookup are free.

### What you get

| Row (`type`) | What's in it | Charged |
|---|---|---|
| `point` | one per grid point and keyword: `rank` (null if not in the results), `rankLabel`, `inTop3`/`inTop10`/`inTop20`, point label (`A1` = north-west corner), latitude/longitude, distance and direction from the center, the top N competitors there (rank, name, rating, reviews, category, website, place ID), `#1`/`#2`/`#3` names, results and ads count, Google Maps link | `grid-point` |
| `summary` | one per keyword: ARP, ATRP, median/best/worst rank, % of points in top 3 / top 10 / top 20, % found, `rankGrid` (2-D array) and a text `heatmap`, top competitors | free |
| `competitor` | leaderboard per keyword (top 20): every business seen in the top 20 across the grid with its ATRP, average rank, share of top 3, % of points where it appears, rating and reviews, and `isYou` | free |

Plus a **heat-map report** (`REPORT`, HTML, no scripts) in the run's key-value store: a colour-coded grid per keyword (green = top 3, light green = 4-10, orange = 11-20, red = 20+ or not found), the stats and the leaderboard. Every dot links to that Google Maps search. Open it from the run's **Output** tab.

**Metrics**

- **ARP** (`averageRank`): your average rank over the points where you're in the top 20.
- **ATRP** (`averageTotalRank`): your average over all points, counting "not in top 20" as 21. Lower is better.
- **SoLV** (`top3Pct`): share of points where you're in the Google Maps 3-pack.

### Input examples

5 x 5 grid around your business, 1 km apart (the default):

```json
{ "keywords": ["dentist"], "googleMapsUrl": "https://www.google.com/maps/place/Swish+Dental+Downtown/@30.2674035,-97.7451433,17z/data=..." }
```

Find the business by name, 3 keywords, 7 x 7 circle with 1-mile spacing:

```json
{ "keywords": ["emergency plumber", "plumber near me", "water heater repair"],
  "businessName": "Applewood Plumbing Heating & Electric", "businessLocation": "Denver, CO",
  "gridSize": 7, "gridShape": "circle", "pointSpacing": 1, "distanceUnit": "mi" }
```

A brand's best branch at each point of a city grid:

```json
{ "keywords": ["gym"], "businessName": "Planet Fitness", "website": "planetfitness.com",
  "matchMode": "any_location", "center": "41.8781, -87.6298", "gridSize": 7, "pointSpacing": 2 }
```

**Tips**

- **Use the Google Maps URL** of the business (or a `maps.app.goo.gl` share link) for an exact match. A name works too: the Actor looks it up once (free) and locks on to that exact listing.
- **Spacing**: 0.3-1 mi (0.5-1.5 km) for city-centre businesses, 2-5 mi for service-area businesses and car dealers.
- **Zoom** sets the map view of each search: 13 (default) covers a city district, like a searcher scanning the map; 12 suits wide metro grids; 15 and up is one neighbourhood, so a business outside that small view won't appear there.
- **Circle** grids skip the far corners, so you pay for fewer points (a 7 x 7 circle is 37 points instead of 49).
- Schedule the same task weekly to track progress; every row has `scrapedAt`.

### Output example (one grid point, shortened)

```json
{
  "type": "point", "keyword": "dentist", "business": "Swish Dental Downtown", "point": "D2", "row": 2, "col": 4,
  "latitude": 30.2763967, "longitude": -97.7347302, "distanceKm": 1.414, "direction": "NE",
  "rank": 1, "rankLabel": "1", "found": true, "inTop3": true, "inTop10": true, "inTop20": true,
  "top1": "Swish Dental Downtown", "top2": "Enamel Dentistry Saltillo (East Austin)", "top3": "Primavera Dental Group - Downtown Austin",
  "competitors": [
    { "rank": 1, "title": "Swish Dental Downtown", "placeId": "ChIJdWgENLS1RIYReos4IrRuTmQ", "rating": 4.5,
      "reviews": 991, "category": "Dentist", "domain": "www.swishsmiles.com" }
  ],
  "resultsCount": 100, "adsCount": 0, "googleMapsUrl": "https://google.com/maps/search/dentist/@30.2763967,-97.7347302,13z?hl=en&gl=US&...",
  "chargedEvents": ["grid-point"], "scrapedAt": "2026-10-17T05:14:18Z"
}
```

Summary row heat map for that 5 x 5 scan (`heatmap`, north at the top; `X` = not found, `20+` = found below 20):

```
 10   6   6   7  11
 13   4   2   1   7
 10   3   1   3  10
 12   5   5   5  12
 19  10  11  15  13
```

The dataset has four views: **Overview**, **Grid points**, **Summary per keyword** and **Competitor leaderboard**. Export to CSV, Excel or JSON, or use the API.

### Pricing (pay per event)

| Event | When it is charged |
|---|---|
| Actor start | once per run |
| `grid-point` | one grid point scanned for one keyword: Google Maps results at that spot with your rank (or "not in the results") and the competitors there |

You are not charged for:

- points where Google Maps shows no businesses for the keyword;
- failed requests;
- the business lookup by name;
- summary, leaderboard and report rows;
- skipped inputs (duplicate or invalid keywords);
- anything beyond your run's maximum charge. The run scans as many whole keyword grids as fit (or a smaller grid) and lists what it didn't scan.

A 5 x 5 grid is 25 grid-point events per keyword; a 7 x 7 circle is 37.

**Apify free plan:** 1 keyword and grids up to 5 x 5 (25 points) per run. Any paid Apify plan unlocks up to 20 keywords and 15 x 15 grids (up to 2,500 points per run).

### Use from AI agents (MCP)

AI assistants such as Claude, ChatGPT and Cursor can run this Actor through [Apify's MCP server](https://docs.apify.com/platform/integrations/mcp) (`https://mcp.apify.com`). The agent can look up the Actor, read its input schema, run it and read the results. Runs use your own Apify account and are charged at the prices on this page.

- **Actor name:** `vncntlabs/google-maps-rank-tracker`
- **Server URL with only this tool:** `https://mcp.apify.com?tools=vncntlabs/google-maps-rank-tracker`. The default `https://mcp.apify.com` also works; the agent can then search Apify Store and call the Actor by name.
- **Claude** (desktop app or claude.ai): Settings > Connectors > Add custom connector, paste the server URL, then sign in to Apify.
- **ChatGPT:** turn on Developer mode, then Settings > Apps & Connectors > Create, paste the server URL and choose OAuth.
- **Cursor:** run `/add-plugin apify` in Cursor chat and connect the Apify MCP server under Cursor Settings > Tools & MCPs, or add the server to `mcp.json`:

```json
{ "mcpServers": { "apify": { "url": "https://mcp.apify.com?tools=vncntlabs/google-maps-rank-tracker" } } }
```

Example prompt: *"Use the Apify Actor vncntlabs/google-maps-rank-tracker to check where the business "Acme Plumbing" in Denver, CO ranks on Google Maps for "emergency plumber" on a 5 x 5 grid."*

To cap the cost of an agent's run, it can pass `maxTotalChargeUsd` in the run options.

### FAQ

**How accurate is a grid point?** Each point is a real Google Maps search centred on that GPS coordinate at the chosen zoom, as Google answered at `scrapedAt`. Real people can see slightly different results depending on device, personalisation and time, so watch trends across scans rather than single points.

**How deep does it look?** Up to about 100 listings per point. If you're further down (or not listed), `rank` is null and `rankLabel` says "Not in top N".

**Does it work outside the US?** Yes, anywhere Google Maps works. Set `language` for local-language results; `country` only helps find a business by name.

**Can I track several locations of my brand?** Use "Any location of the brand" (`matchMode: any_location`) with the brand name or website; each point shows which branch ranked (`matchedListing`).

**Is this the official Google Maps API?** No. This Actor uses DataForSEO's licensed Google Maps data and is not affiliated with or endorsed by Google.

# Actor input Schema

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

Searches your customers type into Google Maps, one per line (up to 20): <code>dentist</code>, <code>emergency plumber</code>, <code>pizza near me</code>. Each keyword is scanned at every grid point.

## `googleMapsUrl` (type: `string`):

Open the business on Google Maps and paste the URL (or a maps.app.goo.gl share link). Gives an exact match and centres the grid on the business.

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

Optional if you gave the URL. The name exactly as shown on Google Maps, e.g. <code>Swish Dental Downtown</code>. Used to find the business (one lookup, included in the run price).

## `businessLocation` (type: `string`):

Optional, helps find the business by name, e.g. <code>Austin, TX</code> or <code>201 W 5th St, Austin</code>.

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

Optional alternative to the URL: <code>ChIJ...</code> place ID or the numeric CID.

## `website` (type: `string`):

Optional, e.g. <code>swishsmiles.com</code>. Helps find the business; in 'any location' mode every listing with this website counts as yours.

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

Only used to look the business up by name. Grid points are GPS coordinates and work anywhere Google Maps works.

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

3 = 3 x 3 (9 points), 5 = 25, 7 = 49, 9 = 81, up to 15 x 15 (225). Odd numbers keep the business in the middle.

## `pointSpacing` (type: `number`):

Spacing between neighbouring grid points, in the unit below. Typical: 0.5-1 mi in cities, 2-5 mi for service-area businesses.

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

Unit for the spacing.

## `gridShape` (type: `string`):

Circle drops the corner points outside the radius, so you pay for fewer points.

## `center` (type: `string`):

Optional, e.g. <code>30.2672, -97.7431</code>. Defaults to the business location.

## `zoom` (type: `integer`):

Google Maps zoom of each search. 13 (default) covers a city district, like a searcher scanning the map; 12 suits wide metro grids; 15+ is a single neighbourhood, so businesses outside that small view won't appear.

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

Google interface language code (en, de, es, fr, pt-BR...).

## `competitorsPerPoint` (type: `integer`):

Top Google Maps listings to include for each point (rank, name, rating, reviews, category, website).

## `matchMode` (type: `string`):

For multi-location brands choose 'any location' to track the best-ranked branch at each point.

## Actor input object example

```json
{
  "keywords": [
    "dentist"
  ],
  "googleMapsUrl": "https://www.google.com/maps/place/Swish+Dental+Downtown/@30.2674035,-97.7451433,17z/data=!4m6!3m5!1s0x8644b5b434046875:0x644e6eb422388b7a!8m2!3d30.2674035!4d-97.7451433",
  "country": "United States",
  "gridSize": 5,
  "pointSpacing": 1,
  "distanceUnit": "km",
  "gridShape": "square",
  "zoom": 13,
  "language": "en",
  "competitorsPerPoint": 3,
  "matchMode": "this_location"
}
```

# Actor output Schema

## `report` (type: `string`):

No description

## `overview` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `competitors` (type: `string`):

No description

## `all` (type: `string`):

No description

## `runSummary` (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 = {
    "keywords": [
        "dentist"
    ],
    "googleMapsUrl": "https://www.google.com/maps/place/Swish+Dental+Downtown/@30.2674035,-97.7451433,17z/data=!4m6!3m5!1s0x8644b5b434046875:0x644e6eb422388b7a!8m2!3d30.2674035!4d-97.7451433"
};

// Run the Actor and wait for it to finish
const run = await client.actor("vncntlabs/google-maps-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 = {
    "keywords": ["dentist"],
    "googleMapsUrl": "https://www.google.com/maps/place/Swish+Dental+Downtown/@30.2674035,-97.7451433,17z/data=!4m6!3m5!1s0x8644b5b434046875:0x644e6eb422388b7a!8m2!3d30.2674035!4d-97.7451433",
}

# Run the Actor and wait for it to finish
run = client.actor("vncntlabs/google-maps-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 '{
  "keywords": [
    "dentist"
  ],
  "googleMapsUrl": "https://www.google.com/maps/place/Swish+Dental+Downtown/@30.2674035,-97.7451433,17z/data=!4m6!3m5!1s0x8644b5b434046875:0x644e6eb422388b7a!8m2!3d30.2674035!4d-97.7451433"
}' |
apify call vncntlabs/google-maps-rank-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vncntlabs/google-maps-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/eAeXUqE9DkTJ18CNF/builds/FUFTGZ8QWaTdiFF9o/openapi.json
