MainMarketAPI Reference
Get Access
Overview
  • Introduction
  • Authentication
  • Errors
Stores
  • List stores
  • Get a store
  • Store sentiment
  • Store coupons
  • Store aisles
Chains
  • List chains
  • Get a chain
  • Chain aisles
  • Resolve a list
Products
  • Catalog search
  • Get a product
  • Coupons for product
Prices
  • Search prices
  • Prices by UPC
  • Prices at a store
  • Cheapest nearby
Coupons
  • List coupons
  • Get a coupon
  • Coupon savings
Indices
  • Published indices
Discovery & meta
  • Discovery routes
  • OpenAPI spec
  • Agent skill spec
Overview
  • Introduction
  • Authentication
  • Errors
Stores
  • List stores
  • Get a store
  • Store sentiment
  • Store coupons
  • Store aisles
Chains
  • List chains
  • Get a chain
  • Chain aisles
  • Resolve a list
Products
  • Catalog search
  • Get a product
  • Coupons for product
Prices
  • Search prices
  • Prices by UPC
  • Prices at a store
  • Cheapest nearby
Coupons
  • List coupons
  • Get a coupon
  • Coupon savings
Indices
  • Published indices
Discovery & meta
  • Discovery routes
  • OpenAPI spec
  • Agent skill spec

List stores

Geo-search grocery stores by latitude/longitude radius, metro, state, or chain slug. Returns one row per matching store with full address, geocode, and chain metadata.

GET/v1/storesFree

Query parameters

NameTypeDescription
latnumberLatitude in decimal degrees, -90 to 90. Pair with lng + radius for a geo search.
lngnumberLongitude in decimal degrees, -180 to 180.
radiusnumberdefault: 5Radius in miles. Only used when lat + lng are both supplied. Range 0.1–50.
chainstringChain slug, e.g. heb, wegmans, lidl. Lowercase.
statestringTwo-letter state code, e.g. NY, TX.
metrostringMetro key, e.g. nyc, sf, dallas.
limitintegerdefault: 100Max rows returned. Range 1–500.
offsetintegerdefault: 0Cursor offset for pagination.
includestringComma-separated opt-in fields: chain_online_config, hours, places_enrichment. Each adds a nested object on every row.
include_countbooleandefault: falseWhen true, runs an extra COUNT(*) pass to populate the top-level count field. Off by default — the count query can't use the geo GIST index and adds ~50% latency. Pass true for paginated B2B feeds that need a total.
ℹ
At least one filter (lat+lng, chain, metro, or state) is recommended. An unfiltered call returns the first limit active stores ordered by id.

Request

Request
curl 'https://api.mainmarket.com/v1/stores?lat=40.7128&lng=-74.0060&radius=5&include=hours'

Response

200 OKjson
{
  "count": 2,
  "stores": [
    {
      "id": "8c1a4d1e-30a7-4d92-9e1c-1cb43c6f2e10",
      "name": "Wegmans Brooklyn",
      "display_name": "Wegmans Brooklyn",
      "banner_name": "Wegmans",
      "web_external_id": "59",
      "chain_name": "Wegmans",
      "chain_slug": "wegmans",
      "chain_logo_url": "https://.../wegmans.png",
      "address": "21 Flushing Ave",
      "address_line_2": null,
      "city": "Brooklyn",
      "state": "NY",
      "zip": "11205",
      "country": "US",
      "lat": 40.6982,
      "lng": -73.9772,
      "metro": "nyc",
      "borough": "brooklyn",
      "neighborhood": "Navy Yard",
      "format": "supermarket",
      "phone": "+17184181100",
      "has_pickup": true,
      "has_delivery": true,
      "is_dark_store": false,
      "is_active": true
    }
  ]
}

StoreResponse fields

NameTypeDescription
iduuidStable canonical store id. Use this everywhere downstream.
namestringInternal name, e.g. 'Wegmans Brooklyn'.
display_namestring | nullCustomer-facing label when distinct from name.
banner_namestring | nullSub-banner inside the chain (e.g. 'Mariano's' under Kroger).
web_external_idstring | nullChain's own store id (used in chain URLs and APIs).
chain_namestringHuman chain name.
chain_slugstringURL-safe chain key. Use with /v1/chains/{slug}.
chain_logo_urlstring | nullPublic CDN URL for the chain logo (PNG, transparent).
addressstringStreet address line 1.
address_line_2string | nullSuite, unit, or floor when present.
city, state, zip, countrystringPostal address. country is always 'US' today.
lat, lngnumberWGS84 decimal degrees.
metrostring | nullMetro region key, e.g. 'nyc'.
borough, neighborhoodstring | nullSub-metro descriptors when geocoded.
formatstring | nullStore format (supermarket, express, dark_store, etc.).
phonestring | nullE.164 phone number.
has_pickup, has_deliverybooleanOnline fulfillment options.
is_dark_storebooleanTrue for fulfillment-only locations not open to walk-ins.
is_activebooleanFalse for stores we know are closed or being decommissioned.
chainobject | nullPresent when include=chain_online_config. Same shape as the ChainResponse documented under GET /v1/chains/{slug}.
hoursarray | nullPresent when include=hours. Up to 7 rows of { day_of_week, open_time, close_time }.
placesobject | nullPresent when include=places_enrichment. SerpAPI-sourced rating, review_count, popular_times, sentiment.

places fields (when include=places_enrichment)

Sourced from SerpAPI's Google Maps engine plus a one-time GPT sentiment pass. Seeded once per store — see places.enriched_at for freshness. Returns null entirely for stores that haven't been seeded yet, or whose SerpAPI match was low-confidence.

NameTypeDescription
ratingnumber | null0.0–5.0, one decimal place (Google rating).
review_countinteger | nullTotal Google reviews.
typical_time_spentstring | nullFree-text, e.g. "People typically spend 15-45 min here".
price_levelstring | null"$" / "$$" / "$$$".
highlightsstring[]Flat de-duped tag list flattened from SerpAPI extensions (e.g. "Curbside pickup", "Wheelchair accessible"). Order preserved — the first 3-5 are typically the most distinctive.
service_optionsobject | nullMap of { key: boolean }, e.g. { in_store_pickup: true, delivery: false }. Render only the truthy keys.
popular_timesobject | null{ current_day, graph }. graph has up to 7 keys (monday..sunday); each is a list of { time, busyness_score (0-100), label }. busyness_score = 0 = closed/no data.
review_topicsobject[]Optional { keyword, mentions } list. Empty array when not returned by Google.
sentimentobject | nullGPT structured-output sentiment: { score, label, summary, axes }. label ∈ Loved | Strong | Mixed | Frustrated. axes always has 5 keys (service / selection / value / cleanliness / operations), each { score, evidence } — score nullable when reviews don't mention that axis.
enriched_atISO8601 string | nullWhen SerpAPI scraped this store. Use as a freshness indicator.
ℹ
Coverage today
Only the 26-store Brooklyn pilot is fully seeded right now. Every other store returns places: null until the production seed (one-time, ~$150 for ~11.5k chain-active stores nationwide) ships. Design your UI to gracefully hide the section when places is null.

Notable behavior

  • Geo search uses PostGIS ST_DWithin over a GIST index — sub-50ms for any radius up to 50 miles. Results are sorted by distance ascending when lat + lng are supplied.
  • include_count=true opts into a second COUNT(*) pass. Off by default because the count can't share the geo GIST index used for the main query — leave off unless you actually paginate.
  • Soft-deleted store ids are filtered out at query time. Use GET /v1/stores/{id} if you need to resolve a stale id.

Errors

NameTypeDescription
400Bad Requestlat or lng outside valid range, or radius outside 0.1–50.
422Unprocessable EntityWrong type for limit/offset; state not 2 letters.
500Internal Server ErrorUnexpected server fault. Retry with backoff.