Pull every store's current price for a single product (identified by UPC, EAN, or canonical product_id). The classic 'price intelligence' query — useful for tracking a SKU nationally, computing chain-level averages, or finding the cheapest banner that carries a product.
/v1/prices?barcode={upc}$0.01/v1/prices with a product filter and no store filter — the response fans out across every store the API has a recent price for. See Search prices for the full parameter reference; this page focuses on the UPC-fanout workflow.| Name | Type | Description |
|---|---|---|
barcode | string | UPC or EAN, 6-32 digits. Matches both raw upc and zero-padded upc_normalized — pass either format. Most common path when you have a scanned barcode. |
product_id | uuid | MainMarket canonical product UUID. Stable forever; UPCs can be reassigned. |
product_ids | string | Comma-separated UUIDs for multi-SKU comparison (e.g. all variants of a brand). Cap 50. |
| Name | Type | Description |
|---|---|---|
chain | string | Limit to one chain slug (e.g. wegmans, heb, kroger). Returns one row per store within that chain. |
state | string | Two-letter state code. Useful for regional comparisons. |
metro | string | Metro key (e.g. nyc, sf, dallas). |
near | string | Geo filter as lat,lng. Pair with radius_mi for "all stores within X miles that carry this product." |
on_sale | boolean | When true, returns only rows where is_on_sale = true. Quick way to find every banner currently running a promo on a SKU. |
limit | integerdefault: 50 | Max rows (1-500). Bump for nationwide queries. |
Resolve the UPC once, then pull a representative price per chain. Useful for building a "cheapest banner for this product" widget.
# Fan out to all stores nationwide that carry this UPC
curl 'https://api.mainmarket.com/v1/prices?barcode=00016000275287&limit=500' \
-H "Authorization: Bearer mm_live_..."Find every banner currently running a promo on a UPC. Pair with a daily cron to catch new sales as they go live.
curl 'https://api.mainmarket.com/v1/prices?barcode=00016000275287&on_sale=true&limit=500' \
-H "Authorization: Bearer mm_live_..."{
"count": 47,
"results": [
{
"product_id": "9d4e1c80-78a3-4a6d-9b1f-2cdb3d0c7e90",
"upc": "00016000275287",
"name": "Cheerios Cereal, 18 oz",
"store_id": "8c1a4d1e-30a7-4d92-9e1c-1cb43c6f2e10",
"store_name": "Wegmans Brooklyn",
"chain_slug": "wegmans",
"price": 4.99,
"regular_price": 5.49,
"sale_price": 4.99,
"is_on_sale": true,
"unit_price": 0.28,
"unit_price_uom": "oz",
"source_tier": "specific",
"source_distance_mi": 0.0,
"pricing_scope": "per_store"
},
{
"product_id": "9d4e1c80-78a3-4a6d-9b1f-2cdb3d0c7e90",
"upc": "00016000275287",
"name": "Cheerios Cereal, 18 oz",
"store_id": "ee44...",
"store_name": "HEB Houston Heights",
"chain_slug": "heb",
"price": 4.49,
"regular_price": 4.49,
"sale_price": null,
"is_on_sale": false,
"source_tier": "specific",
"pricing_scope": "per_store"
}
]
}product_id if your UI only wants one.source_tier = "specific". When you don't pass a store filter, every row should be tier=specific (the API doesn't fall back across stores when no store was asked for). But sanity-check anyway — mixed-tier rows in a national rollup will skew the median.price fields should be filtered before computing aggregates.pricing_scope = "chain_level" (Lidl, Trader Joe's, Aldi), every store row carries the same price — collapse to one row per chain to avoid weighting them by store count.| Name | Type | Description |
|---|---|---|
422 | Unprocessable Entity | No product filter (you must pass barcode, product_id, or product_ids). |
402 | Payment Required | Paid route — no payment proof. |