Google AI Mode shopping results
The shopping cards and videos inside AI Mode answers.
`shoppingCards` carries the products in the answer and `videos` its video cards. `inlineProducts` and `ads` come back empty today.
`include.expandProducts` is not available yet: a request that sets it is refused with a 400 and not charged.
Returned in result
| Field |
|---|
shoppingCards |
inlineProducts |
videos |
ads |
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 shopping results are
AI Mode shopping results are the commercial blocks Google’s AI Mode places inside a conversational answer: a carousel of product cards, product cards embedded in the answer text, a sponsored section, and video cards. This API returns them as shoppingCards, inlineProducts, ads and videos.
They show which merchants hold the carousel and inline product positions for a category query, the price band and who is discounting (from numeric price.value and oldPrice.value), which stores buy the sponsored section, and which channels’ videos appear next to the products.
Field reference
result.shoppingCards[]
| Field | Type | Meaning |
|---|---|---|
title |
string | Product title. |
price.value |
number | Numeric price. |
price.currency |
string | Currency symbol. |
price.raw |
string | Raw price string as displayed, for example "$129.99". |
oldPrice.value, .currency, .raw |
number, string, string | Original price before discount, same shape as price. |
store |
string | Merchant or store name. |
rating |
number | Product rating. |
reviews |
string | Review count, as a string. |
thumbnail |
string | Product image. |
productLink |
string | Direct product URL. |
snippet |
string | Product description snippet. |
snippet_links[] |
array of {text, link, redirectLink} |
Links inside the snippet; redirectLink only when Google served a redirect. |
position |
integer | Position in the shopping carousel, 0-indexed. |
result.inlineProducts[]
| Field | Type | Meaning |
|---|---|---|
title |
string | Product title. |
position |
integer | 1-indexed rank in display order. |
price.value, price.currency |
number, string | Numeric price and currency symbol. |
oldPrice.value, oldPrice.currency |
number, string | Original price before discount. |
store |
string | Merchant or store name. |
thumbnail |
string | Product image URL. |
productLink |
string | Direct product URL. |
result.ads
| Field | Type | Meaning |
|---|---|---|
title |
string | Section title for the ads. |
ads[].title |
string | Ad product title. |
ads[].url |
string | Ad click-through URL. |
ads[].position |
integer | Position index of the ad. |
ads[].price.value, ads[].price.currency |
number, string | Numeric price and currency symbol. |
ads[].store |
string | Merchant or store name. |
ads[].rating |
number | Product rating. |
ads[].reviews |
string | Review count, as a string. |
ads[].redirectLink |
string | Google’s /goto redirect, present only when Google served one. |
result.videos[]
| Field | Type | Meaning |
|---|---|---|
title |
string, required | Video title. |
link |
string, required | Canonical watch URL, timestamp parameters stripped. |
platform |
string | Hosting platform. |
channel |
string | Channel or uploader name. |
duration |
string | Visible duration label, mm:ss or hh:mm:ss. |
thumbnail |
string | Public thumbnail URL. |
redirectLink |
string | Google’s /goto redirect, present only when Google served one. |
Annotated excerpt
Values are illustrative; field names and types follow the POST /v1/monitor/aimode result schema.
{
"success": true,
"result": {
"text": "For small apartments, a compact standing desk with a 48-inch top...",
"shoppingCards": [
{
"title": "Example Lift Desk 48\"",
"price": { "value": 279.0, "currency": "$", "raw": "$279.00" },
"oldPrice": { "value": 329.0, "currency": "$", "raw": "$329.00" },
"store": "Example Home",
"rating": 4.5,
"reviews": "1.2K", // string, not an integer
"productLink": "https://www.example-home.com/desks/lift-48",
"snippet": "Dual motor, memory presets...",
"position": 0 // 0-indexed carousel slot
}
],
"inlineProducts": [
{
"title": "Sample Compact Desk",
"position": 1, // 1-indexed
"price": { "value": 199.99, "currency": "$" },
"store": "Sample Furniture",
"productLink": "https://www.sample-furniture.com/compact-desk"
}
],
// An object: section title plus the ads in it.
"ads": {
"title": "Sponsored",
"ads": [
{
"title": "Example Pro Standing Desk",
"url": "https://www.example-desks.com/pro",
"position": 1,
"price": { "value": 349.0, "currency": "$" },
"store": "Example Desks",
"redirectLink": "https://www.google.com/goto?url=example"
}
]
},
"videos": [
{
"title": "Standing desks for tiny apartments",
"link": "https://www.youtube.com/watch?v=example",
"platform": "YouTube",
"channel": "Example Workspace",
"duration": "9:41"
}
]
}
}
Cost and include flag
shoppingCards, inlineProducts, ads and videos need no flag. An AI Mode request costs $0.00206 on every tier.
include.expandProducts, merchant offers for the products in the answer, is not available yet: a request that sets it is refused with a 400 and not charged. include.googleGoto is deprecated and ignored.
Example: store share of product slots and advertiser share
The script samples a set of product queries and reports each store’s share of all product slots (carousel plus inline), its share of first slots, the share of its slots shown with an old price, and each store’s share of ad slots.
import os
import time
from collections import Counter
from datetime import date
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
PROMPTS = ["best standing desk for a small apartment", "standing desk under $300"]
SAMPLES = 4
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"] 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"):
pending.discard(task_id)
if status == "COMPLETED":
results.append(body["response"]["result"])
return results
tasks = [
{
"taskType": "AIMODE",
"payload": {"prompt": prompt, "country": "US", "hl": "en"},
"idempotencyKey": f"desks-shop-{p}-s{s}-{DAY}",
}
for p, prompt in enumerate(PROMPTS)
for s in range(SAMPLES)
]
answers = run_tasks(tasks)
slots, first, discounted, ads = Counter(), Counter(), Counter(), Counter()
with_products = with_ads = 0
for result in answers:
# Normalise both arrays to 1-based ranks.
products = [(c.get("position", 0) + 1, c) for c in result.get("shoppingCards", [])]
products += [(i.get("position", 0), i) for i in result.get("inlineProducts", [])]
with_products += bool(products)
for rank, item in products:
store = item.get("store") or "?"
slots[store] += 1
first[store] += rank == 1
discounted[store] += bool((item.get("oldPrice") or {}).get("value"))
ad_items = (result.get("ads") or {}).get("ads", [])
with_ads += bool(ad_items)
ads.update(ad.get("store") or "?" for ad in ad_items)
n = len(answers) or 1
total_slots, total_ads = sum(slots.values()) or 1, sum(ads.values()) or 1
print(f"{len(answers)} answers; products in {with_products / n:.0%}, ads in {with_ads / n:.0%}")
for store, count in slots.most_common(10):
print(f"{store:28} slots {count / total_slots:5.0%} first {first[store]:3} discounted {discounted[store] / count:5.0%}")
for store, count in ads.most_common(10):
print(f"ad {store:24} {count / total_ads:5.0%}")
Pitfalls
shoppingCards[].position starts at 0 and inlineProducts[].position at 1. Normalize once, at ingestion.
reviews on shopping cards and ads is a string such as 1.2K. Convert it before doing arithmetic, and keep the original text.
currency is a symbol, and $ is shared by several currencies. Pair it with the request’s country when comparing markets.
ads is an object: read result["ads"]["ads"], and handle a missing ads key.
A product can appear in the carousel and inline. Deduplicate on productLink when counting distinct products; keep both when counting slots.
Product and ad blocks vary between runs, so compare periods on several samples each (AI answer volatility).
Related
- AI Mode engine guide: calls, webhooks, targeting and billing.
- AI Mode sources and AI Mode places.
- Tracking ads in AI answers and Google Search ads.
- Compare with ChatGPT shopping.
- Competitor analysis and credits.
Questions
What is the difference between shoppingCards and inlineProducts in AI Mode?
shoppingCards are the product cards in the shopping carousel, with price, old price, store, rating, reviews, snippet and snippet links. inlineProducts are product cards found within the answer text, with title, price, old price, store, thumbnail and product link.
Are AI Mode product positions 0-based or 1-based?
shoppingCards[].position is 0-indexed within the carousel. inlineProducts[].position is 1-indexed in display order. Normalize both before storing ranks.
Can I get merchant offers with include.expandProducts?
Not available yet: a request that sets it is refused with a 400 and not charged.
Is the AI Mode ads field an array?
No. ads is an object with a section title and an ads array. Each ad has title, url, position, price, store, rating, reviews and, when Google served a redirect, redirectLink.
Do shopping cards, videos and ads cost extra in AI Mode?
No. They are part of the AI Mode result at $2.06 per 1,000 on every tier. The endpoint has no add-ons.