Perplexity shopping results
The contract fields for the shopping cards, places and hotels Perplexity shows. They come back empty today.
`shopping_cards`, `places` and `hotels` are fields of the Perplexity contract. The answers served today carry the text, sources and citation pills only, so all three come back empty.
Returned in result
| Field |
|---|
shopping_cards |
places |
hotels |
Request
curl -X POST "https://api.answerline.dev/v1/monitor/perplexity" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"Which project management tools do agencies use?","country":"US"}'
placesis returned whenever Perplexity shows a map, hotels included.shopping_cardsandhotelsare not returned today: a free perplexity.ai answer shows neither. This page documents the contract’s fields.
What Perplexity’s shopping, place and hotel results are
Perplexity turns some answers into listings: product cards for shopping intent, hotels for travel intent, and places for local questions. The API returns them as shopping_cards, hotels and places.
They tell you how often your product appears in a card and at which position, which merchants carry the offers and how often your store is one of them, and whether your location is listed, with what rating, for local and travel prompts in a market.
Field reference
result.shopping_cards[]
| Field | Type | Meaning |
|---|---|---|
tags |
string[] | Optional category tags for the card. |
products |
object[] | Products in the card, described below. |
result.shopping_cards[].products[]
| Field | Type | Meaning |
|---|---|---|
title |
string | Product name. |
id |
string | Product identifier. |
url |
string | Product page URL. |
price |
string | Current price. |
original_price |
string, nullable | Price before discount; null when not on sale. |
merchant |
string | Merchant name. |
available |
boolean | True if the product is available right now. |
imageUrls |
string[] | Product image URLs. |
numReviews |
number | Number of reviews, 0 when none. |
rating |
number | Product rating on a 0-5 scale. |
position |
integer | 1-indexed rank across all products in all cards, not reset per card. |
description |
string | Product description. |
variants |
object[] | Product variants such as size or color. |
offers |
object[] | Offers from individual merchants, described below. |
result.shopping_cards[].products[].offers[]
| Field | Type | Meaning |
|---|---|---|
merchant_name |
string | Merchant name. |
product_name |
string | Product title as given by the merchant. |
url |
string | Offer link including Perplexity referral parameters. |
price |
string | Offer price. |
original_price |
string, nullable | Price before discount; null when not on sale. |
available |
boolean | True if this offer currently has stock. |
price_details.display_price |
string | Formatted display price. |
result.places[]
| Field | Type | Meaning |
|---|---|---|
name |
string | Place name. |
url |
string | Place page URL. |
rating |
number | Rating, 0-5. |
num_reviews |
integer | Review count. |
phone |
string | Phone number. |
description |
string | The place’s description on its card. |
address |
string[] | Address lines. |
lat, lng, categories |
number, string[] | Coordinates and place categories. |
map_url |
string | Map URL. |
images |
string[] | Place image URLs. |
result.hotels[]
| Field | Type | Meaning |
|---|---|---|
name |
string | Hotel name. |
url |
string | Hotel page URL. |
rating |
number | Rating, 0-5. |
num_reviews |
number | Number of reviews. |
address |
string[] | Address lines. |
phone |
string | Phone number. |
description |
string | Hotel description. |
image_url, images |
string, string[] | Main and additional image URLs. |
lat, lng, price_level |
number, string | Coordinates and price level indicator. |
categories |
string[] | Hotel categories. |
Annotated excerpt
Values are illustrative; field names follow the POST /v1/monitor/perplexity result schema. The three blocks are shown in one excerpt only to document them.
{
"success": true,
"result": {
"text": "For long commutes, noise-cancelling over-ear headphones such as...",
"shopping_cards": [
{
"tags": ["Over-ear"],
"products": [
{
"title": "Example Audio Quiet 700",
"id": "p-7001",
"price": "$249.00",
"original_price": "$299.00", // non-null: on sale
"merchant": "Example Audio",
"available": true,
"imageUrls": ["https://img.example.com/q700.jpg"], // camelCase
"numReviews": 1320, // camelCase
"rating": 4.4,
"position": 1,
"offers": [
{
"merchant_name": "Sample Electronics", // snake_case
"product_name": "Quiet 700 Wireless Headphones",
"url": "https://www.sample-electronics.com/q700?ref=example",
"price": "$244.99",
"original_price": null,
"available": true,
"price_details": { "display_price": "$244.99" }
}
]
}
]
}
],
"places": [
{
"name": "Example Coffee Roasters",
"rating": 4.7,
"address": ["12 Example Street", "Austin, TX 78701"],
"categories": ["Coffee shop"],
"map_url": "https://maps.example.com/?q=example-coffee"
}
],
"hotels": [
{
"name": "Sample Hotel Downtown",
"rating": 4.3,
"num_reviews": 980, // snake_case here, unlike products
"address": ["100 Sample Ave", "Austin, TX 78701"],
"price_level": "$$$"
}
]
}
}
Cost and include flag
There is no shopping include flag on Perplexity. shopping_cards, places and hotels appear when Perplexity shows them, and the price does not change: $0.0015. You pay per run whether a block appears or not, so pick prompts with clear intent.
Example: merchant, product and place presence
The script runs a mixed prompt set in the US, then reports product presence, merchant presence across offers, discount rate, and whether your places and hotels are listed.
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 noise cancelling headphones for commuting",
"best coffee shops to work from in Austin",
"quiet hotels near downtown Austin",
]
SAMPLES = 5
DAY = date.today().isoformat()
MY_PRODUCT = "quiet 700"
MY_MERCHANT = "sample electronics"
MY_PLACES = {"example coffee roasters", "sample hotel downtown"}
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": "PERPLEXITY",
"payload": {"prompt": prompt, "country": "US"},
"idempotencyKey": f"austin-shop-{p}-s{s}-{DAY}",
}
for p, prompt in enumerate(PROMPTS)
for s in range(SAMPLES)
]
answers = run_tasks(tasks)
with_cards = with_locations = 0
product_hits = merchant_hits = 0
merchants = Counter()
place_hits = Counter()
all_products = []
for result in answers:
products = [p for card in result.get("shopping_cards") or [] for p in card.get("products", [])]
if products:
with_cards += 1
all_products += products
product_hits += any(MY_PRODUCT in p.get("title", "").lower() for p in products)
sellers = {(o.get("merchant_name") or "").lower() for p in products for o in p.get("offers", [])}
sellers |= {(p.get("merchant") or "").lower() for p in products}
sellers.discard("")
merchants.update(sellers)
merchant_hits += MY_MERCHANT in sellers
locations = (result.get("places") or []) + (result.get("hotels") or [])
if locations:
with_locations += 1
place_hits.update({loc["name"].lower() for loc in locations if loc.get("name")})
on_sale = sum(p.get("original_price") is not None for p in all_products)
print(f"cards in {with_cards}/{len(answers)} answers; places or hotels in {with_locations}")
if with_cards:
print(f"product presence {product_hits / with_cards:.0%} merchant presence {merchant_hits / with_cards:.0%}")
print("top merchants:", [(m, f"{n / with_cards:.0%}") for m, n in merchants.most_common(5)])
print(f"products on sale: {on_sale}/{len(all_products)}")
for name in MY_PLACES:
print(f"{name:30} listed in {place_hits[name]}/{with_locations or 1} answers with locations")
Presence rates are divided by answers that contained the block, so a prompt that stops showing cards does not read as lost presence; report the block rate next to them. Split prompts by intent in a full study.
Pitfalls
Products use imageUrls and numReviews; offers and hotels use snake_case. Code that assumes one convention silently reads None.
price, original_price and display_price are formatted text. Convert them per market, and handle currency before comparing prices across countries.
original_price is null, not missing, when an item is not on sale. Test with is not None.
Places and hotels have no ID field. Match on a normalized name plus lat/lng or address so two branches of the same chain do not merge.
Any of the three blocks can be empty or missing for the same prompt across runs, and places and hotels differ by market. Take several samples in each market you report on; state-level AI answers covers the setup.
Related
- Perplexity engine guide: sync and async calls, webhooks, geo targeting and billing.
- Perplexity sources and Perplexity media.
- Shopping on ChatGPT, and place cards on Gemini.
- Local rank tracking.
Questions
How do I get Perplexity shopping cards from the API?
They are not returned today: a free perplexity.ai answer shows no shopping cards, so result.shopping_cards is absent. result.places holds the places on Perplexity's map, hotels included, with no include flag to set.
What do Perplexity shopping, place and hotel results cost?
Nothing extra. Every Perplexity request costs $0.0015, whether or not the answer contains shopping_cards, places or hotels.
Why do Perplexity product fields mix camelCase and snake_case?
Products use imageUrls and numReviews, while offers use merchant_name and product_name, and both use original_price. Parse the names exactly as written.
What is the difference between places and hotels?
Perplexity lists hotels on the same map as other places, so they come back in places, each with name, url, rating, num_reviews, address, phone, description, lat, lng, categories, map_url and images. hotels is not returned today.
How is original_price used on Perplexity products?
original_price is the price before a discount and is null when the product or offer is not on sale, so a non-null value marks a discounted listing.