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

Check where a business ranks on Google Maps from every point of a grid around it (3×3 to 15×15). Rank per point, top competitors, average rank and top-3 share, plus a shareable heatmap image and map report. No proxy costs. $0.49 per 7×7 grid.

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

## Pricing

from $7.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.
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?

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 – Geo-Grid Local SEO Heatmap

#### What does Google Maps Rank Tracker do?

It shows **where a business ranks on Google Maps from every part of town**. You give a keyword (e.g. "dentist"), the business and a center point. The actor lays a grid of points around it (3×3 up to 15×15) and, at each point, runs the Google Maps search **as if a customer standing there searched on their phone**. You get the rank at every point, the competitors that beat you there, a summary (average rank, share of top-3), a **ready-to-share grid image** and an **interactive map report**. It's the same kind of report Local Falcon or BrightLocal sell on monthly plans, pay-as-you-go.

#### Why use it?

- **Real Google Maps rankings at exact GPS points.** Each point simulates a searcher at that spot, the way geo-grid rank trackers work. Results don't depend on where our servers are: the same grid run twice gives the same ranks.
- **Report-ready output.** A PNG heatmap of the grid and an HTML map (OpenStreetMap, click any dot to see the top 3 there) with public links you can send to a client.
- **Competitor intelligence.** Top competitors at every point, plus the 10 strongest competitors across the whole grid (average rank, top-3 points, rating, reviews).
- **Local Falcon-style metrics.** Average rank where found (ARP), average rank over all points (ATRP), share of top-3 points (SoLV), found-in count.
- **Fast and reliable.** No browser, no proxy surcharge. A 7×7 grid takes about 15 seconds. 701 of 701 test points succeeded across the US, Germany and Australia.
- **Flexible.** Several keywords per run, grids from 3×3 to 15×15, spacing in km or miles, top 20 up to top 100, any language/country.
- **Fair pricing.** $0.01 per grid point: **$0.49 for a 7×7 grid**. Failed points are never charged.

#### What data do you get?

**One row per grid point** (`type: "point"`):

| Field | Example |
|---|---|
| `keyword` | dentist |
| `gridRow` / `gridCol` | 4 / 4 (row 1 = north) |
| `lat` / `lng` | 40.676498 / -73.983152 |
| `rank` / `rankLabel` | 3 / "3" (or null / "20+" when not in the top 20) |
| `inTop3` | true |
| `topCompetitors` | \[{"rank": 1, "name": "Park Slope Dental Arts", "placeId": "ChIJ...", "rating": 4.9, "reviewCount": 512, "category": "Dentist", "address": "..."}, ...] |
| `mapsUrl` | link to the same search on Google Maps |

**One summary row per keyword** (`type: "summary"`, also in the `SUMMARY` record):

| Field | Example |
|---|---|
| `averageRank` | 8.57 (where found) |
| `averageRankAll` | 9.84 (not found counted as 21) |
| `top3Percent` | 18.4 (% of points in the top 3) |
| `foundIn` / `pointsChecked` | 44 / 49 |
| `bestRank` / `rankAtCenter` | 1 / 1 |
| `gridText` | \["17 13 12 14 7 12 20+", "9 5 13 3 8 19 20+", ...] |
| `topCompetitors` | the 10 strongest competitors on the grid |
| `imageUrl` | public link to the PNG grid image |
| `reportUrl` | public link to the HTML map report |

#### How to use it

1. Enter one or more **keywords** your customers search for.
2. Enter the **business name** and, ideally, its **Google Maps link** (or place ID) for exact matching.
3. Enter the **grid center** (usually the business's address), or leave it empty to center on the business.
4. Pick the **grid size** and the **distance between points**. Click Start.
5. Open **Map report** in the Output tab, or download the rows as JSON, CSV or Excel.

Example input:

```json
{
  "keywords": ["pizza near me", "pizza delivery"],
  "businessName": "Lou Malnati's Pizzeria",
  "centerAddress": "131 W Jefferson Ave, Naperville, IL",
  "gridSize": "7",
  "spacing": 1,
  "spacingUnit": "mi"
}
```

#### How much does it cost?

**$0.01 per grid point checked.** No start fee, no monthly rent, no proxy costs.

| Grid | Points | Price |
|---|---|---|
| 3×3 | 9 | $0.09 |
| 5×5 | 25 | $0.25 |
| 7×7 | 49 | $0.49 |
| 9×9 | 81 | $0.81 |
| 11×11 | 121 | $1.21 |
| 13×13 | 169 | $1.69 |
| 15×15 | 225 | $2.25 |

Each keyword is its own grid. Points that fail are not charged, and summary rows, images and reports are free. Apify's free plan ($5/month credit) covers about ten 7×7 grids a month. If you set a maximum cost per run, the actor only starts grids it can finish within it.

#### Run it from your code

Get your API token in Apify Console → Settings → API & Integrations.

**Python** (`pip install apify-client`)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("ecclesiasteslabs/google-maps-rank-tracker").call(run_input={
    "keywords": ["dentist"], "businessName": "Park Slope Dentistry",
    "centerAddress": "586 President St, Brooklyn, NY", "gridSize": "7", "spacing": 1,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["type"] == "summary":
        print(item["keyword"], item["averageRank"], item["top3Percent"], item["reportUrl"])
```

**JavaScript / Node.js** (`npm install apify-client`)

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('ecclesiasteslabs/google-maps-rank-tracker').call({
    keywords: ['dentist'], businessName: 'Park Slope Dentistry',
    centerAddress: '586 President St, Brooklyn, NY', gridSize: '7', spacing: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.type === 'summary'));
```

**HTTP (one call, returns the rows)**

```bash
curl -X POST "https://api.apify.com/v2/acts/ecclesiasteslabs~google-maps-rank-tracker/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["dentist"], "businessName": "Park Slope Dentistry", "centerAddress": "586 President St, Brooklyn, NY", "gridSize": "5"}'
```

#### Integrations

- **Weekly client reports:** create a Task per client in Apify Console and add a Schedule (e.g. every Monday). Send the `reportUrl` / `imageUrl` to Slack or email with Make, Zapier or n8n, or append the summary rows to Google Sheets to chart progress over time.
- **AI agents (MCP):** connect Claude, ChatGPT, Cursor or any MCP client to [Apify's MCP server](https://mcp.apify.com) and let the agent run local rank audits.

#### Use cases

- **Local SEO agencies:** weekly geo-grid reports for every client and keyword, at a fraction of a monthly Local Falcon seat.
- **Business owners:** see where you win and where competitors take the map pack, before and after optimizing your Google Business Profile.
- **Sales prospecting:** run a 5×5 on a prospect and show them the grey dots.
- **Multi-location brands:** check each branch on its own grid (paste each branch's Maps link).
- **Competitor mapping:** leave the business empty to map who ranks where for a keyword.

#### Tips

- **Paste the Google Maps link** of the business (share links like maps.app.goo.gl work too). It makes matching exact, which matters for chains with many branches. With only a name, we pick the branch closest to the grid center.
- **Spacing:** 0.5–1 km (0.3–0.6 mi) in dense cities, 1–2 mi in suburbs, 3–5 mi for service-area businesses (plumbers, roofers).
- Rankings are hyper-local: a business usually ranks well only near its own location. Lots of "20+" dots on a big grid is normal, and exactly what the report is for.
- "How deep to look" (top 40/60/100) costs the same. Use it to track businesses that aren't in the top 20 yet.
- Keywords in the local language work best (e.g. "Zahnarzt" in Germany). Set `language` to match.

#### FAQ

**Is it the same as what I see on my phone?** Each point reproduces the Google Maps search for a person at that location. Google personalises results for signed-in users (search history, past visits), so an individual's phone can differ slightly. That's true for every rank tracker.

**What does "20+" mean?** The business isn't in the first 20 results at that point (or 40+/60+/100+ if you look deeper).

**Is it legal?** The actor reads public Google Maps search results, the same ones anyone sees. It doesn't log in and collects no personal data beyond business listings. Check the rules that apply to your use case.

**Something broke?** Open an issue on the Issues tab. We fix problems as fast as we can.

# Changelog

This Actor's version history is a separate document: https://apify.com/ecclesiasteslabs/google-maps-rank-tracker/changelog.md

# Actor input Schema

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

What customers type into Google Maps, e.g. "dentist", "emergency plumber", "pizza near me". Each keyword is checked on its own grid (and priced as its own grid).

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

The business to track, as it appears on Google Maps, e.g. "Park Slope Dentistry". The actor finds it near the grid center and then tracks it by its Google id. Leave empty (and the link below too) to only map the top results per point.

## `placeUrlOrId` (type: `string`):

Optional but recommended: the business's Google Maps link (also short maps.app.goo.gl links), its place ID (ChIJ...) or CID number. This makes matching exact, even for chains with many branches. If you leave the center empty, the location in the link is used as the center.

## `centerAddress` (type: `string`):

Address or place the grid is centered on, e.g. "586 President St, Brooklyn, NY". Usually the business's own address. Leave empty to center on the business itself (found from the Maps link or the name).

## `centerLat` (type: `number`):

Instead of an address, give exact coordinates, e.g. 40.67644. Overrides the address.

## `centerLng` (type: `number`):

e.g. -73.983251

## `gridSize` (type: `string`):

Number of points per side. 7 × 7 = 49 points is the usual weekly report; 3 × 3 is a cheap quick check; 13 × 13 or 15 × 15 covers a whole city in detail. You pay per point checked.

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

How far apart neighbouring points are. Typical: 0.5–1 km (0.3–0.6 mi) in a city, 2–5 km in suburbs or for service-area businesses. The grid's total width = (grid size − 1) × distance.

## `spacingUnit` (type: `string`):

Kilometres or miles.

## `maxRank` (type: `string`):

Check the top 20 results per point (like Local Falcon) or go deeper. If the business isn't found, the point shows "20+" (or 40+ etc.). Same price either way.

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

How many of the businesses ranking at each point to include in each point's row (the ones above you first). The summary row also ranks the top 10 competitors across the whole grid.

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

Two-letter language code for Google Maps, e.g. en, es, de, fr. It changes the language of names and categories, and can slightly change results.

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

Leave empty to detect it from the grid center. Or set a two-letter code such as us, gb, ca, au, de.

## `searchMode` (type: `string`):

'Searcher at each point' (default) simulates someone standing at the point and searching on their phone – the standard for geo-grid rank tracking, and the results don't depend on where our servers are. 'Map view only' centers the map on the point without a searcher location; results then spread wider and vary more.

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

Google Maps zoom level of the simulated search (14 ≈ a neighbourhood). It matters little in the default mode; in 'Map view only' mode lower numbers show results from a wider area.

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

Not needed: the actor works without a proxy and automatically retries through Apify Proxy if a request fails. Set this only if you want every request to go through a specific proxy.

## Actor input object example

```json
{
  "keywords": [
    "dentist"
  ],
  "businessName": "Park Slope Dentistry",
  "centerAddress": "586 President St, Brooklyn, NY 11215",
  "gridSize": "7",
  "spacing": 1,
  "spacingUnit": "km",
  "maxRank": "20",
  "competitorsPerPoint": 3,
  "language": "en",
  "searchMode": "searcherAtPoint",
  "zoom": 14,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `points` (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 = {
    "keywords": [
        "dentist"
    ],
    "businessName": "Park Slope Dentistry",
    "centerAddress": "586 President St, Brooklyn, NY 11215"
};

// Run the Actor and wait for it to finish
const run = await client.actor("ecclesiasteslabs/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"],
    "businessName": "Park Slope Dentistry",
    "centerAddress": "586 President St, Brooklyn, NY 11215",
}

# Run the Actor and wait for it to finish
run = client.actor("ecclesiasteslabs/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"
  ],
  "businessName": "Park Slope Dentistry",
  "centerAddress": "586 President St, Brooklyn, NY 11215"
}' |
apify call ecclesiasteslabs/google-maps-rank-tracker --silent --output-dataset

```

## MCP server setup

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