# Google Maps Rank Tracker — Geo-Grid Local SEO (`kaz_kakyo/geo-grid-rank-tracker`) Actor

Google Maps rank tracker for local SEO: check a business's rank at every point of a 3x3 to 13x13 geo-grid, with the top 3 competitors per point, average rank, share of local voice and a rank grid. A pay-per-point local rank tracker and Local Falcon alternative: $3 per 1,000 grid points.

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

## Pricing

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

## Google Maps Geo-Grid Rank Tracker

See where a business ranks on Google Maps across its whole service area, not just at its front door.

Give a business, one or more keywords and a grid centre. The actor searches Google Maps from every point of a 3 x 3 to 13 x 13 grid and returns, for each point:

- **The business's rank** among Google Maps results (1–20, ads excluded), or "not in the top 20"
- **The top 3 businesses** ranking there, with place ID, category, rating and review count
- Optionally the **full top 20** at each point

The run summary (`OUTPUT`) adds, per keyword, the **rank grid** (one rank per grid point, laid out like the map, ready to paste into a report or spreadsheet), average rank, and the share of points where the business is in the Maps top 3.

It is a pay-per-use alternative to Local Falcon-style geo-grid tools for local SEO agencies, multi-location brands and anyone checking how a Google Business Profile performs street by street. Schedule it weekly to track a trend. No accounts, API keys or logins needed.

### How much does it cost?

| Event | Price | When |
|---|---|---|
| `grid-point` | $0.003 | One keyword checked at one grid point, delivered (found or not in the top 20) |

A 5 x 5 grid for one keyword is 25 points, **$0.075**. A 7 x 7 grid for three keywords is 147 points, $0.441. Apify also bills its start event (`apify-actor-start`): $0.00005 per run at the default 256 MB.

You are **not** charged when Google blocks a request, times out or returns a page the actor cannot read. Each such point is retried once through a different proxy and, if it still fails, listed in the free **status** dataset. Set **Maximum cost per run** in Apify to cap spend: the actor stops before starting a point the budget cannot cover.

### Input

| Field | Example | Notes |
|---|---|---|
| `businessName` | `"Toothbar"` | As shown on Google Maps; case and punctuation are ignored |
| `placeId` | `"ChIJO0xN0RG1RIYRIDFDZYJkYjA"` | Optional, recommended: tells apart branches that share a name. Every row returns place IDs |
| `keywords` | `["dentist", "teeth whitening"]` | Up to 10 |
| `centerLat`, `centerLng` | `30.2672`, `-97.7431` | Usually the business's location (the numbers after `@` in its Maps URL) |
| `gridSize` | `5` | 3, 5, 7, 9, 11 or 13 points per side |
| `spacingKm` | `1` | Distance between neighbouring points |
| `zoom` | `14` | Google Maps zoom at each point (11–18) |
| `businessCountry` | `"US"` | Two-letter code; sets Google's region and proxy routing (below) |
| `language` | `"en"` | Google interface language |
| `includeAllResults` | `false` | Add the full top 20 to each row (same price) |

Keywords x grid points is capped at 1,000 per run.

### Output

One row per keyword and grid point (rows run north to south, columns west to east):

```json
{
  "keyword": "plumber",
  "row": 0, "col": 0, "lat": 39.766331, "lng": -105.025346,
  "rank": 1, "found": true, "matchedBy": "placeId",
  "matchedPlaceId": "ChIJkWUJx22Ha4cRVVLq4AJk7Ng",
  "top3": [
    { "rank": 1, "name": "Applewood Plumbing Heating & Electric", "placeId": "ChIJkWUJx22Ha4cRVVLq4AJk7Ng", "category": "Plumber", "rating": 4.9, "reviews": 17883, "isTarget": true },
    { "rank": 2, "name": "Golden West Plumbing, Heating, Air Conditioning, and Electrical", "...": "..." },
    { "rank": 3, "name": "Good Plumbing Service Company", "...": "..." }
  ],
  "resultsCount": 20,
  "zoom": 14,
  "checkedAt": "2026-10-17T12:56:00.000Z"
}
```

`OUTPUT` (key-value store) per keyword:

```json
{ "keyword": "plumber", "pointsDelivered": 25, "pointsFound": 24, "pointsTop3": 6, "top3Share": 0.24,
  "averageRankWhereFound": 9, "averageRankAllPoints": 9.48,
  "grid": ["1 1 4 12 18", "1 1 6 14 17", "1 1 8 10 16", "4 5 10 15 16", "11 12 15 20+ 18"] }
```

### How ranks are measured

Each point is a Google Maps search with the map centred on that point at the chosen zoom, the same request the Maps website makes. Rank is the position in the result list, ads excluded. In testing on 2026-10-17 the results matched a manual Google Maps check exactly (top 8–10, in order) for a dentist in Austin, a plumber in Denver and a cafe in Manchester, at grid centres and corners.

Google also re-orders results by the searcher's own IP location. A proxy inside the business's country would make positions 2–10 depend on wherever that proxy happens to be, and differ from run to run. So the actor sends requests from **outside** the business's country: residential IPs in Germany (then the UK) for US businesses, US datacenter IPs (then US residential) for everyone else. The ranks are therefore what Maps shows for that map view, without a local searcher's personal location. A phone physically in the area can see a slightly different order below the top 3.

### Limits

- Google Maps HTTP only, no browser. If Google changes this response format, points become free `unrecognised_response` status rows rather than wrong data.
- The top 20 only: "not in the top 20" is reported as `rank: null` (and `20+` in the grid).
- Name matching is exact after normalising case, accents and punctuation; use `placeId` for chains or near-duplicate names. With `placeId` set, a different location with the same name is never counted.
- A result page with an unreadable entry or no results at all is a free `unrecognised_response` row, never a charged "not in the top 20".
- The whole grid must lie between latitudes -85 and 85; grids crossing the 180° meridian are supported.
- This actor does not collect personal data. It returns public business listings only.

### Usage telemetry

Each run sends one anonymous usage record (a hashed caller ID, whether the run came from the API or the Console, and charged-event counts) to the developer's private dataset. No inputs, keywords or results are recorded.

# Actor input Schema

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

The business to track, exactly as Google Maps shows it (case and punctuation are ignored). Optional when you give a place ID.

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

Optional but recommended: the business's Google place ID (starts with ChIJ). It tells apart branches that share a name. Every result row carries place IDs, so you can copy it from a first run.

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

What customers type into Google Maps, for example "dentist" or "emergency plumber". Each keyword is checked at every grid point. Up to 10.

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

Usually the business's own location. Copy it from the Google Maps URL (the first number after @).

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

Usually the business's own location (the second number after @ in a Google Maps URL).

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

Points per side: 3, 5, 7, 9, 11 or 13. A 5 x 5 grid is 25 points per keyword. Keywords x points is capped at 1,000 per run.

## `spacingKm` (type: `number`):

Kilometres between neighbouring grid points (1 km = 0.62 miles). A 5 x 5 grid at 1 km covers 4 x 4 km.

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

Google Maps zoom at each point. 14 shows roughly a 25 km-wide desktop map; a lower zoom widens the area Google ranks over.

## `businessCountry` (type: `string`):

Two-letter country code of the business (US, GB, DE, ...). It sets Google's region and picks a proxy outside that country, so results are not skewed by the proxy's own location.

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

Google interface language code, for example en or de.

## `includeAllResults` (type: `boolean`):

Adds the full top 20 (name, place ID, category, rating, reviews, address, coordinates) to every row. Same price.

## Actor input object example

```json
{
  "businessName": "Toothbar",
  "placeId": "ChIJO0xN0RG1RIYRIDFDZYJkYjA",
  "keywords": [
    "dentist"
  ],
  "centerLat": 30.2672,
  "centerLng": -97.7431,
  "gridSize": 3,
  "spacingKm": 1,
  "zoom": 14,
  "businessCountry": "US",
  "language": "en",
  "includeAllResults": false
}
```

# Actor output Schema

## `points` (type: `string`):

One row per keyword and grid point: the business's Google Maps rank there (null = not in the top 20) and the top 3 results.

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

Per keyword: the rank grid (north to south), average rank, share of points in the top 3, and every outcome count.

# 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": "Toothbar",
    "placeId": "ChIJO0xN0RG1RIYRIDFDZYJkYjA",
    "keywords": [
        "dentist"
    ],
    "centerLat": 30.2672,
    "centerLng": -97.7431,
    "gridSize": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("kaz_kakyo/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 = {
    "businessName": "Toothbar",
    "placeId": "ChIJO0xN0RG1RIYRIDFDZYJkYjA",
    "keywords": ["dentist"],
    "centerLat": 30.2672,
    "centerLng": -97.7431,
    "gridSize": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("kaz_kakyo/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 '{
  "businessName": "Toothbar",
  "placeId": "ChIJO0xN0RG1RIYRIDFDZYJkYjA",
  "keywords": [
    "dentist"
  ],
  "centerLat": 30.2672,
  "centerLng": -97.7431,
  "gridSize": 3
}' |
apify call kaz_kakyo/geo-grid-rank-tracker --silent --output-dataset

```

## MCP server setup

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