# Google Local Pack Tracker · AI Overview + Rank (`doesaiknow/google-local-pack-tracker`) Actor

Track the Google local 3-pack and AI Overview for any keyword and city. One row per keyword x place: who owns the 3-pack, whether an AI Overview is taking the query and who it cites, a 0-100 Local Visibility Score, week-over-week pack changes, and AI takeover alerts. Pay per row, no seat.

- **URL**: https://apify.com/doesaiknow/google-local-pack-tracker.md
- **Developed by:** [David S](https://apify.com/doesaiknow) (community)
- **Categories:** SEO tools, AI, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.016 / local pack check (per keyword × location)

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Local Pack AI Tracker

Track the Google local **3-pack** and **AI Overview** for any keyword × location — and know
the moment AI starts eating your local visibility.

Give it a list of keywords and the cities you care about. For every keyword × place pair you
get one row: who owns the local 3-pack, whether an AI Overview is now answering that query,
which domains the AI cites, a 0–100 **Local Visibility Score**, a week-over-week **pack diff**,
and an **AI takeover alert** when an AI Overview newly displaces the pack.

Built for local-SEO agencies, franchise networks, and multi-location brands who need a
decision — not a raw SERP dump.

---

### What you get per row

One dataset row = one keyword × place pair (`keywords × places` = the full cartesian list).

| Field | Type | What it tells you |
|---|---|---|
| `keyword` | string | The tracked query (echo of input) |
| `place` | string | The location it was checked in (echo of input) |
| `checkedAt` | ISO 8601 | When this observation was taken |
| `notFound` | boolean | `true` when there is no local pack for this pair (still a delivered answer) |
| `localPack` | array ≤3 `{rank, name, domain, rating, reviewsCount}` | The 3-pack, in order. Every key is always present; `domain`/`rating`/`reviewsCount` may be `null` for a business with no site or ratings |
| `aioPresent` | boolean | Is an AI Overview shown for this keyword? |
| `aioCitedDomains` | string[] | Normalised domains the AI Overview cites |
| `aioDisplacement` | boolean | AI Overview is present **and** sits above / has pushed out the local pack |
| `localVisibilityScore` | number 0–100 | Your visibility for this query — see below |
| `packDiff` | `{added[], dropped[], rankShifts[]}` \| `null` | What changed in the pack since the previous run — see below |
| `aioTakeoverAlert` | boolean | An AI Overview **newly** displaced the pack vs the previous run — see below |
| `source` | `"primary"` \| `"fallback"` | Which data path filled the row |

#### Local Visibility Score (0–100)

A single number that answers "how visible am I for this query, right now?"

- Position weights in the pack: **rank 1 = 100**, **rank 2 = 80**, **rank 3 = 60**.
- With `trackedDomains` set: the score is the best weight among your domains found in the pack
  (not in the pack → `0`).
- Without `trackedDomains`: the score reflects the pack **surface itself** — pack present = `100`.
- An AI Overview that displaces the pack subtracts a **−25** penalty; the result is clamped to 0–100.
- `notFound` (no pack) → `0`.

#### `packDiff` — week-over-week 3-pack movement

The actor keeps a snapshot of each keyword × place pack between runs, so on the next run you
see exactly what moved:

- `added` — domains that entered the pack
- `dropped` — domains that fell out
- `rankShifts` — `{domain, from, to}` for domains that changed position

The **first run has no prior snapshot, so `packDiff` is `null`** (the key is always present, never
missing). A missing or corrupt snapshot is never an error — you get `null` and the run continues.
Domains without a website (`domain: null`) do not take part in the diff.

#### `aioTakeoverAlert` — the signal that matters

`true` only when an AI Overview **newly** displaces the local pack for a query that it did not
displace on the previous run. This is your early-warning that AI has started intercepting a
query you used to win in the map pack. No prior snapshot → `false`.

---

### Input

```json
{
  "keywords": ["plumber near me", "emergency plumber"],
  "places": ["Austin, TX", "Dallas, TX"],
  "languageCode": "en",
  "trackedDomains": ["acmeplumbing.com"]
}
````

| Field | Required | Notes |
|---|---|---|
| `keywords` | yes | Local search queries (min 1). Each is combined with every place. |
| `places` | yes | Locations to check (min 1). `keywords × places` = the rows tracked this run. US: `"Austin, TX"`. International: a plain city name plus `countryCode` (e.g. `"London"` + `"GB"`), or a fully qualified `"City,Region,Country"` form. |
| `languageCode` | no | Two-letter language code. Default `"en"`. |
| `countryCode` | no | Two-letter ISO country code of your places (e.g. `"GB"`, `"DE"`, `"PL"`). Resolves plain city names and picks the Google market for AI Overview checks. Derived from places/language when omitted. |
| `trackedDomains` | no | Your own domains, used to compute the Local Visibility Score. Leave empty to report pack composition only. |

***

### Pricing (pay per row, no seat)

Billing is **per delivered row**, using Apify's pay-per-event model.

| Event | When | Price |
|---|---|---|
| Run start | Once per run | **$0.005** (flat, one-time) |
| Tracked row | Per delivered row | **$0.02** per row (volume ladder below) |

Volume ladder for the per-row event across account tiers:

| Tier | Price / row |
|---|---|
| FREE / BRONZE | $0.020 |
| SILVER | $0.018 |
| GOLD | $0.016 |
| PLATINUM | $0.014 |
| DIAMOND | $0.012 |

**You are never charged for:**

- **Cache hits** — a keyword × place pair checked in the last **24 hours** is served from cache
  and bills nothing beyond the one-time run start.
- **Failures** — a row that could not be fetched is skipped and never billed.

A `notFound` result **is** a delivered answer and is billed as one row — knowing a query has no
local pack is a result.

Typical cadence: re-run every **~7 days** to keep the week-over-week `packDiff` and takeover
alerts fresh.

***

### How it stays reliable

- **Cache (24h):** identical keyword × place pairs within 24 hours are served from cache — free,
  and with the original observation time preserved.
- **Snapshots:** a per-pair pack snapshot powers `packDiff` and `aioTakeoverAlert`. State is
  read/written fail-open — a storage hiccup yields `packDiff: null`, never a crashed run.
- **Fallback path:** if the primary source fails twice for a single pair, that one row is retried
  through a secondary source (`source: "fallback"`) so it is still delivered.
- **Plan-limit guard:** on FREE accounts the run stops **before** the first paid request when the
  forecast exceeds your plan limit — nothing is charged.

***

### Legal & fair use

- This actor reports **publicly visible** Google local pack and AI Overview results for the
  keywords and locations you provide. It stores no personal data beyond public business listing
  fields (name, domain, rating, review count).
- Output is provided **as-is** for competitive-intelligence and SEO-monitoring purposes. Search
  results change constantly; figures are point-in-time observations, not guarantees.
- Google is a trademark of Google LLC. This actor is **not affiliated with, endorsed by, or
  sponsored by** Google.
- You are responsible for using the output in line with the terms of the platforms you operate on
  and applicable law in your jurisdiction.

***

### Related actors

Pair this with the rest of the doesaiknow suite for a full local + listing view. See the
publisher page for companion actors (rank tracking, listing audits, keyword research).

# Actor input Schema

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

Local search queries to track (e.g. "plumber near me"). Each keyword is combined with every place (cartesian product) into one tracked query row.

## `places` (type: `array`):

Locations to check each keyword in. US: "Austin, TX". International: a plain city name plus the Country code field (e.g. "London" with country code "GB", "Berlin" with "DE", "Warszawa" with "PL"), or a fully qualified location "City,Region,Country" (e.g. "London,England,United Kingdom"). Each place is combined with every keyword; keywords × places = the list of queries tracked in this run.

## `languageCode` (type: `string`):

Language of the search results.

## `countryCode` (type: `string`):

Country of the places you track — used to resolve plain city names to a location. Leave on "Auto" to let the location resolver decide.

## `trackedDomains` (type: `array`):

Optional. Your own domains to score for local visibility (e.g. "acmeplumbing.com"). Used to compute the Local Visibility Score; leave empty to only report the pack composition.

## Actor input object example

```json
{
  "keywords": [
    "plumber near me"
  ],
  "places": [
    "Austin, TX"
  ],
  "languageCode": "en",
  "countryCode": ""
}
```

# Actor output Schema

## `results` (type: `string`):

All delivered rows: keyword, place, localPack, aioPresent, aioCitedDomains, localVisibilityScore, packDiff, aioTakeoverAlert, source.

## `snapshots` (type: `string`):

Per-pair snapshots powering week-over-week packDiff and takeover alerts.

# 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 = {
    "keywords": [
        "plumber near me"
    ],
    "places": [
        "Austin, TX"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("doesaiknow/google-local-pack-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 = {
    "keywords": ["plumber near me"],
    "places": ["Austin, TX"],
}

# Run the Actor and wait for it to finish
run = client.actor("doesaiknow/google-local-pack-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "plumber near me"
  ],
  "places": [
    "Austin, TX"
  ]
}' |
apify call doesaiknow/google-local-pack-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=doesaiknow/google-local-pack-tracker",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Google Local Pack Tracker · AI Overview + Rank",
        "description": "Track the Google local 3-pack and AI Overview for any keyword and city. One row per keyword x place: who owns the 3-pack, whether an AI Overview is taking the query and who it cites, a 0-100 Local Visibility Score, week-over-week pack changes, and AI takeover alerts. Pay per row, no seat.",
        "version": "0.0",
        "x-build-id": "pbbhMAfsZwUXp7Dpj"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/doesaiknow~google-local-pack-tracker/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-doesaiknow-google-local-pack-tracker",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/doesaiknow~google-local-pack-tracker/runs": {
            "post": {
                "operationId": "runs-sync-doesaiknow-google-local-pack-tracker",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/doesaiknow~google-local-pack-tracker/run-sync": {
            "post": {
                "operationId": "run-sync-doesaiknow-google-local-pack-tracker",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "keywords",
                    "places"
                ],
                "properties": {
                    "keywords": {
                        "title": "Keywords",
                        "minItems": 1,
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Local search queries to track (e.g. \"plumber near me\"). Each keyword is combined with every place (cartesian product) into one tracked query row.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "places": {
                        "title": "Places",
                        "minItems": 1,
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Locations to check each keyword in. US: \"Austin, TX\". International: a plain city name plus the Country code field (e.g. \"London\" with country code \"GB\", \"Berlin\" with \"DE\", \"Warszawa\" with \"PL\"), or a fully qualified location \"City,Region,Country\" (e.g. \"London,England,United Kingdom\"). Each place is combined with every keyword; keywords × places = the list of queries tracked in this run.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "languageCode": {
                        "title": "Language",
                        "enum": [
                            "en",
                            "de",
                            "fr",
                            "es",
                            "it",
                            "pt",
                            "nl",
                            "pl",
                            "sv",
                            "no",
                            "da",
                            "fi",
                            "cs",
                            "ro",
                            "el",
                            "tr",
                            "ru",
                            "uk",
                            "ar",
                            "he",
                            "hi",
                            "id",
                            "ja",
                            "ko",
                            "zh",
                            "th",
                            "vi",
                            "ms"
                        ],
                        "type": "string",
                        "description": "Language of the search results.",
                        "default": "en"
                    },
                    "countryCode": {
                        "title": "Country (optional)",
                        "enum": [
                            "",
                            "US",
                            "GB",
                            "DE",
                            "FR",
                            "ES",
                            "IT",
                            "NL",
                            "PL",
                            "SE",
                            "NO",
                            "DK",
                            "FI",
                            "IE",
                            "PT",
                            "CH",
                            "AT",
                            "BE",
                            "CZ",
                            "RO",
                            "GR",
                            "TR",
                            "RU",
                            "UA",
                            "CA",
                            "MX",
                            "BR",
                            "AR",
                            "CL",
                            "CO",
                            "AU",
                            "NZ",
                            "IN",
                            "ID",
                            "JP",
                            "KR",
                            "CN",
                            "TW",
                            "HK",
                            "SG",
                            "MY",
                            "TH",
                            "VN",
                            "PH",
                            "AE",
                            "SA",
                            "IL",
                            "EG",
                            "ZA",
                            "NG",
                            "KE"
                        ],
                        "type": "string",
                        "description": "Country of the places you track — used to resolve plain city names to a location. Leave on \"Auto\" to let the location resolver decide.",
                        "default": ""
                    },
                    "trackedDomains": {
                        "title": "Tracked domains",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Optional. Your own domains to score for local visibility (e.g. \"acmeplumbing.com\"). Used to compute the Local Visibility Score; leave empty to only report the pack composition.",
                        "items": {
                            "type": "string"
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
