Google AI Mode places
The contract fields for local places and map results in AI Mode answers. They come back empty today.
`places` and `map` are fields of the AI Mode contract for the local pack of Google's conversational search. The answers served today do not carry them, so both come back empty.
Returned in result
| Field |
|---|
places |
map |
Request
curl -X POST "https://api.answerline.dev/v1/monitor/aimode" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"Family-friendly hotels in Lisbon","country":"PT","hl":"en"}'
What AI Mode places are
AI Mode places are the local businesses Google’s AI Mode shows inside a conversational answer to a local question, such as “emergency plumber near me” or “quiet coffee shops to work from”. This API returns them twice over: places holds the place cards in the order the answer shows them, with rating, reviews, category, price, address and status, and map holds the places Google pinned on the answer’s map, with GPS coordinates. A place gets its placeId and a map entry when Google pins it; an answer without a map has an empty map.
For local businesses and the agencies serving them, this is the local pack of conversational search. The core metrics are presence rate (the share of answers for a keyword and location that list the business), average position when present, how both change across a service area with the location parameter, and which competitors fill the other slots with which rating and review count.
Field reference
result.places[]
| Field | Type | Meaning |
|---|---|---|
title |
string | Place name. |
link |
string | URL that opens the place in Google’s viewer. |
placeId |
string | Google Places ID. |
index |
integer | Index of the place’s position in the results. |
thumbnail |
string | Place thumbnail image URL. |
rating |
number | Star rating, 0 to 5. |
reviews |
integer | Number of reviews. |
type |
string | Place type or category. |
priceLevel |
string | Price level indicator. |
address |
string | Full address. |
status |
string | Operating status. |
result.map[]
| Field | Type | Meaning |
|---|---|---|
title |
string | Place name. |
link |
string | URL that opens the place in Google’s viewer. |
placeId |
string | Google Places ID. |
index |
integer | Index of the place’s position in the results. |
gps_coordinates.latitude |
number | Latitude. |
gps_coordinates.longitude |
number | Longitude. |
thumbnail |
string | Place thumbnail image URL. |
rating |
number | Star rating, 0 to 5. |
reviews |
integer | Number of reviews. |
type |
string | Place type or category. |
address |
string | Full address. |
status |
string | Operating status. |
All fields are optional. The differences are small but matter when you store them: only places has priceLevel, only map has gps_coordinates, and gps_coordinates is snake_case while the rest is camelCase. reviews is an integer here, unlike the string reviews on AI Mode shopping cards and ads.
Annotated excerpt
Values are illustrative; field names and types follow the POST /v1/monitor/aimode result schema.
{
"success": true,
"result": {
"text": "Several plumbers in the area offer 24-hour emergency service...",
// Inline place cards in the answer.
"places": [
{
"title": "Example Plumbing & Drain",
"link": "https://www.google.com/viewer/place?example-1",
"placeId": "ChIJexamplePlumbing1",
"index": 0,
"rating": 4.8,
"reviews": 612, // integer on places and map
"type": "Plumber",
"priceLevel": "$$",
"address": "410 Example Ave, Denver, CO",
"status": "Open 24 hours"
},
{
"title": "Sample Rooter Co.",
"link": "https://www.google.com/viewer/place?example-2",
"placeId": "ChIJexampleRooter2",
"index": 1,
"rating": 4.5,
"reviews": 203,
"type": "Plumber"
}
],
// GPS-enriched entries: coordinates, no priceLevel.
"map": [
{
"title": "Example Plumbing & Drain",
"placeId": "ChIJexamplePlumbing1",
"index": 0,
"gps_coordinates": { "latitude": 39.739, "longitude": -104.99 },
"rating": 4.8,
"reviews": 612,
"address": "410 Example Ave, Denver, CO"
}
]
}
}
Cost and request options
No include flag is needed. An AI Mode request costs $0.00206 on every tier; places and map add nothing. A grid multiplies quickly: 3 keywords × 10 locations × 3 samples is 90 tasks, $0.1854 per run. Size the grid on credits before scheduling it.
| Field | Use for local studies |
|---|---|
country or gl |
Country, ISO 3166-1 alpha-2. |
location |
Google canonical City,Region,Country name from the Google Ads geotargets list. |
uule |
Pre-encoded UULE string, instead of location. |
hl |
Interface language, if it differs from the country default. |
Example: presence rate and average position across a city grid
The script runs a few local keywords across a list of locations, several samples each, and reports, for your listings in MY_PLACE_IDS, the presence rate and average rank per keyword and location, plus the competitors that appear most.
import os
import time
from collections import Counter, defaultdict
from datetime import date
from statistics import mean
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
KEYWORDS = ["emergency plumber near me", "water heater repair"]
LOCATIONS = [
"Denver,Colorado,United States",
"Aurora,Colorado,United States",
"Lakewood,Colorado,United States",
"Littleton,Colorado,United States",
]
MY_PLACE_IDS = {"ChIJexamplePlumbing1"}
SAMPLES = 3
DAY = date.today().isoformat()
def run_tasks(tasks):
resp = requests.post(f"{API}/v1/async/task/batch", headers=HEADERS, json=tasks, timeout=60)
resp.raise_for_status()
pending = {item["task"]["id"]: tasks[item["index"]] for item in resp.json()["results"] if item["success"]}
results = []
while pending:
time.sleep(10)
for task_id in list(pending):
body = requests.get(f"{API}/v1/async/task/{task_id}", headers=HEADERS, timeout=30).json()
status = body["task"]["status"]
if status in ("COMPLETED", "FAILED"):
task = pending.pop(task_id)
if status == "COMPLETED":
results.append((task["payload"], body["response"]["result"]))
return results
tasks = [
{
"taskType": "AIMODE",
"payload": {"prompt": keyword, "country": "US", "location": location},
"idempotencyKey": f"plumber-{k}-{location.split(',')[0].lower()}-s{s}-{DAY}",
}
for k, keyword in enumerate(KEYWORDS)
for location in LOCATIONS
for s in range(SAMPLES)
]
runs = Counter()
ranks = defaultdict(list)
competitors = Counter()
for payload, result in run_tasks(tasks):
cell = (payload["prompt"], payload["location"])
runs[cell] += 1
cards = sorted(result.get("places", []), key=lambda p: p.get("index", 0))
mine = [rank for rank, card in enumerate(cards, start=1) if card.get("placeId") in MY_PLACE_IDS]
if mine:
ranks[cell].append(mine[0])
competitors.update(
card.get("title") or card.get("placeId") or "?" for card in cards if card.get("placeId") not in MY_PLACE_IDS
)
for keyword in KEYWORDS:
print(f"\n{keyword}")
for location in LOCATIONS:
cell = (keyword, location)
seen = ranks[cell]
presence = len(seen) / (runs[cell] or 1)
position = f"{mean(seen):.1f}" if seen else "-"
print(f" {location:40} runs {runs[cell]:2} present {presence:5.0%} avg rank {position}")
print("\nmost frequent competitors:", competitors.most_common(10))
The rank is derived from the order of index within each answer, starting at 1, so the report does not depend on the base index uses. Each run contributes at most one rank, the best one, so presence stays a share of runs even if a listing repeats within an answer.
Reading the grid
Read presence before position. Average rank only covers runs where the business appeared, so a 1.2 average with 20% presence is weaker than 2.5 with 90%.
A business strong in its home city and absent two suburbs away has a service-area problem rather than a ranking problem. Plot presence per location on the map coordinates of the businesses that do appear.
Run the same keywords, with the city in the prompt, through Gemini to see whether Google’s assistant picks the same businesses in its place cards.
Pitfalls
location must be a Google canonical name. Keep a vetted list and never build names by string formatting from user input.
Match your business on placeId. Titles vary (“Example Plumbing” versus “Example Plumbing & Drain”) and franchises share names.
The same business can appear in both places and map. Choose one array as the rank source; the example uses places.
Brand or informational prompts may produce no place cards. Use the wording customers use, “near me” included, and keep it fixed.
Answers vary between runs. Take several samples per cell and keep the targeting fixed in one series (AI answer volatility).
Related
- AI Mode engine guide: calls, webhooks, targeting and billing.
- AI Mode sources and AI Mode shopping.
- Local AI answers by country, state and city.
- Compare with Gemini places.
- Local rank tracking.
Questions
What is the difference between places and map in AI Mode results?
places holds the inline place cards in the answer, with price level. map holds GPS-enriched location data with gps_coordinates. Both carry title, link, placeId, index, thumbnail, rating, reviews, type, address and status.
Do AI Mode places cost extra?
No. places and map are part of the AI Mode result at the base price: $2.06 per 1,000 on every tier. No include flag is needed.
How do I target a city for AI Mode local results?
Send country or gl together with location, a Google canonical name in City,Region,Country form, or send a pre-encoded uule instead of location. location and uule are mutually exclusive.
How should I match my business in AI Mode places?
Match on placeId, the Google Places ID. Titles vary in wording and several branches can share a name, while placeId identifies one listing.
Is AI Mode places index 0-based or 1-based?
Don't depend on the base. Derive a rank from the sort order of index within each answer, starting at 1, so every series uses the same base.