shelfatlas-com Stores API

The Stores API from shelfatlas-com — 5 operation(s) for stores.

Operations 5

GET /api/v1/public/catalog/stores List stores, or find the nearest to a coordinate #
GET /api/v1/public/catalog/stores/{id} Get a store by id #
GET /api/v1/public/catalog/stores/{id}/stock Community shelf-stock signal for one store #
GET /api/v1/public/catalog/stores/{id}/local-prices Local (crowd-observed) shelf prices for one store (SA-0162) #
GET /api/v1/public/catalog/local-prices Local (crowd-observed) shelf prices near a point (SA-0164) #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/shelfatlas-com:shelfatlas-com-stores-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

shelfatlas-com-stores-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: ShelfAtlas Catalog Stores API
  version: 1.0.0
  description: 'Read access to normalised Danish retail catalog data — products, offers, stores, and chains. Authenticate with a Bearer `sa_live_` API key. This document is the contract the official ShelfAtlas SDKs conform to. Data coverage: the offer feed covers every major Danish chain, but the curated product catalog is smaller and actively expanding — an empty data array from /products/{id}/offers or /price-history means no offers are linked to that product yet, not an error.'
  termsOfService: https://shelfatlas.com/legal/terms
servers:
- url: https://api.shelfatlas.com
  description: Production
security:
- bearerAuth: []
tags:
- name: Stores
paths:
  /api/v1/public/catalog/stores:
    get:
      operationId: listStores
      summary: List stores, or find the nearest to a coordinate
      description: 'Provide lat+lng (optionally radius_km) for nearest-first ordering, or chain_slug to list a chain. Not cursor-paginated: geo mode is distance-ordered and list mode caps at limit=500 (covers the full store set today) — pagination.cursor is always null here and does not mean "last page reached" the way it does on /products and /offers.'
      tags:
      - Stores
      parameters:
      - name: chain_slug
        in: query
        required: false
        schema:
          type: string
          pattern: ^[a-z0-9-]+$
          minLength: 1
          maxLength: 64
        description: Restrict to a single chain by slug.
      - name: postal_code
        in: query
        required: false
        schema:
          type: string
          pattern: ^\d{1,4}$
        description: Danish postcode prefix (1–4 digits); `21` matches all of 21xx.
      - name: lat
        in: query
        required: false
        schema:
          type: number
          minimum: -90
          maximum: 90
        description: Latitude; must be paired with lng.
      - name: lng
        in: query
        required: false
        schema:
          type: number
          minimum: -180
          maximum: 180
        description: Longitude; must be paired with lat.
      - name: radius_km
        in: query
        required: false
        schema:
          type: number
          exclusiveMinimum: 0
          maximum: 500
        description: Optional radius for nearest search; requires lat/lng.
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 500
          default: 50
        description: Page size (1–500, default 50).
      responses:
        '200':
          description: Stores (nearest-first when a coordinate is given).
          content:
            application/json:
              schema:
                type: object
                required:
                - ok
                - data
                additionalProperties: false
                properties:
                  ok:
                    type: boolean
                    enum:
                    - true
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Store'
                  pagination:
                    type: object
                    additionalProperties: false
                    properties:
                      limit:
                        type: integer
                        description: The effective page size.
                      cursor:
                        type:
                        - string
                        - 'null'
                        description: Opaque cursor for the next page; null on the last page.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /api/v1/public/catalog/stores/{id}:
    get:
      operationId: getStore
      summary: Get a store by id
      tags:
      - Stores
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The store.
          content:
            application/json:
              schema:
                type: object
                required:
                - ok
                - data
                additionalProperties: false
                properties:
                  ok:
                    type: boolean
                    enum:
                    - true
                  data:
                    $ref: '#/components/schemas/Store'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /api/v1/public/catalog/stores/{id}/stock:
    get:
      operationId: getStoreStock
      summary: Community shelf-stock signal for one store
      description: 'The latest approved shelf observation per curated product at this store: status, when it was observed, its validity window, and (C4) how many independent reporters confirmed the status within the last 24 hours. (SA-0022) `confidence` says how much to trust that status: `confirmed` (a partner report, or 2+ agreeing sources at combined weight >= 1.0) or `reported` (a single established source, or 2+ lower-trust agreeing sources); `trusted: false` flags a status backed only by new/unestablished sources (e.g. a first-ever report). Empty `products` means no reports yet, not an error.'
      tags:
      - Stores
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The store's current stock signal.
          content:
            application/json:
              schema:
                type: object
                required:
                - ok
                - data
                additionalProperties: false
                properties:
                  ok:
                    type: boolean
                    enum:
                    - true
                  data:
                    $ref: '#/components/schemas/StoreStock'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /api/v1/public/catalog/stores/{id}/local-prices:
    get:
      operationId: getStoreLocalPrices
      summary: Local (crowd-observed) shelf prices for one store (SA-0162)
      description: 'The latest APPROVED observation per product at this store that carries a price — kept data-separate from CatalogOffer: this is never a discount/leaflet claim, only what a person saw on the shelf. A price-less observation never appears here (see `/stores/{id}/stock`). `is_price_outlier` rows are excluded. Open to any catalog key — no coverage-package grant required, unlike `/observed-prices`; tier gating on freshness is a pricing decision not yet built. RAW facts only: `observedAt` carries no server-computed age (the response is CDN-cached).'
      tags:
      - Stores
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The store's local observed prices.
          content:
            application/json:
              schema:
                type: object
                required:
                - ok
                - data
                additionalProperties: false
                properties:
                  ok:
                    type: boolean
                    enum:
                    - true
                  data:
                    $ref: '#/components/schemas/StoreLocalPrices'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /api/v1/public/catalog/local-prices:
    get:
      operationId: listNearbyLocalPrices
      summary: Local (crowd-observed) shelf prices near a point (SA-0164)
      description: 'The radius-wide sibling of `/stores/{id}/local-prices`: the latest APPROVED, non-outlier observation per (store, product) that carries a price, at any active store within radius_km of lat/lng — newest first. Same data-separation as the per-store route: never a CatalogOffer, never mixed with discount prices. `summary` gives the same-radius proof-line counts: `today` (since local Copenhagen midnight) prices/stores, `week` (rolling 7 days) prices/contributors — contributors is a COUNT only, never a reporter id. Open to any catalog key — no coverage-package grant required. RAW facts only: `observedAt` carries no server-computed age (the response is CDN-cached).'
      tags:
      - Stores
      parameters:
      - name: lat
        in: query
        required: true
        schema:
          type: number
          minimum: -90
          maximum: 90
        description: Latitude.
      - name: lng
        in: query
        required: true
        schema:
          type: number
          minimum: -180
          maximum: 180
        description: Longitude.
      - name: radius_km
        in: query
        required: false
        schema:
          type: number
          minimum: 1
          maximum: 100
          default: 15
        description: Search radius in km (1–100, default 15).
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 20
        description: Page size (1–100, default 20).
      responses:
        '200':
          description: Nearby local observed prices plus the radius summary.
          content:
            application/json:
              schema:
                type: object
                required:
                - ok
                - data
                additionalProperties: false
                properties:
                  ok:
                    type: boolean
                    enum:
                    - true
                  data:
                    $ref: '#/components/schemas/NearbyLocalPrices'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  responses:
    RateLimited:
      description: Rate limit or monthly limit reached (`rate_limited` / `monthly_limit_reached`).
      headers:
        Retry-After:
          description: Seconds to wait before retrying (present on `rate_limited`).
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource not found (`not_found`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ServiceUnavailable:
      description: Temporary backend failure (`service_unavailable`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: Invalid parameters (`invalid_params`, `incompatible_params`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing, invalid, or revoked API key (`missing_api_key` / `invalid_api_key` / `revoked_api_key`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    StoreStock:
      type: object
      additionalProperties: false
      required:
      - storeId
      - products
      properties:
        storeId:
          type: string
          format: uuid
        products:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
            - ean
            - productId
            - stockStatus
            - observedAt
            - validUntil
            - confirmations
            properties:
              ean:
                type: string
              productId:
                type:
                - string
                - 'null'
                format: uuid
              stockStatus:
                type: string
                enum:
                - in_stock
                - low_stock
                - out_of_stock
              observedAt:
                type: string
                format: date-time
              validUntil:
                type:
                - string
                - 'null'
                format: date-time
                description: When the signal stops applying; a consumer must treat an expired row as "not reported".
              confirmations:
                type: integer
                minimum: 1
                description: Independent reporters behind the status within the last 24h (C4 trust layer) — the same count `confidence` was derived from.
              confidence:
                type: string
                enum:
                - confirmed
                - reported
                description: 'How much this status is trusted (SA-0022): confirmed = a partner report, or 2+ agreeing sources reaching combined weight 1.0; reported = a single established source (5+ approved reports) or 2+ agreeing lower-trust sources. A lone new/low-trust report is surfaced as reported with trusted=false (SA-0275).'
              trusted:
                type: boolean
                description: 'SA-0275: false when only new/unestablished sources (no partner, none with 5+ approved reports) back the status — e.g. one first-ever report; render it as "one report". Optional for pre-SA-0275 servers.'
    Error:
      type: object
      additionalProperties: true
      required:
      - error
      properties:
        error:
          type: string
          description: Machine-readable error code.
        message:
          type: string
        detail:
          type: string
        retry_after_sec:
          type: integer
          description: Seconds to wait before retrying (429).
        plan:
          type: string
          description: Caller's resolved plan id (present on monthly_limit_reached).
        limit:
          type: integer
          description: The plan monthly request quota (present on monthly_limit_reached).
        used:
          type: integer
          description: Requests already spent this UTC month (present on monthly_limit_reached).
        resets_at:
          type: string
          format: date-time
          description: When the quota resets — next UTC month start (present on monthly_limit_reached).
        next_plan:
          type: object
          nullable: true
          description: The cheapest plan with a larger quota, or null at the top of the ladder.
          properties:
            id:
              type: string
            name:
              type: string
            monthly_price_dkk:
              type: integer
            monthly_request_quota:
              type: integer
        plans_url:
          type: string
          format: uri
          description: Public pricing page — no sign-in required.
        upgrade_url:
          type: string
          format: uri
    NearbyLocalPrices:
      type: object
      additionalProperties: false
      required:
      - items
      - summary
      properties:
        items:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
            - ean
            - label
            - priceCents
            - stockStatus
            - observedAt
            - trust
            - store
            - distanceKm
            properties:
              ean:
                type: string
              label:
                type:
                - string
                - 'null'
                description: Curated product name, or null when the ean is not a known product.
              priceCents:
                type: integer
                description: Shelf price in øre (DKK×100) as observed.
              stockStatus:
                type:
                - string
                - 'null'
                enum:
                - in_stock
                - low_stock
                - out_of_stock
                - null
              observedAt:
                type: string
                format: date-time
                description: When the shelf was seen — a raw fact, no server-computed age.
              trust:
                type: string
                enum:
                - partner
                - established
                - new
                description: Source class — never the reporter identity.
              store:
                type: object
                additionalProperties: false
                required:
                - id
                - name
                - chainSlug
                - chainName
                properties:
                  id:
                    type: string
                    format: uuid
                  name:
                    type: string
                  chainSlug:
                    type: string
                  chainName:
                    type: string
              distanceKm:
                type: number
                description: Great-circle distance from lat/lng, rounded to 0.1 km.
        summary:
          type: object
          additionalProperties: false
          required:
          - today
          - week
          description: Same-radius proof-line counts (design 2026-09-23 §1, §2 layer 5).
          properties:
            today:
              type: object
              additionalProperties: false
              required:
              - prices
              - stores
              description: Since local Copenhagen midnight.
              properties:
                prices:
                  type: integer
                stores:
                  type: integer
                  description: Distinct stores with at least one price seen today.
            week:
              type: object
              additionalProperties: false
              required:
              - prices
              - contributors
              description: Rolling last 7 days.
              properties:
                prices:
                  type: integer
                contributors:
                  type: integer
                  description: Distinct contributors (count only — never a reporter id).
    StoreLocalPrices:
      type: object
      additionalProperties: false
      required:
      - storeId
      - store
      - prices
      properties:
        storeId:
          type: string
          format: uuid
        store:
          type: object
          additionalProperties: false
          required:
          - id
          - name
          - chainSlug
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
            chainSlug:
              type: string
        prices:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
            - ean
            - label
            - priceCents
            - stockStatus
            - observedAt
            - trust
            properties:
              ean:
                type: string
              label:
                type:
                - string
                - 'null'
                description: Curated product name, or null when the ean is not a known product.
              priceCents:
                type: integer
                description: Shelf price in øre (DKK×100) as observed.
              stockStatus:
                type:
                - string
                - 'null'
                enum:
                - in_stock
                - low_stock
                - out_of_stock
                - null
              observedAt:
                type: string
                format: date-time
                description: When the shelf was seen — a raw fact, no server-computed age.
              trust:
                type: string
                enum:
                - partner
                - established
                - new
                description: Source class — never the reporter identity.
    Store:
      type: object
      additionalProperties: false
      required:
      - id
      - name
      - chainSlug
      - chainName
      - address
      - city
      - lat
      - lng
      - timezone
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        chainSlug:
          type: string
        chainName:
          type: string
        address:
          type: string
        city:
          type: string
        postalCode:
          type:
          - string
          - 'null'
        lat:
          type: number
        lng:
          type: number
        openingHours:
          type:
          - object
          - 'null'
          description: Weekly opening hours keyed mon..sun; each an array of {open,close} "HH:MM" intervals (empty = closed).
          additionalProperties:
            type: array
            items:
              type: object
              required:
              - open
              - close
              properties:
                open:
                  type: string
                close:
                  type: string
        timezone:
          type: string
        topReporterAlias:
          type:
          - string
          - 'null'
          description: '"Claim to fame" (2026-08-20): the store''s top approved-alias reporter over the rolling 30-day window (min 5 approved observations), or null when there is no eligible leader or the leader has no admin-approved alias.'
        topReporterStandInNumber:
          type:
          - number
          - 'null'
          description: 'Pending stand-in name (2026-08-21): a deterministic number (1..9999) for the qualifying leader when their alias is awaiting approval or re-pick (pending/rejected), so a chosen-but-unapproved name never appears publicly. Never set at the same time as topReporterAlias, and null when there is no eligible leader or the leader has no alias row at all.'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'A ShelfAtlas API key: `Authorization: Bearer sa_live_<key>`.'
    deviceTokenAuth:
      type: apiKey
      in: header
      name: X-Device-Token
      description: 'Anonymous per-device identity for POST /api/v1/observations: a UUID the client generates and keeps locally. Any UUID version is accepted — the check is purely the 8-4-4-4-12 hex-digit shape.'