Google Maps Rank Tracker | Geo-Grid Heatmap & Competitors
Pricing
from $5.00 / 1,000 grid point scanneds
Google Maps Rank Tracker | Geo-Grid Heatmap & Competitors
Track where a business ranks on Google Maps at every point of a geo-grid (3×3 to 15×15). Competitors are ranked on the same scans at no extra cost. Average rank, share of local voice, top-3 share, top-10 competitor leaderboard, heatmap PNG, HTML report and change since your last scan.
Pricing
from $5.00 / 1,000 grid point scanneds
Rating
0.0
(0)
Developer
Corvuslab
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
What does Google Maps Rank Tracker do?
Google Maps Rank Tracker shows where a business ranks on Google Maps at every point of a grid around it, the way customers across town see it. One run returns the rank at each point, average rank, share of local voice, top-3 and top-10 share, a top-10 competitor leaderboard per keyword, a heatmap image, a shareable HTML report and the change since your last scan, in 66 structured fields. It's no-code, and the output goes to JSON, CSV, Excel, the API or an AI agent.
Give it a business (a name plus a city, a Google Maps link, a place ID or a CID), keywords and a grid size. You get one summary row per business × keyword, with the rank at every point inside it. Schedule it weekly and every summary shows what moved: a pay-per-scan alternative to a monthly local-rank-tracking subscription.
New to Apify? Sign up free and use the included monthly platform credit to try this Actor. No credit card needed.
Why this rank tracker
- 🆚 Competitors at no extra cost: up to 19 more businesses ranked on the same scans.
- 📈 See what changed: the previous rank per point and the movement in every metric.
- 🗺️ Client-ready visuals: a heatmap PNG per keyword and a shareable HTML report.
- 🔔 Drop alerts: Telegram, Slack, Discord, WhatsApp or a webhook.
- ⚡ Zero setup: no login, no proxy setup, and an empty input runs a full example scan.
What data can you extract from a Google Maps geo-grid scan?
Every record carries up to 66 fields, grouped like this:
| Group | What you get |
|---|---|
| Business & scan | Business name, place ID, CID, address, Google category, Maps link, rating, review count, keyword, match mode, scan time |
| Rank at every point | Row, column, latitude/longitude, distance from the grid centre, rank, found / in top 3, the name that matched, results checked, a "check it on Google Maps" link, and the top 3 businesses at that point |
| Headline metrics | Average rank (ARP), average rank across all points (ATRP), share of local voice (SoLV), % of points in the top 3 and top 10, best and worst rank, rank at the business, points ranked, the full rank grid as a 2D array |
| Change since last scan | Previous rank, rank change and movement (up, down, same, new, lost) per point, the previous and changed ARP, SoLV and top-3 share, and the counts of changed, improved and declined points |
| Competitor intelligence | A top-10 leaderboard per keyword with each rival's name, place ID, CID, category, rating, reviews, points found, average rank, SoLV, top-3 share and whether it's one you track |
| Grid & deliverables | Grid size and shape, radius in km and miles, point spacing, zoom, deepest rank checked, centre coordinates, failed points, a completeness flag, and links to the heatmap PNG and the HTML report |
Key features
- 🆚 Competitors on the same grid, free. Add up to 19 competitors or your client's other locations. One set of searches ranks all 20 businesses, so the scan count and your bill stay the same as for one.
- 📈 Change since your last scan. Each point carries
previousRank,rankChangeand a plainmovement(up,down,same,new,lost), so a business that drops off the list shows aslost, not a blank. Each summary carriesarpChange,solvChange,pctTop3Changeand how many points improved or declined. - 🏆 Top-10 competitor leaderboard. For every keyword, the 10 businesses that really own the grid, ranked by share of local voice, with average rank, top-3 share, rating and reviews. Competitors you added stay in the list, flagged
tracked. - 🗺️ Heatmap PNG + shareable HTML report. A colour-coded grid per business × keyword (green = top 3, amber = 4–10, orange = 11+, red = not ranked) and one self-contained report per run.
- 🎯 Exact match, brand mode or one grid per business. A Google Maps link, place ID or CID is always an exact match. Brand mode counts any branch of a chain, and one-grid-per-business lets an agency scan several clients in one run.
- 📐 Bigger, deeper grids. 3 × 3 to 15 × 15 points, up to 25 keywords, ranks checked down to position 60. A circle grid drops the corners outside the radius and scans 34–48 % fewer points.
- 📍 Ranks as a local customer sees them, wherever the scan runs from, so re-scans compare like with like.
- ⚡ Runs out of the box. No proxy setup and no Google account. Keywords are optional: without them it scans the business's own Google category.
🚀 How to scrape Google Maps rankings on a geo-grid
- Enter the business: its name plus a city, or paste its Google Maps link, place ID or CID.
- Add your keywords (what customers type into Google Maps) and, optionally, competitors to rank on the same grid.
- Pick a grid size and radius (5 × 5 over 2 miles is the default and suits a typical town).
- Click Start. The heatmap, the HTML report and the rows appear in the Output tab.
- Schedule it weekly to track progress, and add a notification channel for drop alerts.
Quick-start example input
{"businessName": "Loyal Coffee","location": "Colorado Springs, CO","keywords": ["coffee shop near me", "latte"],"businesses": ["Frankly Coffee", "Bittersweet Coffee and Venue"],"gridSize": 5,"radiusMiles": 2}
This scans 2 keywords over a 5 × 5 grid (50 scans) and ranks Loyal Coffee and both competitors at all 25 points: 6 summary rows and 6 heatmaps, for the same 50 scans as Loyal Coffee alone.
Input
Configure it in the visual editor with no code, or pass JSON through the API. 33 inputs are available, grouped as in the editor.
📍 What to track
| Field | Type | What it does |
|---|---|---|
businessName | string | The business to track: its Google Maps name, Maps link, place ID (ChIJ…) or CID. The grid is centred on it. |
keywords | array | What customers type into Google Maps, up to 25, each scanned across the whole grid. Empty = the business's Google category. |
location | string | City, state or neighbourhood, used to find the business by name. Not needed with a link, place ID or CID. |
businesses | array | Up to 19 more businesses (competitors, or your client's other listings) ranked on the same grid at no extra cost. |
matchAllBranches | boolean | Brand mode: a point counts the first listing whose name matches (e.g. any "Starbucks"). |
🗺️ Grid
| Field | Type | What it does |
|---|---|---|
gridSize | integer | Points per side, 3–15 (default 5). 5 means 25 points, 15 means 225. |
radiusMiles | number | How far the grid reaches from the centre in each direction, 0.25–25 miles (default 2). |
radiusKm | number | The same radius in kilometres (0.4–40). When set, it replaces the radius in miles. |
gridShape | enum | square (every point) or circle (corners dropped, fewer scans). |
separateGrids | boolean | Give each business its own grid centred on it. |
centerLat / centerLng | number | Centre the grid somewhere other than the business, such as a service-area town. |
⚙️ Search settings
| Field | Type | What it does |
|---|---|---|
zoom | integer | Google Maps zoom each point is searched at, 8–18 (default 13). Higher zoom is a closer view, so ranks change faster from point to point. |
maxRank | integer | Deepest position checked, 1–60 (default 20). Anything deeper counts as not ranked. |
placeId | string | Pins the exact listing when a business name has look-alikes nearby. |
language | string | Search language as a 2-letter code (default en). |
countryCode | string | Search country (e.g. us, gb, de). Empty = the business's own country. |
📈 Tracking over time
| Field | Type | What it does |
|---|---|---|
compareWithPrevious | boolean | Adds the previous scan of the same business, keyword and grid to every row (on by default). |
historyKey | string | Keeps a separate scan history under this name, e.g. one per client. |
includePointRows | boolean | Adds one row per grid point (rank, top 3 there, previous rank, movement, a Google Maps link). Off by default: the summary's rankGrid already holds every point's rank. |
generateHeatmaps | boolean | Saves a heatmap PNG per business × keyword and one HTML report per run (on by default). |
🔔 Notifications
| Field | Type | What it does |
|---|---|---|
notifyOn | enum | always, changes (only when ranks changed) or drops (only when rankings dropped). |
dropThreshold | number | With drops: how many positions the average rank must worsen to alert (default 1). |
| Channel fields | string / object | Telegram, Slack, Discord, WhatsApp and webhook settings, listed in the alerts section. Tokens and URLs are secret inputs: encrypted, masked and never logged. |
🛠️ Advanced
| Field | Type | What it does |
|---|---|---|
proxyConfiguration | object | Leave it empty: the actor picks how to search so every point reflects a local customer. Set it only if a run reports points that could not be read. |
More example inputs
An exact match by place ID with a circle grid and a deep rank check:
{"businessName": "ChIJyaUh0yFFE4cR9kbrTpDyb3I","keywords": ["coffee shop near me", "espresso", "cafe with wifi"],"gridSize": 7,"gridShape": "circle","radiusMiles": 3,"maxRank": 40}
A weekly agency report, with one grid per client, its own history and alerts only on drops:
{"businessName": "Loyal Coffee","businesses": ["https://www.google.com/maps?cid=17399631780147321288"],"location": "Colorado Springs, CO","keywords": ["coffee shop near me"],"separateGrids": true,"historyKey": "client-loyal-coffee","notifyOn": "drops","dropThreshold": 1.5,"slackWebhookUrl": "https://hooks.slack.com/services/..."}
Output
By default the dataset holds one summary row per business × keyword, with the metrics, competitors and change since the last scan. Turn on includePointRows to add a point row per grid point; recordType tells them apart. A real summary row from a 3 × 3 rescan, trimmed:
{"recordType": "summary","scannedAt": "2026-10-19T08:48:04+00:00","keyword": "latte","businessName": "Loyal Coffee","businessPlaceId": "ChIJyaUh0yFFE4cR9kbrTpDyb3I","businessCid": "8246076144351921910","businessAddress": "408 S Nevada Ave, Colorado Springs, CO 80903","businessCategory": "Coffee shop","matchMode": "exact","targetRating": 4.7,"targetReviews": 1850,"gridSize": 3,"radiusMiles": 2.0,"zoom": 13,"maxRank": 20,"total": 9,"pointsFound": 9,"arp": 5.0,"atrp": 5.0,"solv": 80.0,"pctTop3": 0.0,"pctTop10": 100.0,"rankAtCenter": 5,"rankGrid": [[4, 5, 7], [5, 5, 5], [4, 5, 5]],"complete": true,"previousArp": 4.44,"arpChange": -0.56,"solvChange": -2.8,"pctTop3Change": -11.1,"improvedPoints": 2,"declinedPoints": 5,"topCompetitors": [{ "name": "Misty Mountain Collective", "placeId": "ChIJfzeYJOlFE4cRaYJqyMgHtSQ", "category": "Coffee shop","rating": 4.9, "reviews": 426, "pointsFound": 9, "arp": 1.44, "solv": 97.8, "pctTop3": 100.0, "tracked": false }]}
And a point row from the same scan (the north-west corner, with includePointRows on):
{"recordType": "point","keyword": "latte","businessName": "Loyal Coffee","row": 0,"col": 0,"lat": 38.856884,"lng": -104.859523,"distanceMiles": 2.83,"rank": 4,"previousRank": 4,"rankChange": 0,"movement": "same","top3": [{ "rank": 1, "name": "Bittersweet Coffee and Venue", "rating": 4.8, "reviews": 63 },{ "rank": 2, "name": "Misty Mountain Collective", "rating": 4.9, "reviews": 426 },{ "rank": 3, "name": "Frankly Coffee", "rating": 4.8, "reviews": 480 }],"mapsSearchUrl": "https://www.google.com/maps/search/latte/@38.856884,-104.859523,13z?hl=en&gl=us"}
Every summary row also links its heatmapUrl (the PNG) and reportUrl (the run's HTML report).
Data fields
The field groups are in the table above. Reading them: row 0 is the north edge and col 0 the west edge; a null rank means the business wasn't in the top maxRank; in every change field (rankChange, arpChange, solvChange, pctTop3Change) positive means better. rankChange needs a rank in both scans; movement covers every case, including new (entered the top maxRank) and lost (fell out of it). Change fields are null on a first scan. Missing values are null.
The Output tab has three views: Overview, Summary per keyword (metrics, changes, competitors, heatmap and report links) and Rank at every point (rank, movement and a Google Maps link per point). With point rows on, every view lists both kinds of row, so filter on recordType when you export only one kind.
♻️ How to monitor Google Maps rankings week by week
Schedule it, and Compare with the previous scan (on by default) adds the last scan of the same business, keyword and grid to every row:
| First scan (baseline) | Every scan after | |
|---|---|---|
| Rank at every point | ✅ | ✅ |
| Previous rank + change per point and per metric | — | ✅ |
| Alert only when rankings dropped | — | ✅ |
You're billed per scan (keyword × grid point), not per business, so every competitor on the grid is free:
| Businesses on one grid | Scans if run separately | Scans here | You save |
|---|---|---|---|
| 2 (you + 1 competitor) | 2× | 1× | 50 % |
| 5 (you + 4 competitors) | 5× | 1× | 80 % |
| 20 (the maximum) | 20× | 1× | 95 % |
Setting up scheduled tracking
- Configure your business, keywords, competitors and grid. Optionally set a History name per client.
- Run it once to seed the baseline.
- In the Schedules tab, add a recurring run (weekly is the usual cadence for local SEO).
- Add a notification channel and set Send a notification to Only when rankings dropped.
Keep the grid settings unchanged between runs: a different size, shape, radius, centre, zoom, depth, language or country starts a fresh history.
🔔 How to set up Google Maps ranking drop alerts
Get a message after every scan, only when ranks moved, or only when they dropped. Fill in just the channels you want:
| Channel | What to configure |
|---|---|
| ✈️ Telegram | telegramToken (from @BotFather) + telegramChatId |
| 💬 Slack | slackWebhookUrl (an incoming webhook URL) |
| 🎮 Discord | discordWebhookUrl (a channel webhook URL) |
whatsappAccessToken + whatsappPhoneNumberId + whatsappTo (recipients must have messaged your business number in the last 24 h) | |
| 🪝 Webhook | webhookUrl (+ optional webhookHeaders) receives every summary row plus the report link as JSON, ideal for n8n, Make or Zapier |
A drop is the average rank worsening by dropThreshold positions or more, the top-3 share falling by 10 points, or the business vanishing from the grid. A broken channel can't stop the scan or the other channels.
💡 What can you do with Google Maps rank data?
Weekly local SEO reports for clients
Schedule each client's grid weekly and send the heatmap and HTML report. The change fields show exactly which neighbourhoods improved after your work.
Prove the impact of Google Business Profile work
Scan before and after you change categories, add photos or run a review drive. The point-by-point movement shows where the work paid off.
See who owns the map in your area
The leaderboard shows which competitors dominate each keyword, with their rating and review counts. Check which of them also buy their way in with our Google Ads Scraper.
Audit multi-location brands
Brand mode counts any branch, and one-grid-per-business scans every location in one run, so the stores invisible in their own neighbourhood stand out.
Win new agency clients
Scan a prospect and their top rivals, then open the pitch with a heatmap of where they lose customers. To find prospects in the first place, our Google Maps No-Website Leads Scraper lists local businesses with no website of their own.
Feed dashboards and AI agents
The default output is one compact row per business × keyword, for Looker Studio, Google Sheets, an LLM or an MCP tool.
💰 How much does it cost to track Google Maps rankings?
This Actor uses Apify's pay-per-event model and bills one event per grid point scanned (one keyword at one point), so a 5 × 5 grid is 25 events per keyword. The Pricing tab shows the live numbers. What keeps the bill down:
- Competitors are free, and circle grids skip the corners (both covered above).
- Failed points are never charged: they're retried, then reported as failed.
- Spending limits are respected. The run won't start a keyword's grid your limit can't cover. If even the first doesn't fit, it scans the points your limit covers and warns you.
🔌 Integrations & export
Export to JSON, CSV, Excel or an HTML table, or pull from the REST API and the JavaScript / Python clients. Run it on a schedule, connect it to Google Sheets, Looker Studio, Slack, Make, Zapier and n8n, or use it as an MCP tool for AI agents. The heatmap PNGs and the HTML report sit in the run's key-value store, ready to link or download.
More Google data from us: Google Maps No-Website Leads Scraper for local prospect lists, and Google Ads Scraper for any advertiser's ads from the Ads Transparency Center.
🔗 Using the API
Run this Actor from your own code. Example with the Apify Python client:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_API_TOKEN>")run_input = {"businessName": "Loyal Coffee","location": "Colorado Springs, CO","keywords": ["coffee shop near me"],"businesses": ["Frankly Coffee"],"gridSize": 5,}run = client.actor("corvuslab/google-maps-geo-grid-rank-tracker").call(run_input=run_input)for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["recordType"] == "summary":print(item["businessName"], item["keyword"], item["arp"], item["solv"], item["heatmapUrl"])
It also works with the JavaScript/TypeScript client, the Apify CLI and the REST API.
❓ FAQ
How are average rank, ATRP and share of local voice calculated? arp is the average rank over the points where the business ranked. atrp averages over all points, counting a miss as maxRank + 1, so it punishes gaps. solv (0–100) is the mean over all points of (maxRank + 1 − rank) / maxRank × 100: rank 1 earns a full share, a miss earns 0. pctTop3 and pctTop10 are the share of points in the top 3 or top 10.
Why does a rank differ from what I see on my phone? Your phone adds your own location and search history. Each point is a signed-out search by a customer standing at that point; mapsSearchUrl opens the same search on Google Maps.
What do null and 0 mean in rankGrid? null means the business wasn't in the top maxRank results there (raise maxRank, up to 60, to look deeper), or the point couldn't be read (counted in failedPoints, and marked failed in point rows). A 0 appears only in a circle grid, for a corner outside the radius.
What is rankAtCenter for competitors or a custom centre? It's the rank at the grid point nearest that business's own address, so each tracked business gets its own "rank at the business". It's null when the business lies outside the grid; in brand mode it's the rank at the grid centre. distanceKm / distanceMiles are measured from the grid centre.
The business name matches several listings. What happens? The run takes the first matching listing in your city and warns in the log. For a guaranteed match, paste the Google Maps link, place ID or CID, or add the placeId hint. Tracking a chain? Turn on Count any branch of a brand instead.
What if some points can't be read? They're retried. Any that still fail are counted in failedPoints, not charged, and complete is set to false. An incomplete grid isn't saved as history, so it never skews your next comparison.
How long do the heatmap and report links work? As long as Apify keeps the run's storage (your plan's data retention). Download the PNGs and the report, or export the dataset, to keep them longer.
Which countries and languages work? Any country where Google Maps works. By default the search uses the business's own country and English. Set countryCode and language (e.g. de / de) to match how your customers search.
⚖️ Is it legal to scrape Google Maps rankings?
This Actor reads only publicly available Google Maps search results, the same ones anyone sees in a browser, and it collects business listings rather than personal profiles. You're responsible for how you use the data and for complying with Google's terms and applicable law, including the GDPR where it applies. This Actor is not affiliated with, endorsed by or sponsored by Google.
Keywords: google maps rank tracker · geo-grid rank tracker · local rank tracker · google maps ranking heatmap · local SEO rank tracking · share of local voice · average rank ARP · map pack rank tracker · local pack tracker · google business profile ranking · competitor rank tracking · local SEO agency reporting · grid rank checker · google maps scraper · track google maps rankings weekly · export to CSV · MCP tool for AI agents