Skip to main content

Products

List products

GET /access/api/v1/products

Query parameters:

ParameterTypeDescription
pagenumberPage number (default: 1)
limitnumberItems per page (default: 18, max: 50)
sortstringA-Z, Z-A, RECENT, OLD
categoriesstringCategory slug — includes the category and all its descendants
minPricenumberMinimum price filter
maxPricenumberMaximum price filter
featuredbooleanOnly return featured products
onSalebooleanOnly return products on sale
termstringSearch within the product list

Response:

{
"status": "success",
"data": {
"currentPage": 1,
"totalPages": 5,
"totalDocuments": 90,
"limit": 18,
"data": [
{
"_id": "69bc36fda53000e2ede23dca",
"title": "main product",
"slug": "main-product",
"featured": false,
"hasVariantions": false,
"date": "2026-03-19T17:48:45.415Z",
"categories": [
{ "_id": "69bc3612a53000e2ede23cff", "name": "child 2", "slug": "child-2" }
],
"primaryCategory": { "_id": "69bc3612a53000e2ede23cff", "name": "child 2", "slug": "child-2" },
"primaryImage": null,
"secondaryImage": null,
"price": 5,
"salePrice": null,
"onSale": false,
"actualPrice": 5,
"minPrice": null,
"maxPrice": null,
"sku": "SKU-B7WI956J7T",
"status": "in-stock",
"sellable": null,
"reviewAggregate": [{ "_id": null, "count": 1, "average": 3 }],
"reviewCount": 1,
"averageRating": 3
},
]
}
}

Pricing

Use actualPrice as the authoritative price for display and totals — it already accounts for any active sale. salePrice is only set when a discount is active and lower than price. For products without variations, minPrice and maxPrice are always null.

Stock

sellable is the number of units available. It is null when the merchant has stock tracking off for that product — in that case treat the product as always in stock. When tracking is on, use status (in-stock or out-of-stock) to decide whether to allow adding to cart.

Images

uploadUrl is a relative path, not a full URL. Prepend media.url from the settings response to build the full URL:

const imageUrl = settings.media.url + product.primaryImage.uploadUrl;

Always check that primaryImage is not null before accessing it.

Variant products

When hasVariantions is true, the product has multiple variants (e.g. different sizes or colours). In this case:

  • price, salePrice, and actualPrice are always null — there is no single price at the product level
  • Use minPrice and maxPrice to show the price range (e.g. "KES 200 – KES 800")
  • To get the exact price for a specific variant, call Get all variations or Get variation details

Get single product

GET /access/api/v1/products/:id

:id accepts either the product slug or the product _id.

Response — product without variations:

{
"status": "success",
"data": {
"_id": "69ac75f878a9512e92b56fb5",
"title": "product v2 main",
"slug": "product-v2-main",
"description": "<h1>We are the best here</h1><p>Product description...</p>",
"featured": false,
"hasVariantions": false,
"date": "2026-03-07T19:01:12.026Z",
"categories": [
{ "_id": "69bc34f9a53000e2ede23ba4", "name": "child 1", "slug": "child-1" }
],
"primaryCategory": { "_id": "69bc34f9a53000e2ede23ba4", "name": "child 1", "slug": "child-1" },
"media": [
{ "_id": "6985925dd59ed6381f73f026", "type": "image/jpeg", "size": "159472", "title": "image-1.jpg", "uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-1.jpg", "position": 1 },
{ "_id": "69ac336aa7e93c257591fe5f", "type": "image/jpeg", "size": "36214", "title": "image-2.jpg", "uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-2.jpg", "position": 2 }
],
"primaryImage": {
"_id": "6985925dd59ed6381f73f026",
"type": "image/jpeg",
"size": "159472",
"title": "image-1.jpg",
"uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-1.jpg"
},
"secondaryImage": {
"_id": "69ac336aa7e93c257591fe5f",
"type": "image/jpeg",
"size": "36214",
"title": "image-2.jpg",
"uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-2.jpg"
},
"price": 110,
"salePrice": 100,
"onSale": true,
"actualPrice": 100,
"minPrice": null,
"maxPrice": null,
"sku": "SKU-V4V0GDNPV7",
"status": "in-stock",
"sellable": 84,
"options": null,
"defaultOptions": null,
"hasShipping": true,
"shipping": {
"weight": 87,
"length": 55,
"width": 96,
"height": 12,
"weightUnit": "g",
"measurementUnit": "cm"
},
"reviewAggregate": [],
"reviewCount": 0,
"averageRating": 0
}
}

description is returned as an HTML-escaped string. Unescape it before rendering — see Get policy document for the unescape helper. options and defaultOptions are null for products without variations.


Response — product with variations:

{
"status": "success",
"data": {
"_id": "69ac80f778a9512e92b57705",
"title": "variable",
"slug": "variable",
"description": "",
"featured": false,
"hasVariantions": true,
"date": "2026-03-07T19:48:07.830Z",
"categories": [
{ "_id": "6985925bd59ed6381f73d88e", "name": "new one_edited", "slug": "new-one_edited" }
],
"primaryCategory": { "_id": "6985925bd59ed6381f73d88e", "name": "new one_edited", "slug": "new-one_edited" },
"media": [
{ "_id": "6985925dd59ed6381f73f21a", "type": "image/png", "size": "66684", "title": "image-1.png", "uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-1.png", "position": 1 },
{ "_id": "6985925dd59ed6381f73f026", "type": "image/jpeg", "size": "159472", "title": "image-2.jpg", "uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-2.jpg", "position": 2 }
],
"primaryImage": {
"_id": "6985925dd59ed6381f73f21a",
"type": "image/png",
"size": "66684",
"title": "image-1.png",
"uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-1.png"
},
"secondaryImage": {
"_id": "6985925dd59ed6381f73f026",
"type": "image/jpeg",
"size": "159472",
"title": "image-2.jpg",
"uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-2.jpg"
},
"price": null,
"salePrice": null,
"onSale": true,
"actualPrice": null,
"minPrice": 0,
"maxPrice": 50,
"sku": "SKU-SKU-sku-main",
"status": "in-stock",
"sellable": 5,
"options": [
{
"_id": "69ac80f778a9512e92b5770d",
"name": "Color",
"position": 1,
"values": [
{ "_id": "69ac80f778a9512e92b57707", "value": "red" },
{ "_id": "69ac80f778a9512e92b57708", "value": "green" },
{ "_id": "69ac80f778a9512e92b57709", "value": "blue" }
]
},
{
"_id": "69ac811078a9512e92b57737",
"name": "Size",
"position": 2,
"values": [
{ "_id": "69ac811078a9512e92b5772d", "value": "sm" },
{ "_id": "69ac811078a9512e92b5772e", "value": "lg" }
]
}
],
"defaultOptions": [
{
"_id": "69ac80f778a9512e92b5770d",
"name": "Color",
"value": { "_id": "69ac80f778a9512e92b57707", "value": "red" }
},
{
"_id": "69ac811078a9512e92b57737",
"name": "Size",
"value": { "_id": "69ac811078a9512e92b5772d", "value": "sm" }
}
],
"hasShipping": false,
"shipping": null,
"reviewAggregate": [],
"reviewCount": 0,
"averageRating": 0
}
}

options drives the variant selector UI — render one dropdown or button group per option using values. defaultOptions tells you which option values to pre-select on page load (the first variant to display). When the customer changes a selection, join the selected value _ids with a dash to form the variationKey and call Get all variations or Get variation details to fetch the matching variant's price, stock, and images.

To display reviews and the star rating summary on the product page, call the Reviews endpoints using the product _id.

Possible errors: PRODUCT_NOT_FOUND


Get all variations

GET /access/api/v1/products/variants

Fetch all variants for a product after loading the product detail page. Each variant contains its own price, stock, images, and shipping dimensions.

Query parameters:

ParameterTypeRequired
productIdstringYes — slug or _id

Response:

{
"status": "success",
"data": {
"product": {
"_id": "69859254d59ed6381f73ae2b",
"title": "details variant",
"slug": "details-variant"
},
"totalVariants": 2,
"variants": [
{
"_id": "6954440add51aff39872bbf6",
"key": "6954440add51aff39872bbd6-6954440add51aff39872bbd8",
"position": 1,
"optionsValues": [
{ "key": { "_id": "6954440add51aff39872bbde", "name": "Colors" }, "value": { "_id": "6954440add51aff39872bbd6", "value": "redz" } },
{ "key": { "_id": "6954440add51aff39872bbdc", "name": "Size" }, "value": { "_id": "6954440add51aff39872bbd8", "value": "sm" } }
],
"sku": "SKU-OTHER-MAIN",
"trackStock": true,
"status": "in-stock",
"sellable": 5,
"price": 5000,
"salePrice": 4300,
"onSale": true,
"actualPrice": 4300,
"hasShipping": true,
"shipping": {
"weight": 90,
"length": 180,
"width": 270,
"height": 360,
"weightUnit": "kg",
"measurementUnit": "cm"
},
"media": [
{ "_id": "68f3ae7aa0756e3f6977179c", "type": "image/png", "size": "70912", "title": "image-1.png", "uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-1.png", "position": 1 }
],
"primaryImage": {
"_id": "68f3ae7aa0756e3f6977179c",
"type": "image/png",
"size": "70912",
"title": "image-1.png",
"uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-1.png",
"position": 1
}
},
{
"_id": "6954440add51aff39872bbf7",
"key": "6954440add51aff39872bbd7-6954440add51aff39872bbd8",
"position": 2,
"optionsValues": [
{ "key": { "_id": "6954440add51aff39872bbde", "name": "Colors" }, "value": { "_id": "6954440add51aff39872bbd7", "value": "Blue" } },
{ "key": { "_id": "6954440add51aff39872bbdc", "name": "Size" }, "value": { "_id": "6954440add51aff39872bbd8", "value": "sm" } }
],
"sku": "SKU-2H01VGVROH",
"trackStock": false,
"status": "in-stock",
"sellable": null,
"price": 910,
"salePrice": null,
"onSale": false,
"actualPrice": 910,
"hasShipping": true,
"shipping": {
"weight": 55,
"length": 65,
"width": 75,
"height": 85,
"weightUnit": "kg",
"measurementUnit": "m"
},
"media": [
{ "_id": "68f3acbba0756e3f69771745", "type": "image/webp", "size": "87150", "title": "image-2.webp", "uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-2.webp", "position": 1 }
],
"primaryImage": {
"_id": "68f3acbba0756e3f69771745",
"type": "image/webp",
"size": "87150",
"title": "image-2.webp",
"uploadUrl": "67e3cd865bf29cd7117f53a8/media/image-2.webp",
"position": 1
}
}
]
}
}

Each variant has a key — a dash-joined string of its option value IDs. When a customer selects options, join their chosen value _ids in the same order to form the key, then find the matching variant in this list to update the displayed price, stock, and images without another API call.

Possible errors: PRODUCT_ID_REQUIRED, PRODUCT_NOT_FOUND


Get variation details

GET /access/api/v1/products/variant-details

Fetches a single variant by product ID and variation key. Use this if you need to fetch one variant on demand rather than loading all variants upfront.

Query parameters:

ParameterTypeRequiredDescription
productIdstringYesSlug or _id
variationKeystringYesDash-joined option value IDs matching the desired variant (e.g. 6954440add51aff39872bbd6-6954440add51aff39872bbd8)

Response: Same shape as a single variant object from Get all variations.

Possible errors: PRODUCT_ID_REQUIRED, INVALID_VARIATION_KEY, INVALID_OPTION_VALUES, VARIANT_NOT_FOUND


Best sellers

GET /access/api/v1/products/best-sellers

Query parameters: page, limit

Response:

{
"status": "success",
"data": {
"currentPage": 1,
"totalPages": 1,
"totalDocuments": 2,
"limit": 12,
"data": []
}
}

Each item in data follows the same shape as List products.


Similar products

GET /access/api/v1/products/similar/:id

:id is the product slug or _id.

Query parameters: page, limit

Response:

{
"status": "success",
"data": {
"currentPage": 1,
"totalPages": 1,
"totalDocuments": 2,
"limit": 12,
"data": []
}
}

Each item in data follows the same shape as List products.


Hydrate cart

POST /access/api/v1/products/cart

Request body:

{
"cart": [
{
"productId": "69bc36fda53000e2ede23dca",
"quantity": 2,
"type": "product",
"variationKey": ""
}
]
}

variationKey is required for variant products — pass "" for products without variations.

Response:

{
"status": "success",
"total": 1,
"data": [
{
"_id": "69bc36fda53000e2ede23dca",
"title": "Running Shoes",
"slug": "running-shoes",
"primaryImage": null,
"secondaryImage": null,
"hasVariantions": false,
"primaryCategory": { "_id": "...", "name": "Footwear", "slug": "footwear" },
"media": [],
"price": 49.99,
"salePrice": null,
"onSale": false,
"actualPrice": 49.99,
"minPrice": null,
"maxPrice": null,
"sku": "SKU-B7WI956J7T",
"trackStock": false,
"status": "in-stock",
"sellable": null,
"variationKey": null,
"variation": null
}
]
}

Use actualPrice for order totals. Items no longer available are absent from the response.


Cart similarities

POST /access/api/v1/products/cart-similarities

Request body: Same as hydrate cart.

Query parameters: page, limit

Response:

{
"status": "success",
"data": {
"currentPage": 1,
"totalPages": 1,
"totalDocuments": 2,
"limit": 12,
"data": []
}
}

Each item in data follows the same shape as List products.