Every public route in the MainMarket API. Discovery endpoints (stores, chains) are free; data endpoints (prices, products, coupons, indices) are $0.01 per call, settled via x402 wallet payment, internal token, or B2B API key.
All routes live under https://api.mainmarket.com and return JSON. See Authentication for how requests are charged and openapi.json for the machine-readable spec.
Discovery routes return store and chain metadata. No payment, no token. CDN-cached for 60 seconds. Use them to build a search interface or pre-flight a paid call.
| Method | Path | Reference |
|---|---|---|
| GET | /v1/stores | List stores |
| GET | /v1/stores/{id} | Get a store |
| GET | /v1/stores/{id}?include=places_enrichment | Store sentiment & enrichment |
| GET | /v1/chains | List chains |
| GET | /v1/chains/{slug} | Get a chain |
| GET | /health | Health + discovery routes |
Data routes return live shelf prices, coupons, aisle layouts, list resolutions, and published indices. Each successful response (HTTP 200) deducts one cent from the configured payment source. Errors (4xx / 5xx) are not charged.
| Method | Path | Reference |
|---|---|---|
| GET | /v1/products | Catalog search |
| GET | /v1/products?product_id=... | Get a product |
| GET | /v1/products/{id}/coupons | Coupons for product |
| GET | /v1/prices | Search prices |
| GET | /v1/coupons | List coupons |
| GET | /v1/coupons/{id} | Get a coupon |
| GET | /v1/coupons/{id}/savings | Coupon savings |
| GET | /v1/stores/{id}/coupons | Store coupons |
| GET | /v1/stores/{id}/aisles | Store aisles |
| GET | /v1/chains/{slug}/aisles | Chain aisles |
| POST | /v1/chains/{slug}/resolve-list | Resolve a list |
| GET | /v1/indices/{slug} | Published indices |
All errors return a JSON body of the form { "detail": <string|object> } with an appropriate HTTP status code. Pydantic validation errors (422) include a structured detail array pointing at the offending field.
| Code | Name | When you'll see it |
|---|---|---|
200 | OK | Successful response. Body matches the documented schema for the route. |
304 | Not Modified | Returned by /v1/coupons when the client's If-None-Match header matches the current ETag. No body. |
400 | Bad Request | Malformed query parameter (e.g. lat/lng outside valid range, slug not lowercase). |
401 | Unauthorized | Missing or invalid Authorization header on an internal or B2B route. |
402 | Payment Required | Paid route called without payment proof (x402 wallet header, internal token, or B2B key). |
404 | Not Found | store_id, coupon_id, or index slug does not exist. |
422 | Unprocessable Entity | Pydantic validation failure — wrong type, missing required filter, period not 'YYYY-MM'. |
429 | Too Many Requests | Rate limit exceeded. Future. Not enforced in v1. |
500 | Internal Server Error | Unexpected server fault. Retry with exponential backoff; report persistent occurrences. |
503 | Service Unavailable | Returned by /health when the database ping fails. Indicates upstream Postgres or pooler issue. |
{
"detail": "Provide at least one filter (q, barcode, product_id, product_ids, store_id, store_ids, chain, metro, state, or near)"
}unit_price_uom = "oz").{ count, results } or { data, meta } — they never return bare arrays.Cache-Control, ETag) are honored where present. The /v1/coupons route supports If-None-Match for 304 short-circuits.