Skip to content
OmniRetailConnect Docs

Commerce · REST API

Products

Search products, get one product by id and look up a product by variant SKU, with sales, price breaks, SEO, labels and properties.

View as Markdown

Search products

GET /api/v1/products

Needs commerce.read. Equivalent MCP tool: search_products.

At least one of query, collection and brand is required.

Query parameters of GET /products
Name Type Required Description
query string Optional 2 to 100 characters. Name, brand, SKU or EAN; prefixes work; every word must match. May not contain % or _ (422), also not written as HTML entities such as &percnt; or &#37;. Text after a < that looks like the start of a tag is dropped.
status string Optional draft, published or any. Default published. draft and any return unpublished products.
collection string Optional Category id or slug (letters, digits, _, -). Includes subcategories.
brand string Optional Exact brand name, case-insensitive, at most 100 characters.
limit integer Optional 1 to 50, default 20.
cursor string Optional meta.next_cursor of the previous page.
curl
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/products?query=hamer&limit=1" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"

Example values on this page are illustrative: the shop you call returns its own products.

Unpublished products are readable

status=draft and status=any on this endpoint, and GET /products/{id} and GET /skus/{sku} for any product, return unpublished products to every token with commerce.read. This is intended: the API gives operational read access, the same as the MCP server. Do not hand commerce.read tokens to parties who must not see drafts.

The response is a list: data holds the products and meta the paging state.

Example response · 200
{
    "data": [
        {
            "id": 101,
            "sku": "HAM-500",
            "name": "Klauwhamer 500 g",
            "brand": "Hammerwerk",
            "status": "published",
            "price": {
                "min": {
                    "amount_minor": 1495,
                    "currency": "EUR",
                    "formatted": "€ 14,95"
                },
                "max": {
                    "amount_minor": 1895,
                    "currency": "EUR",
                    "formatted": "€ 18,95"
                },
                "tax_inclusive": true
            },
            "variant_count": 2,
            "categories": [
                {
                    "id": 12,
                    "slug": "gereedschap",
                    "name": "Gereedschap"
                },
                {
                    "id": 14,
                    "slug": "handgereedschap",
                    "name": "Handgereedschap"
                }
            ],
            "url": "https://your-shop.example.com/p/klauwhamer-500-g"
        }
    ],
    "meta": {
        "next_cursor": "eyJ2IjoxLCJzIjoic2VhcmNoX3Byb2R1Y3RzIiwiZCI6eyJvZmZzZXQiOjIwfX0.q1w2e3r4t5y6u7i8o9p0as",
        "has_more": true,
        "truncated": false
    }
}
Fields of meta
Field Type Description
next_cursor string|null Pass it back unchanged as cursor for the next page. null on the last page.
has_more boolean Whether more results exist.
truncated boolean true when more results exist beyond result position 500. There is then no next_cursor: narrow the query.
  • Results are ranked (exact SKU or EAN, SKU prefix, name, name or brand) and paged by position, up to position 500.
  • When a SKU or EAN was searched, sku is the matching variant's SKU.
  • No stock is included: use Availability or Get a product.
  • Walk pages with the same filters as the first request: see pagination.
curl
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/products?collection=gereedschap&brand=Hammerwerk&status=any" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"

Get a product

GET /api/v1/products/{id}

Needs commerce.read. Equivalent MCP tool: get_product.

Path parameter of GET /products/{id}
Name Type Required Description
id integer Required The product id: digits only, no leading zero, at most 18 digits. Anything else in that position (abc, 0, 01) is 404.
curl
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/products/101" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"
Example response · 200
{
    "data": {
        "id": 101,
        "name": "Klauwhamer 500 g",
        "status": "published",
        "brand": "Hammerwerk",
        "description": "Klauwhamer met hickory steel en antislip handgreep.",
        "categories": [
            {
                "id": 12,
                "slug": "gereedschap",
                "name": "Gereedschap"
            },
            {
                "id": 14,
                "slug": "handgereedschap",
                "name": "Handgereedschap"
            }
        ],
        "variants": [
            {
                "id": 201,
                "sku": "HAM-500",
                "ean": "8712345678901",
                "price": {
                    "amount": {
                        "amount_minor": 1495,
                        "currency": "EUR",
                        "formatted": "€ 14,95"
                    },
                    "tax_inclusive": true,
                    "tax_class": "Standaard"
                },
                "availability": {
                    "quantity": 14,
                    "purchasable": true,
                    "low_stock": false,
                    "expected_restock_at": null,
                    "source": "local",
                    "status": "ok"
                },
                "weight_g": 620,
                "sales": [],
                "price_breaks": []
            },
            {
                "id": 202,
                "sku": "HAM-700",
                "ean": null,
                "price": {
                    "amount": {
                        "amount_minor": 1895,
                        "currency": "EUR",
                        "formatted": "€ 18,95"
                    },
                    "tax_inclusive": true,
                    "tax_class": "Standaard"
                },
                "availability": {
                    "quantity": 3,
                    "purchasable": true,
                    "low_stock": true,
                    "expected_restock_at": "2026-10-20T00:00:00+02:00",
                    "source": "local",
                    "status": "ok"
                },
                "weight_g": null,
                "sales": [],
                "price_breaks": []
            }
        ],
        "images": [
            {
                "id": 101,
                "url": "https://your-shop.example.com/storage/101/klauwhamer.jpg"
            }
        ],
        "url": "https://your-shop.example.com/p/klauwhamer-500-g",
        "updated_at": "2026-10-05T14:21:07+02:00",
        "slug": "klauwhamer-500-g",
        "seo": {
            "meta_title": null,
            "meta_description": null
        },
        "labels": [],
        "properties": []
    }
}
Fields of a product
Field Type Description
id, name, status, brand mixed Identity and publication state (draft or published). brand is null when the product has no brand.
description string Plain text, at most 2000 characters. An empty string when there is no description (never null).
categories array Objects with id, slug and name.
variants array At most 100 entries with id, sku, ean, price, availability, weight_g, sales and price_breaks. sku and ean can be null.
variants[].sales array The active and planned sales, soonest first, at most 10: {id, price, starts_at, ends_at, active}. price is a tax-inclusive money object and stays the regular price; a sale never changes it. ends_at is null without an end. Set with the sale write.
variants[].price_breaks array The quantity price breaks, by quantity: {min_quantity, price}, tax-inclusive money per unit. Set with the price breaks write.
variants[].price object amount (money), tax_inclusive and tax_class (the name of the tax class, or null). The whole price is null when no price is set. In a product list price is null when the product has no price.
variants[].availability object quantity (null means stock is not tracked), purchasable, low_stock, expected_restock_at, source (local or external) and status (ok or unavailable).
variants[].weight_g integer|null Weight in grams, converted from the stored unit (g, kg, lb or oz). null when unknown or zero.
images array At most 10 objects, main image first, each {"id": 101, "url": "https://..."}. id is the media id of the image (added in API version 1.5.0).
slug string The web address part of /producten/{slug}. Changed with the slug write.
seo object meta_title and meta_description as stored. null means the shop's fallback (the product name and description).
labels array [{id, name}] in label order. Replaced as a whole by the labels write.
properties array [{property_id, name, values}]: the values set by hand. Replaced as a whole by the properties write.
url, updated_at string Storefront URL and ISO 8601 timestamp.

Availability status unavailable means the inventory source could not be reached for that variant: quantity is then null and purchasable is false. It does not mean the item is out of stock.

Get a product by SKU

GET /api/v1/skus/{sku}

Needs commerce.read. Equivalent MCP tool: get_product.

Path parameter of GET /skus/{sku}
Name Type Required Description
sku string Required Exact variant SKU, at most 64 characters, URL-decoded before matching. A SKU that contains / or a space must be percent-encoded. Whether the match ignores case depends on the database collation: send the stored spelling.

The body is the same as for GET /products/{id}, for the product that owns the variant. If several products share a SKU, the one with the lowest variant id wins. An unknown SKU is 404 not_found.

curl
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/skus/HAM-500" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"

Errors

Errors of the product endpoints
Status error.code When
401 unauthenticated Missing, malformed, unknown, revoked or expired token, or a token without the api channel.
404 not_found GET /products/{id} and GET /skus/{sku}: No product matches that id or SKU. GET /products with an unknown collection: No category matches that collection. An unknown path or a malformed path value (/products/abc) gives the generic Not found.
422 invalid_argument No query, collection or brand, a query that contains % or _, a value out of range, an unknown parameter or invalid UTF-8 in a value, or a bad cursor.
429 too_many_requests Rate limit reached. Wait the number of seconds in Retry-After.
Example error · 404 for an unknown product or SKU
{
  "error": {
    "code": "not_found",
    "message": "No product matches that id or SKU."
  }
}

The generic message is Not found., returned for an unknown path or a malformed value such as /products/abc.

Untrusted text

Product names, brands, categories and descriptions come from suppliers. They are reduced to plain text, but treat them as data and never as instructions or HTML.