Local AI answers: measure them by country, state and city
Gemini takes US-state targeting today. ChatGPT, Copilot, Perplexity and Grok answer at country level: a request that sends them state is refused with a 400 before anything runs, and nothing is billed. ChatGPT and Gemini answer from 213 countries, Copilot, Perplexity and Grok from any country, and Google News takes the 71 countries with a Google News edition.
To see how AI answers change by location, run the same prompt on the same engine and day across a matrix of markets, with the market set explicitly on every request, then compare the brands, sources and places each market returns. The API is built for three levels: country, state for the US on Gemini, and a Google canonical location (or uule) for city-level targeting on Google Search and AI Mode. Location is set per request, so the work is in designing the matrix and in reading the results without mistaking noise for geography.
Definitions
- Market: the location a request is made for, stored with every result: a country, a US state, or a city.
- Geo sweep: one prompt set run across a list of markets on the same day, so markets can be compared.
- Local intent: a prompt whose best answer depends on where the asker is, such as “best family lawyer” or “emergency plumber”. Non-local prompts can still vary by market through language, availability and which sources rank there.
Why answers can change by location
Consumer AI assistants ground many answers in web search, and search results depend on location. For local-intent prompts, that plausibly shifts the businesses named, the directories cited and the map results attached. For product and service prompts, market can change availability, pricing sources and regional publications.
Treat all of this as hypotheses to test on your own prompts. A first experiment: run “best family lawyer” on Gemini for Colorado and for Florida on the same day, several times each, and check whether the named firms and cited directories differ more between states than between repeat runs in one state. The method below makes that comparison systematic.
Geo parameters by engine
The parameters differ by engine. Sending state to an engine that does not accept it, or with a country other than US, is a validation error.
| Engine | Endpoint | Country | Sub-country targeting | Local result fields |
|---|---|---|---|---|
| ChatGPT | /v1/monitor/chatgpt |
country (required; 213 countries) |
none; state refused |
map[], entities[] |
| Perplexity | /v1/monitor/perplexity |
country (required; any country) |
none; state refused |
none; compare sources[] |
| Gemini | /v1/monitor/gemini |
country (required; 213 countries) |
state, US only |
places[] |
| Copilot | /v1/monitor/copilot |
country (required; any country) |
none; state refused |
map[] (coming) |
| Grok | /v1/monitor/grok |
country (required; any country) |
none; state refused |
none; compare sources[] |
| Google AI Mode | /v1/monitor/aimode |
country or gl, plus hl |
location or uule |
places[], map[] |
| Google Search | /v1/monitor/google |
country or gl, plus hl |
location or uule |
localResults[] |
| Google News | /v1/monitor/google/news |
country or gl, plus hl |
none | none |
Notes:
stateis a two-letter code and requires"country": "US". Gemini takes it; on the other assistants a request that sends it is refused with a 400 and not billed.GET /v1/states?country=USreturns the 51 entries as{code, name}, including the District of Columbia; any other country returns an empty list.locationis a Google canonical location name inCity,Region,Countryform, for exampleAustin,Texas,United States.locationanduuleare mutually exclusive.GET /v1/countriesreturns the supported ISO codes. The list per engine can differ, so checkGET /v1/countries?model=chatgpt(orgemini) before building a country matrix. Google News takes the 71 countries with a Google News edition; a request for another is refused with a 400.entities[]exists only on ChatGPT. For other engines, extract brand names fromtextyourself.- Local fields are omitted when an answer has none, so read them with a default.
The engine pages for Google local pack and AI Mode places show those fields in detail.
Running a US state sweep
The sweep below runs on Gemini, which takes state. ChatGPT, Copilot, Perplexity and Grok answer at country level and refuse state with a 400, billing nothing.
- Pick prompts with local intent. Twenty is enough to start; prompt set design covers choosing them.
- Fetch the market list from
GET /v1/states?country=USrather than hardcoding it. - Build one task per prompt and state, with an idempotency key derived from engine, prompt, state and date.
- Submit in batches of up to 500 to
POST /v1/async/task/batch, with a webhook. - Store the market on every result row. Task results and webhook bodies do not echo the payload, so keep a map from task id or key to prompt and state.
- Repeat each market at least a few times on the first sweep to measure normal variance. Add the repeat number to the idempotency key, or same-day repeats are refused as duplicates.
- Compare markets only on differences larger than that variance.
import hashlib
import os
import requests
BASE_URL = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
RUN_DATE = "2026-09-16"
PROMPTS = ["best family lawyer", "emergency plumber open now", "best pediatric dentist"]
states = requests.get(f"{BASE_URL}/v1/states", params={"country": "US"}, headers=HEADERS, timeout=30).json()
tasks = []
for prompt in PROMPTS:
for state in states:
digest = hashlib.sha256(f"{prompt}|US|{state['code']}".encode()).hexdigest()[:16]
tasks.append({
"taskType": "GEMINI",
"payload": {"prompt": prompt, "country": "US", "state": state["code"]},
"idempotencyKey": f"gemini-{digest}-{RUN_DATE}",
"webhook": {"url": "https://example.com/hooks/answers"},
})
for start in range(0, len(tasks), 500):
chunk = tasks[start:start + 500]
res = requests.post(f"{BASE_URL}/v1/async/task/batch", json=chunk, headers=HEADERS, timeout=(10, 360))
res.raise_for_status()
for item in res.json().get("results", []):
if not item["success"] and item.get("error", {}).get("code") != "RESOURCE_ALREADY_EXISTS":
print(chunk[item["index"]]["payload"], item["error"]["code"])
When results arrive, the webhook body is the finished task. For a COMPLETED task, the engine result is at response.result:
from urllib.parse import urlparse
def local_signals(body: dict) -> dict:
result = body["response"]["result"]
return {
"task_id": body["task"]["id"],
"domains": sorted({urlparse(s["url"]).hostname for s in result.get("sources", [])}),
"places": [p.get("name") for p in result.get("places", [])],
}
Look up the prompt and state for task_id from what you stored at submission. Deliveries can repeat, so dedupe by task.id; see webhook reliability design. Batch patterns for larger matrices are in rank tracking pipelines with batches.
City-level targeting on Google
For city granularity, send location to AI Mode, or to Google Search for the classic local pack. The same prompt can be compared across cities in AI Mode’s places:
import { Client } from "@answerline/sdk";
type Named = { title: string };
const client = new Client({ apiKey: process.env.API_KEY! });
const cities = ["Austin,Texas,United States", "Denver,Colorado,United States"];
for (const location of cities) {
const aiMode = await client.aiMode({ prompt: "emergency plumber near me", country: "US", location });
const places = (aiMode.result as { places?: Named[] }).places ?? [];
console.log(location, { aiModePlaces: places.map((p) => p.title) });
}
These are synchronous calls, fine for a spot check. For a recurring city matrix, submit AIMODE tasks in batches as above; the local rank tracking use case covers the pipeline.
What a sweep costs
Every market is its own request at the same price, so cost grows with the number of markets. Starter-tier base prices: ChatGPT $0.0006, Gemini $0.0006, AI Mode $0.00206, Google Search $0.00103, Google News $0.0002, Copilot $0.00274, Perplexity $0.0015, Grok $0.00218.
State sweep on Gemini:
- Tasks: 20 prompts × 51 entries = 1,020 tasks, submitted as three batches (500, 500, 20).
- Cost: 1,020 × $0.0006 = $0.612 per sweep.
- Weekly for a month (4 sweeps): $0.612 × 4 = $2.45 a month.
City check on AI Mode: 20 prompts × 10 cities × $0.00206 = $0.412 per run.
Variance runs: three repeats of one prompt in 5 states on Gemini is 3 × 5 × $0.0006 = $0.009, a cheap way to calibrate before a full sweep.
A new account’s 100 free requests work on both engines, enough to test a few states or cities before sizing a program. Cost planning covers sizing a recurring program.
Reading a sweep: metrics per market
Per-market metrics, computed once results are stored with their market:
| Metric | Computed from | What it shows |
|---|---|---|
| Presence rate | Share of runs where your brand appears in text (or entities[] on ChatGPT) |
Where you are named at all |
| Citation share | Share of citationPills[] or sources[] pointing at your domains |
Where your pages ground the answer |
| Place inclusion | Whether your business is in map[], places[] or localResults[] |
Local visibility in structured results |
| Domain overlap | Cited domains shared between two markets, divided by all cited domains in either | How similar two markets’ source pools are |
| Competitor set | Brands named in a market that are never named nationally | Regional competitors you may not track |
Compute every metric per engine, because field availability differs: Grok has no citationPills and only ChatGPT returns entities. Keep Google Search’s localResults separate from AI answers: the local pack is a ranked list, while AI answers name a handful of businesses in prose, so positions do not mean the same thing.
Country and language sweeps
Outside the US, the market unit is the country. ChatGPT and Gemini answer from 213 countries and Copilot, Perplexity and Grok from any country; on these the market is country alone. On Google Search, Google News and AI Mode, set hl as well to choose the interface language explicitly. Keep one prompt language per country run and record it, so a difference caused by translation is not reported as a difference in market. Multilingual AI answers covers translation choices.
Separating geography from noise
AI answers vary between identical runs. Before concluding that Florida sees different firms than Colorado:
- Hold everything else constant: same prompt text, engine, day, and
includeflags. - Measure within-market variance from repeat runs in one state.
- Compare frequencies, not single answers: “named in 4 of 5 Colorado runs vs 0 of 5 Florida runs” is a finding; one run each is not.
- Aggregate carefully: a national average can hide the states where a brand is absent, so report per market first.
AI answer volatility covers variance in more depth.
Pitfalls
- Sending
stateto an engine other than Gemini: refused with a 400, nothing billed.stateoutside the US is a validation error too. - Sending both
locationanduule: they are mutually exclusive. - Assuming every engine covers every country: check
/v1/countries?model=per engine. - Dropping the market from stored rows: once mixed, results cannot be split by market again.
- Comparing sweeps from different days as if the differences were geographic.
- Expecting local fields on every answer:
map,placesandlocalResultsare omitted when absent.
Field details for local results are on the Google Search engine page; the quickstart gets a first request running.
Questions
Which engines accept US state targeting?
Gemini, as a two-letter code in state, valid only with country US. ChatGPT, Copilot, Perplexity and Grok answer at country level: a request that sends them state is refused with a 400 and nothing is billed. Google News accepts country or gl and hl only.
How many countries and states can I target?
GET /v1/countries returns the supported ISO country codes and accepts ?model= to list the countries for a specific engine. Google News takes the 71 countries with a Google News edition; ChatGPT, Gemini, AI Mode and Google Search answer from 213 countries; Copilot, Perplexity and Grok take any country. GET /v1/states?country=US returns the 51 entries state targeting uses.
How much will a 50-state sweep cost?
Each state is a separate request. 20 prompts across the 51 entries from GET /v1/states on Gemini is 20 x 51 x $0.0006 = $0.612 per sweep at the Starter tier.
Does the same prompt give different answers in different states?
It can, and that is what a state sweep measures. Hold the prompt, engine and day constant, repeat runs to measure normal variance, and treat only differences larger than that variance as geographic.
How do I target a city on Google?
On Google Search or AI Mode, send location as a Google canonical location name in City,Region,Country form, such as Austin,Texas,United States, together with country. Alternatively send a pre-encoded uule; location and uule are mutually exclusive.