John Cole

Jul 24, 2026 • 3 min read

How to Track Google Local Pack Rankings with One API

Track local pack positions for any keyword and city, then export the history to CSV. A pay-per-page API and MCP tool for local SEO.

Every SEO agency I know has the same recurring headache: proving to a local client that their map ranking moved. The local pack, that block of three businesses on a map at the top of Google Search, is where local revenue starts, and Google gives you no ranking endpoint for it. So you end up eyeballing incognito searches and screenshotting, which is neither repeatable nor billable.

The Google Local API tracks Google local pack rankings by querying the local results for a keyword in a specific city and returning the businesses and their positions as clean JSON.

What the ranking data looks like

Each local result comes back as one structured row you can diff over time:

  • Business name and its position in the pack

  • Rating and review count

  • Phone, hours, and address

  • GPS coordinates and place_id for stable joins

You target by city, country (gl), language (hl), and device, which matters because the pack a phone shows often differs from desktop. Billing is per page, so a single ranking check for one keyword-city pair is a fraction of a cent.

Track rankings in three steps

Save one task per keyword-city pair, run it on a schedule, and diff the positions run over run:

  • Set the query, for example "plumber near me", and the city.

  • Run it and read the position field on each business.

  • Attach an Apify schedule to build a rank history, and export to CSV.

Use it as a REST API

Kick off a run with a single POST and read the dataset when it finishes:

POST /v2/acts/johnvc~google-local-api/runs

{ "q": "plumber near me", "location": "Tampa, Florida", "max_pages": 1 }

The run lifecycle is standard Apify, so any existing Apify client library works without extra glue.

Wire it into Claude with MCP

Over the Model Context Protocol the Actor becomes a tool Claude, Claude Code, or Cursor can call. Ask "where does example.com rank in the local pack for roofers in Denver?" and your agent runs a live check and answers from the actual SERP, no scraping code.

New to Claude? You can start with a free trial through my referral.

What people build with it

Two patterns dominate. Agencies wire up rank tracking, starting from the local pack rank tracker for SEO agencies task or the "plumber near me" rankings template. Lead-gen operators build contact lists instead, like roofer contacts in Dallas. There are more runnable examples on the Google Local API project page.

FAQ About Tracking Local Pack Rankings

What does the local pack scraper actually track?

The scraper returns the businesses in the local pack for a query in a chosen city, each with its position, rating, review count, phone, hours, and place_id. Run the same query-city pair on a schedule and the position field is your rank history.

How do I call the local pack scraper as a REST API on a schedule?

Save one task per keyword-city pair, attach an Apify schedule, and read the position field each run. Output exports to JSON or CSV, and a single check costs a fraction of a cent since billing is per page.

Can Claude call the local pack scraper as an MCP tool?

Yes. Registered through Apify's MCP server, the scraper shows up as a callable tool in Claude, Claude Code, and Cursor, and returns the same structured rows a REST call would, so an agent can answer ranking questions on demand.

What will a local pack scraper not tell you?

It returns phones and links, not email addresses, so email prospecting needs an enrichment step. The pack is also location and device specific, so treat a run as a consistent measurement from the city and device you requested, not the exact page every user sees.

Issues and questions get a same-day response, so if a query behaves oddly, send it over and I will look.

Join John on Peerlist!

Join amazing folks like John and thousands of other builders on Peerlist.

peerlist.io/

It’s available... this username is available! 😃

Claim your username before it's too late!

This username is already taken, you’re a little late.😐

0

2

0