Ask a dentist where they rank on Google Maps for "dentist" and they will search from their own chair and say "first". Two kilometres away, a customer searching the same word sees a different list, and the clinic may be eighth, or missing. Google Maps ranks local businesses partly by where the searcher's map is, so a single rank number is a guess. The useful answer is a grid: the same search run from many points around the business, with the rank at each one.
This post builds that grid in Python, reads a few summary scores from it and sets up a weekly run that shows which points moved.
Disclosure: I'm Aurelix, and I built Google Maps Rank Tracker, the tool used here. It's a paid tool (prices below).
What each row holds
One row per keyword per grid point:
-
grid_row,grid_col(row 1 is the north edge),latitude,longitude,distance_kmfrom the centre. -
rank(1-20, or null) andrank_label("4" or "not in top 20"). -
top_3: the first three businesses at that point withratingandreview_count, andis_your_business. -
maps_url: the same search at that point on Google Maps, to check it yourself.
And one summary row per keyword: average_rank, average_rank_with_misses (points outside the top 20 count as 21), best_rank, share_top_3, share_top_10, share_top_20, and top_businesses, the five businesses most often in the top 3 across the grid.
A 5x5 grid in a few lines
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("aurelix/google-maps-rank-tracker").call(run_input={
"business": "Bond Street Dental",
"keywords": ["dentist"],
"gridSize": "5x5",
"spacing": 1,
"unit": "km",
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
points = [r for r in rows if r["row_type"] == "point"]
for row in range(1, 6):
line = [p for p in points if p["grid_row"] == row]
print(" ".join(f"{(p['rank'] or '-'):>3}" for p in sorted(line, key=lambda p: p["grid_col"])))
summary = next(r for r in rows if r["row_type"] == "summary")
print("average rank", summary["average_rank"], "top 3 at", summary["share_top_3"])
The business can be a name, a Google place ID or a Google Maps link. With an empty centre the grid sits on the business itself; give an address or lat, lng to centre it elsewhere, for example on a neighbourhood you want to win.
The printed grid is the whole story at a glance: low numbers near the business, rising or vanishing towards the edges. Where it vanishes is where customers do not see you.
Who beats you, and where
top_3 at each point names the businesses ahead of you, with their rating and review count. The summary's top_businesses adds them up across the grid, so you can see which two or three businesses own the map around you and how many reviews they have compared with yours.
Weekly tracking
Add a stateKey and keep the grid the same:
run_input = {
"business": "Bond Street Dental",
"keywords": ["dentist", "emergency dentist"],
"gridSize": "5x5",
"spacing": 1,
"stateKey": "bond-dental-weekly",
}
From the second run on, every point has previous_rank and rank_change (positive means you moved up), and each summary has average_rank_change, points_improved and points_declined. Save the input as a task in Apify Console and schedule it weekly. Set slackWebhookUrl and each run posts a short message with every keyword's average rank and the change.
Limits
- The rank comes from the logged-out Google Maps list of up to 20 businesses per point; ranks past 20 show as "not in top 20".
- A signed-in phone can show a slightly different order than the logged-out view.
- Results drift a little between minutes; compare runs of the same grid, zoom and centre.
- No heatmap image: the rows carry latitude and longitude for a map chart in Sheets or your own code.
Cost
You pay per grid point searched and saved, plus a small start fee per run; summary rows are free. A 5x5 grid for one keyword is 25 grid points. Current prices are on the Store page; Apify's free plan includes $5 of credit a month.
Top comments (0)