Local rank tracking measures where a website ranks on Google for a search as seen from a specific city or area. Google uses the searcher's location to pick results, so the same keyword can rank differently in different cities.
Local Rank Tracking: How to Check Google Rankings by City
Local rank tracking means checking where a website ranks on Google for the same search, as seen from each city or area it serves. It is rank tracking with a location attached. It matters because Google uses location to choose results: "your current location is used to give you more helpful results," and a search for coffee shops shows ones nearby "even if you didn't include a location in your search," as Google's own help page puts it (Google Search Help). A chain of clinics can rank third in one suburb and not appear in the next town over.
This guide covers the teams that track local rankings, the two ways to make a search "local," and an offline-tested Python script that checks rankings city by city with Massive's Search endpoint. It sits next to our earlier guide to ChatGPT rank tracking, which does the same job for AI answers.
Key Takeaways
- Google estimates location from several sources. One is the IP address, which Google says is "roughly based on geography."
- A local rank check needs a search that Google treats as coming from the target city. You can either search from a device in that city or emulate the location with a
uulestring. - Massive's Search endpoint does the first with
country,subdivision, andcityparameters. Withuule, its docs say "if possible, the actual location is searched from not emulated." - Results come back as rendered HTML, not JSON, so you count positions yourself. Our parser and loop below pass 10 offline tests; they have not been run against a live Google page.
- Track a fixed keyword list, the same cities, and fresh results (
expiration=0) on a regular schedule, and save the raw pages.
Who needs local rank tracking?
Three kinds of teams run it at scale:
- Local SEO agencies report rankings for clients with one or many locations, often across a grid of points around each address.
- Multi-location brands (franchises, retailers, clinics, home services) compare how each branch ranks in its own market.
- Rank tracking tools sell local data to both groups, and they need location-accurate results on demand.
They share one problem: the person running the check is rarely in the city being checked. A search run from an office in New York shows New York results.
Why do Google rankings change by location?
Google's help page lists the sources it combines to estimate where a searcher is, including device location settings and "the IP address of your Internet connection." On IP addresses, Google says they "are roughly based on geography," so "any website you use, including google.com, may get some information about your general area." When Google used the IP address to place a search, the results page says "From your internet address" at the bottom.
That gives a local rank check two requirements: Google has to see the search as coming from the target area, and the check has to be repeatable, so last week's number and this week's number mean the same thing.
Real location or emulated location?
There are two common ways to make a search local:
Massive's Search endpoint supports both. Its geotargeting parameters route each search "through one of more than 190 countries, by subdivision and city" (Massive docs, geotargeting), using real consumer devices on Massive's network. It also accepts uule, and the docs add that "if possible, the actual location is searched from not emulated" (Massive docs, search). For most local SEO work, the city parameter is the simpler starting point.
How to track Google rankings by city
The steps below use Python with requests and beautifulsoup4. Set your API token as MASSIVE_TOKEN.
1. Fetch the results page from each city
serp() sends one search from one city. serps=2 fetches two results pages, and expiration=0 turns off Massive's cache, which otherwise reuses results for up to a day. A live search averages a few seconds, but the docs allow up to 3 minutes per call for retries, so the timeout is generous.
City names follow the "commonly spelled name" from GeoNames, and the docs note they are "temporarily case sensitive," so write Chicago, not chicago.
2. Find your position in the HTML
The Search endpoint returns "rendered HTML," the same page a browser would show. rank() counts links that contain an <h3> heading and an absolute URL, which is how organic result titles are usually marked up, and returns the position of the first one on your domain or a subdomain. Results from both pages count in order.
This is a heuristic. Google changes its markup, and features such as the map pack, ads, and "People also ask" are laid out differently, so check it against a few saved pages for your own keywords before you rely on it. If you also want AI Overviews or People-also-ask blocks in the page, add awaiting=ai or awaiting=answers; the Search docs cap the wait for an AI Overview at one minute. Our post on AI Overviews and PAA on Search covers both.
3. Run the grid
grid() checks one keyword across a list of cities and returns a position for each, or None when the domain isn't on the pages fetched.
Run it for each keyword, for example grid("emergency plumber", [("US", "Chicago"), ("US", "Austin")], "example.com"). It returns a dict shaped like this illustrative output, not a live result:
{'Chicago': 3,'Austin': None}
Write each run to a CSV or JSON file with the keyword, the date, and the city, and keep the raw HTML next to it. The Search docs say the endpoint autoscales and that a 503 should resolve within a minute, so retry a failed city after a short wait rather than stopping the whole grid.
All three functions are tested offline: 10 tests cover the request parameters, subdomain, look-alike, and mixed-case domain matching, counting across pages, and the city loop (see the methods note below). Without a token, the endpoint returns 401 with "Authorization header must be provided," which we confirmed on November 1, 2026. That is the first error a new integration sees.
What about the map pack and geo-grids?
For a search like "emergency plumber," the map pack (the local three-pack with a map) is often what customers look at first, and rank() ignores it. The function counts organic results only: links with an <h3> title. Map pack listings use different markup, so they need their own parser, written against pages you have saved and checked, because Google's layout changes. Many local SEO tools also track a grid of points around a business address rather than one city. That needs finer locations than the city parameter gives, such as a uule string per grid point. Massive's Search endpoint accepts uule and, per its docs, searches from the actual location when it can. Start with organic positions per city, and add map pack and grid tracking once the basic loop is stable.
Google's own guidance explains why map results move so much by location: "Local results are mainly based on relevance, distance, and popularity" (the page's section calls the last one prominence), and distance "refers to how far each business is from the customer who's searching" (Google Business Profile Help). Distance is the one factor that changes with every city you check.
Tips for accurate local rankings
- Fix the keyword list. Change it only on purpose, or week-over-week numbers stop being comparable.
- Use the same cities every time. If you serve a metro area, pick the suburbs that matter, and check each one.
- Turn off caching for tracking runs.
expiration=0makes every check a live search. - Track mobile separately. The
deviceparameter emulates a smartphone or tablet from the list at/search/devices. Mobile and desktop results may differ, so track them as separate series. - Keep the raw pages. When a position changes, the saved HTML shows whether a map pack, an ad block, or a competitor moved.
- Budget the calls. Each successful Search call costs one base credit; the Web Render pricing page has current rates. Keywords times cities times runs adds up quickly.
If you buy local SERP data rather than build it, our local SERP tracking page covers proxies for rank tracking, and proxy geo-targeting by city, ZIP code, and ASN covers the network-level options.
Methods note: the tests use a small synthetic page shaped like a results page, not a saved Google page, and replace network calls with a stub. They check the counting rules and the documented request parameters, not live rankings.
The bottom line
- Google picks results by location, so local rank tracking only means something with a city attached.
- Searching from a device in the target city matches what local searchers see;
uuleemulation is the fallback. - Fix the keywords and cities, fetch fresh results, count positions in the HTML, and keep the raw pages.
Sources
- Google Search Help, "Understand & manage your location when you search on Google"
- Google Business Profile Help, "Tips to improve your local ranking on Google"
- Massive docs, Search
- Massive docs, Geotargeting
- Massive, Web Render pricing
- Massive, no-token request to the Search endpoint and 10 offline tests of the code above, November 1, 2026 (first-party test)
Frequently Asked Questions
Run the search so that Google sees it as coming from that city, either from a device located there or with an emulated location such as a uule string. Massive's Search endpoint does the first with its country, subdivision, and city parameters.
Google estimates your location from sources such as your IP address and device settings, and shows results it considers more helpful for that place. Local businesses and map results change the most.
uule is an encoded location string that tells Google to treat a search as coming from a given place. Massive's Search endpoint accepts it, and its docs say it searches from the actual location when possible instead of emulating it.
Weekly works for most local SEO reporting, and daily for volatile or high-value terms. Use the same keywords, cities, and device type on every run, and turn off caching so each check is a live search.
