ChatGPT shopping results
The products ChatGPT's shopping search shows, with their prices and offers, in inlineProducts; shoppingCards is usually empty today.
`inlineProducts` holds the products shown within the answer, with title, position, price, merchant, rating, review count, images, offers and product URL. Today the products from ChatGPT's shopping search come there, and `shoppingCards` is usually empty.
Request them with `include.shopping`, free. The brands and products an answer names are also in `entities` (`type: "product"`).
Returned in result
| Field |
|---|
shoppingCards |
inlineProducts |
Request
curl -X POST "https://api.answerline.dev/v1/monitor/chatgpt" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"What are the best running shoes for flat feet?","country":"US","include":{"markdown":true}}'
What ChatGPT’s shopping results are
A shopping result is a product ChatGPT shows with a price, a rating and links to buy it. The contract has two fields for them: shoppingCards holds the carousels, each product with per-merchant offers, and inlineProducts holds products rendered within the answer.
The signed-out ChatGPT answers served today (checked 2026-10-19) show no product carousels, so shoppingCards comes back empty. They list products in the answer itself. A product from ChatGPT’s shopping search comes back in inlineProducts (render_as: "inline"), with its title, price, link, images and merchant offers. A product the answer names from the web search has only a price as text and a “Check price” link, so it comes back only as a name in entities (type: "product"), free and without include.shopping. entities names the shopping-search products too.
The card is the shelf: a brand can be named in the prose yet missing from the products a buyer clicks. Shopping data gives you share of shelf (how often one of your products appears in category answers, and at which position), merchant share (whether the offers point to your store, a marketplace or a reseller) and the price a buyer sees per merchant and run. The monitoring setup is in ChatGPT shopping cards.
Field reference
result.shoppingCards[]
| Field | Type | Meaning |
|---|---|---|
tags |
string[] | Category tags for the card. |
products |
object[] | Products in the card, described below. |
result.shoppingCards[].products[]
| Field | Type | Meaning |
|---|---|---|
title |
string | Product name. |
position |
integer | 1-indexed rank across all products in all shopping cards in the response, not reset per card. |
url, price |
string | Product page link carrying ChatGPT attribution; current price as displayed. |
featured_tag |
string | Product category or style tag. |
image_urls |
string[] | Product image URLs. |
rating |
number | Product rating on a 0-5 scale. |
num_reviews |
integer | Number of reviews. |
id |
string | Unique product identifier. |
offers |
object[] | Offers from different merchants: merchant_name, product_name, url, price, details (stock and delivery), available, checkoutable, price_details (base, total), tag (text). |
rating_grouped_citation |
object | Where the rating comes from: title, url, supporting_websites[] (title, url). |
result.inlineProducts[]
The products ChatGPT’s shopping search returned and the answer lists, each once, in answer order. The fields below are what the contract defines; a product carries those its answer has. On the answers served today (checked 2026-10-19) that is title, id, url, price, image_urls and offers with merchant_name, product_name, url, price, details, available and tag; no rating, num_reviews or product details.
| Field | Type | Meaning |
|---|---|---|
render_as |
inline, hero or block |
Way the product is displayed in the response. |
product |
object | The product, described below. |
details.rationale |
object | rationale, the AI-generated reason for recommending the product, and citations[] supporting it. |
details.reviews.summary |
string | Summary of product reviews. |
details.reviews.reviews[] |
object[] | Per-source review: source, theme, summary, sentiment (positive, neutral, negative), cite, rating, num_reviews, cite_url. The last four are nullable. |
result.inlineProducts[].product
| Field | Type | Meaning |
|---|---|---|
title, description |
string | Product name and description. |
position |
integer | 1-indexed rank in inlineProducts, its own sequence. |
url, price, featured_tag, rating, num_reviews, image_urls, id |
as in shopping cards | Same meaning as on card products; url and offer urls without ChatGPT’s utm_source tag. |
variants |
untyped | Product variants such as size or color. |
offers[] |
object[] | As on cards, plus merchant_subtitle, original_price (before discount), is_digital, provider, shop_id, checkout_payload, checkout_image_urls, price_details with display_price, shipping and tax, and a tag with tooltip. |
rating_grouped_citation |
object | Rating source: title, url, supporting_websites[]. |
generatedProductQuery |
string | Broad shopping query ChatGPT generated for this product: the search intent behind the recommendation. |
product_lookup_data.request_query |
string | Query used to search for this product. |
provider, providers, metadata_sources, product_lookup_key, other product_lookup_data keys, showcase_metadata, show_price_disclosure, analytics_meta, product_event_uuid |
mixed | Provider identifiers, lookup keys, and UI and tracking metadata. Opaque for analysis. |
Annotated excerpt
Values are illustrative; field names and types follow the POST /v1/monitor/chatgpt result schema with include.shopping set to true.
{
"success": true,
"result": {
"text": "For a quiet home office, a compact mechanical keyboard with...",
"inlineProducts": [
{
"render_as": "inline",
"product": {
"title": "Example Keys 75 Wireless",
"position": 1, // 1-based within inlineProducts
"id": "prod-001",
"url": "https://www.example-keys.com/k75",
"price": "$119.00", // a display string, not a number
"image_urls": ["https://www.example-keys.com/img/k75.jpg"],
"offers": [
{
"merchant_name": "Example Keys", // read the merchant here
"product_name": "Example Keys 75 Wireless",
"url": "https://www.example-keys.com/k75",
"price": "$119.00",
"details": "In stock, free delivery",
"available": true
}
]
}
}
],
// Every brand and product the answer names, the product above too.
"entities": [{ "type": "product", "name": "Example Keys 75 Wireless" }]
}
}
Cost and include flag
Shopping data needs include.shopping: true. Without it, shoppingCards and inlineProducts are not in result at all, so read them as optional; with it, today’s products arrive in inlineProducts and shoppingCards is an empty array. The flag is free, like every ChatGPT include flag: at the Starter tier a shopping request costs $0.0006.
Example: share of shelf, merchant share and median price
The script samples each buying-intent prompt, then reports each product’s shelf share, each merchant’s share of offers, and the median displayed price per product.
import os
import re
import time
from collections import Counter, defaultdict
from datetime import date
from statistics import median
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
PROMPTS = ["best quiet mechanical keyboard for a home office", "best wireless keyboard under $150"]
SAMPLES = 5
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
def products(result):
"""Products from the shopping cards and inline products, one entry per product id or title."""
found = [p for card in result.get("shoppingCards", []) for p in card.get("products", [])]
found += [item["product"] for item in result.get("inlineProducts", [])]
return list({p.get("id") or p.get("title"): p for p in found}.values())
def amount(price):
digits = re.sub(r"[^\d.]", "", price or "")
return float(digits) if digits else None
tasks = [
{
"taskType": "CHATGPT",
"payload": {"prompt": prompt, "country": "US", "include": {"shopping": True}},
"idempotencyKey": f"keyboards-shop-{p}-s{s}-{DAY}",
}
for p, prompt in enumerate(PROMPTS)
for s in range(SAMPLES)
]
answers = run_tasks(tasks)
shopping = [r for r in answers if products(r)]
shelf, merchants, prices = Counter(), Counter(), defaultdict(list)
for result in shopping:
for p in products(result):
shelf[p.get("title")] += 1
merchants.update(o["merchant_name"] for o in p.get("offers", []) if o.get("merchant_name"))
if amount(p.get("price")) is not None:
prices[p.get("title")].append(amount(p["price"]))
print(f"{len(shopping)} of {len(answers)} answers showed products")
for title, hits in shelf.most_common(10):
mid = f"{median(prices[title]):.2f}" if prices[title] else "n/a"
print(f"{title:45} shelf {hits / len(shopping):5.0%} median price {mid}")
total_offers = sum(merchants.values()) or 1
for merchant, n in merchants.most_common(10):
print(f"{merchant:30} offer share {n / total_offers:5.0%}")
Shelf share is divided by answers that showed products; report that rate next to it, because a prompt that stops showing cards is a different change from losing your spot.
Pitfalls
Prices are display strings. The amount helper handles formats like $1,299.00 only; a comma decimal separator needs a market-specific conversion. Keep country fixed so currencies never mix.
The same product can come back under title variants, and merchants use their own product_name. Map variants to your catalog before trending.
In shoppingCards, position counts across every card in the answer; to rank within one card, use the index in that card’s products array. In inlineProducts it is its own 1-based sequence.
Without include.shopping the keys are absent. Read them with a default (result.get("shoppingCards", [])).
A product carries its displayed price, and offers lists the stores it is shown with: when cards were served (to 2026-09-24), one offer whose merchant_name is the store line under the product (for example “Best Buy + more”); on today’s inline products, one offer per merchant ChatGPT lists. No product-level merchant field is filled: count merchant share from offers[].merchant_name, and keep that rule fixed.
Products and their order change between runs, so trend over several samples per period (answer volatility).
Related
- ChatGPT engine guide: sync and async calls, webhooks, geo targeting and billing.
- ChatGPT query fan-out and ChatGPT entities for the
searchQueriesfield, empty on the answers served today, and theentitiesfield, which lists the brands and products an answer names, with or without a card. - Measuring brand mentions in ChatGPT defines shopping presence alongside mention and citation rates.
- Competitor analysis.
Questions
How do I get ChatGPT shopping cards from the API?
Set include.shopping to true on POST /v1/monitor/chatgpt or on a CHATGPT task. Without the flag, shoppingCards and inlineProducts are omitted from result entirely, not returned as empty arrays.
How much does a ChatGPT shopping request cost?
$0.0006 at the Starter tier, the same as any ChatGPT request. include.shopping is free.
What is the difference between shoppingCards and inlineProducts?
shoppingCards are the product carousels, each with tags and a products list. inlineProducts is the contract field for products rendered within the answer, with a render_as value of inline, hero or block and optional rationale and review details. On the signed-out ChatGPT answers served today (checked 2026-10-19), shoppingCards comes back empty; a product from ChatGPT's shopping search comes back in inlineProducts, with its price, link, images and merchant offers; a product the answer names from the web search is only a name in entities.
Is product position reset for each shopping card?
No. In shoppingCards, position is a 1-indexed rank across all products in all cards of the response. inlineProducts numbers its products in their own 1-based sequence.
Are ChatGPT product prices returned as numbers?
No. price, offer prices and price_details values are strings as displayed. Parse them yourself and keep the market fixed.