Geo-scoped cheapest-product search. Pass a user's location and a product filter; get back every store within radius that carries the product, sorted by distance ascending then price ascending. The 'where can I buy this for the lowest price near me' workflow.
/v1/prices?near={lat,lng}&radius_mi={r}$0.01source_tier ≠ "specific" before sorting. Regional/chain fallback rows give you a directional price for a chain that hasn't been re-scraped, but ranking them as if they were verified prices at a specific nearby store will produce phantom-cheapest results.| Name | Type | Description |
|---|---|---|
q | string | Trigram text search on product name + brand. Used when the user typed a query. |
barcode | string | UPC or EAN, 6-32 digits. Used when scanning. |
product_id | uuid | Single canonical product UUID. Used when you've already resolved the catalog entry. |
product_ids | string | Comma-separated UUIDs (cap 50). For comparing multiple variants of a product across nearby stores in one call. |
| Name | Type | Description |
|---|---|---|
nearrequired | string | Caller location as lat,lng, e.g. 40.6892,-73.9942. Both values required. |
radius_mi | numberdefault: 10 | Search radius in miles. Range 0.1-50. Pulls every source store whose location is within radius. |
curl 'https://api.mainmarket.com/v1/prices?barcode=00016000275287&near=40.6892,-73.9942&radius_mi=2&limit=20' \
-H "Authorization: Bearer mm_live_..."{
"count": 4,
"results": [
{
"product_id": "9d4e1c80-78a3-4a6d-9b1f-2cdb3d0c7e90",
"name": "Cheerios Cereal, 18 oz",
"store_id": "ee44...",
"store_name": "ALDI Brooklyn 4th Ave",
"chain_slug": "aldi",
"price": 3.79,
"regular_price": 3.79,
"is_on_sale": false,
"source_tier": "specific",
"source_distance_mi": 0.0,
"pricing_scope": "per_store",
"distance_mi": 0.41
},
{
"product_id": "9d4e1c80-78a3-4a6d-9b1f-2cdb3d0c7e90",
"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,
"source_tier": "specific",
"pricing_scope": "per_store",
"distance_mi": 0.62
}
]
}With ?near= set, the API sorts results by:
distance_mi ASC — closest source store firstprice ASC — within the same distance bucket, cheaper winsThat's the natural "Nearby Prices" ordering for a product-detail screen — closest first, then cheapest. If you want pure cheapest-first regardless of distance, re-sort client-side after filtering to source_tier = "specific".
| Name | Type | Description |
|---|---|---|
distance_mi | number | null | User-to-store distance. Populated only when ?near= was supplied. This is what you display in the UI. |
source_distance_mi | number | Source-store-to-requested-store distance. Always present. 0.0 when source_tier='specific'. Used to caveat fallback rows ('approx — based on a sibling 8mi away'). |
Add ?chain=... to scope to one banner. Useful for loyalty-app flows ("show me Wegmans prices nearby for this product") or to compute a chain-specific cheapest-nearby.
curl 'https://api.mainmarket.com/v1/prices?barcode=00016000275287&chain=heb&near=29.7604,-95.3698&radius_mi=10' \
-H "Authorization: Bearer mm_live_..."chain=heb&near=40.6,-73.9 in NYC, you'll get an empty result — there are no H-E-B stores there.Cache-Control: public, max-age=30, stale-while-revalidate=120. Prices change roughly hourly; the short TTL keeps typeahead snappy without showing stale rungs.| Name | Type | Description |
|---|---|---|
422 | Unprocessable Entity | Missing product or geo filter, malformed near=, lat/lng out of range, or radius_mi out of 0.1-50. |
402 | Payment Required | Paid route — no payment proof. |