---
title: "Batch product search"
method: POST
path: "/v1/products/search"
tags: ["Products"]
---

# Batch product search

`POST /v1/products/search`

## Request

- Base URL: `https://api.openstock.sh`
- URL: `https://api.openstock.sh/v1/products/search`
- Auth: HTTP bearer

Find live listings for a product by name: current price, regular price, image and the store’s product page, for up to 10 queries per request. Each listing names its store; when the store is in the Checkmate network, `merchant.id` is the Checkmate merchant ID to pass to /v1/merchants/codes and `activation_url` is an attributed link that lands on the product page. Listings come from a product feed refreshed roughly daily (see `updated_at`); confirm the price at the store before promising it. Size and colour variants of one product at one store are collapsed to one listing, an in-stock one when there is one.

## Request body

- ProductSearchRequest
  - `queries` ProductSearchQuery[], required: Batch of product searches (1–10); query_id unique within the batch. Each query returns a result with an `items` array, empty when nothing matched.
    - `query_id` string, required: Caller-supplied opaque identifier correlated back in results
    - `query` string, required: What the shopper is looking for, matched against product names: a brand and model ("Hoka Clifton 10"), a brand and category ("Brooklinen sheets"), or a product name. Not a merchant name on its own.
    - `store` string: The brand’s own store, by name, when the shopper wants it from the source: that store’s listings come first, then other stores selling the product. Use the name the store trades under, with its own capitalisation ("Hill House Home", "HOKA"); the match is exact. Without it, results are in the feed’s relevance order, where resellers with short listing names often outrank the brand’s own site.
    - `limit` integer: Maximum number of listings to return for this query, in the feed’s relevance order. Defaults to 5.
    - `currency` string: ISO 4217 currency of the listings to return. Defaults to USD.
    - `on_sale` boolean: When true, only listings currently priced below their regular price are returned.

## Response `200`

Per-query results in request order. Each result has an `items` array of listings in the feed’s relevance order; empty when nothing matched.

- ProductSearchResponse
  - `results` ProductSearchResult[], required
    - `query_id` string, required
    - `items` ProductSearchItem[], required: Listings matching the query in the feed’s relevance order, at most `limit`. Size and colour variants of one product at one store are collapsed to one listing, an in-stock one when there is one. Empty when nothing matched.
      - `id` string, required: Listing identifier in the product feed; stable for a store’s listing
      - `name` string, required: Product name as listed by the store
      - `brand` string, nullable, required: Brand as listed by the store, or null when the feed does not carry one
      - `image_url` string, nullable, required: Primary product image hosted by the store or its CDN, or null when none is listed. Hotlink it or proxy it; it is not hosted by OpenStock.
      - `url` string, required: The store’s own product page. Unattributed: prefer `activation_url` when it is set.
      - `price` number, required: Current price in `currency` major units (dollars, not cents), as last refreshed from the store feed. See `updated_at` for freshness.
      - `regular_price` number, nullable, required: The store’s regular price before any sale, in `currency` major units; equal to `price` when the listing is not on sale, null when the feed does not report one.
      - `currency` string, required: ISO 4217 currency of `price` and `regular_price`
      - `on_sale` boolean, required: Whether the store marks the listing as on sale; when the feed does not say, true when `price` is below `regular_price`.
      - `discount_percent` number, nullable, required: Percentage saving against `regular_price` as the feed reports it, or null when it reports none. This is the store’s own markdown, not a Checkmate code.
      - `availability` 'in_stock' | 'out_of_stock' | 'preorder' | 'backorder' | 'unknown', required: Stock status at the last feed refresh. `unknown` when the store does not report it.
      - `merchant` ProductMerchant, required
        - `id` string, nullable, required: Checkmate merchant ID of the store selling this listing, when the store is in the network. Pass it to /v1/merchants/codes for codes and cashback at that store. Null when the store is not in the network; the listing is still real.
        - `name` string, required: Store name as reported by the product feed
        - `domain` string, required: Hostname of the product page, without `www.`
      - `activation_url` string, nullable, required: Checkmate-attributed link to this product page, so the shopper earns any cashback the store offers (see /v1/merchants/codes). Lands on the product page when the store’s network supports deep links, otherwise on the store’s attributed landing page. Null when the store is not in the network; use `url` then.
      - `updated_at` string, nullable, required: ISO 8601 UTC time the feed last refreshed this listing’s price and availability, or null when not reported. Prices are refreshed roughly daily; confirm at the store before promising a price.

## Other responses

- `400`: Invalid request body or query cap exceeded (max 10 queries)
- `401`: Missing or invalid API key
- `503`: The product feed did not answer (outage or rate limit). Nothing in the batch was answered; retry the whole request with backoff.

## Changes

- **2026-10-27** `ade57114a3c9`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/openstock/apis/openstock-api/changes/v1/products/search/post.md)

---

[API](https://skmtc.dev/openstock/apis/openstock-api.md) - [All operations](https://skmtc.dev/openstock/apis/openstock-api/llms.txt) - [OpenAPI document](https://skmtc.dev/openstock/apis/openstock-api/revisions/e3af2bb31234?raw)
