Frequency-ranked aisles aggregated across every store in the chain. Produces a stable per-chain aisle vocabulary suitable for mapping any store's price feed onto a single picker — or as a fallback when a specific store has too-low coverage to use its own aisles.
/v1/chains/{slug}/aisles$0.01| Name | Type | Description |
|---|---|---|
slugrequired | string | Chain slug, e.g. wegmans. |
| Name | Type | Description |
|---|---|---|
limit | integerdefault: 20 | Max aisles to return. Range 1–100. |
curl 'https://api.mainmarket.com/v1/chains/wegmans/aisles?limit=25'{
"chain_slug": "wegmans",
"source": "chain",
"coverage": 0.81,
"aisles": [
"Aisle 1",
"Aisle 2",
"Aisle 3",
"Produce",
"Bakery",
"Deli",
"Meat",
"Seafood",
"Dairy",
"Frozen"
],
"aisle_counts": {
"Aisle 1": 18420,
"Aisle 2": 17905,
"Produce": 9120
}
}| Name | Type | Description |
|---|---|---|
chain_slug | string | Echo of the requested slug. |
source | string | Always chain for this endpoint, except generic when chain coverage is below the threshold. |
coverage | number | Fraction of sampled SPP rows that carried a non-null aisle (0.0–1.0). |
aisles | string[] | Normalized aisle names ordered by frequency descending. |
aisle_counts | object | Map of aisle name → product count from the sample. |
aisle_counts to gauge confidence — top entries with thousands of product counts are stable; tail entries with a few dozen are noisy.Each call samples up to 50,000 rows from the chain's price catalog, runs them through the normalization pipeline below, then ranks the surviving aisle names by frequency:
"Aisle 12", "AISLE 12", "aisle 12" all collapse to "12" for matching, then get re-prefixed to "Aisle 12" on output."Aisle 12, Shelf 3", "Aisle 12 - Shelf 3", and "Aisle 12 / Shelf 3" all collapse to "Aisle 12" — shelf-level granularity isn't useful at chain scope."12" after stripping is re-prefixed to "Aisle 12" for display. Word aisles like "Produce", "Bakery", "Deli" stay un-prefixed.limit caps the returned list (default 20, max 100).The classic use: when a user shops the same chain across many stores, you want the aisle list to look the same regardless of which store they're currently in. Pull this once per chain, cache it, then use it as the canonical vocabulary when joining to /v1/prices rows with ?normalize_aisles=true.
import httpx
# Step 1: pull the chain vocabulary once (cache for the session)
chain_aisles = httpx.get(
"https://api.mainmarket.com/v1/chains/wegmans/aisles",
params={"limit": 50},
headers={"Authorization": "Bearer mm_live_..."},
).json()
order = {name: i for i, name in enumerate(chain_aisles["aisles"])}
# Step 2: for any store in the chain, sort prices by chain-level aisle order
prices = httpx.get(
"https://api.mainmarket.com/v1/prices",
params={
"store_id": "8c1a4d1e-30a7-4d92-9e1c-1cb43c6f2e10",
"product_ids": "9d4e...,a1b2...,c3d4...",
"normalize_aisles": True,
},
headers={"Authorization": "Bearer mm_live_..."},
).json()
# Sort by chain aisle order — items with unknown aisles sink to the bottom
prices["results"].sort(key=lambda r: order.get(r.get("aisle"), len(order)))
for row in prices["results"]:
aisle = row.get("aisle") or "?"
print(f" {aisle:>10} {row['name']} ${row['price']:.2f}")/v1/stores/{id}/aisles returns — by design, so a user picker built from one works on the other.source = "generic" is rare here. A chain has to have very low aisle coverage chain-wide for the fallback to fire — usually means the chain doesn't expose aisle data on PDPs at all (e.g. chains that only ship their flyer with prices but no aisle metadata). Coverage will be 0.0 when this happens.aisle_counts for inventory analysis — they're proportional, not absolute. The 50k cap means a chain with 200k SKUs is sampled at 25%.Chain aisle distributions change slowly (a remodel might add or rename aisles once a year). The endpoint sends Cache-Control: public, max-age=3600, stale-while-revalidate=7200 — a one-hour cache. For mobile clients, persist by chain slug and refresh once per session.
?normalize_aisles=true so price rows join cleanly to either aisle vocabulary.| Name | Type | Description |
|---|---|---|
404 | Not Found | Slug is not a known chain. |
422 | Unprocessable Entity | limit out of range. |
402 | Payment Required | Paid route — no payment proof. |