Catalogue
Products, variants, options, collections, images and prices, and how to filter, sort, include and page through the product list.
The catalogue endpoints give your storefront everything it needs to render listing and product pages. They are read-only, and only published products are ever returned.
Endpoints
| Endpoint | Returns |
|---|---|
GET /v1/products | A page of products, with filters, sorts and includes |
GET /v1/products/price-bounds | The lowest and highest price for the current filters, for a price slider |
GET /v1/products/{slug} | One product, with everything included |
GET /v1/collections | A page of collections |
GET /v1/collections/{slug} | One collection |
GET /v1/collections/{slug}/products | A page of the collection's products, with the same filters, sorts and includes as GET /v1/products |
GET /v1/promotions | The store's live public offers, for an offers strip or banner |
GET /v1/countries | The countries the store sells and ships to, for address forms |
Catalogue responses are cached by the engine and cleared when the merchant changes the catalogue, so you can call them on every page view.
The building blocks
- Products have a
name,description,slugandstatus. Link product pages byslug. - Variants are what a shopper actually buys. Each has an
id, asku, itsstockand whether it isshippable. Add a variant to a cart by itsidor itssku. - Options describe how variants differ, such as Colour or Size. A product's
optionslist the options its live variants use, in the merchant's order, each with the values in use. Each variant's ownoptionsname its value per option. That is all an option picker needs. - Collections group products. They nest: each has a
parent_id, and may list itschildren. - Attributes are the merchant's own product fields, such as brand or material. Filterable attributes can narrow a listing with
filter[attr]. - Images have a
urland athumbnail_url. - Prices are per variant, as money objects.
A product
{
"data": {
"id": 101,
"status": "published",
"name": "Oak desk lamp",
"description": "Turned solid oak base with a linen shade.",
"slug": "oak-desk-lamp",
"options": [
{ "id": 3, "handle": "finish", "name": "Finish", "label": "Finish",
"values": [{ "id": 7, "name": "Natural" }, { "id": 8, "name": "Smoked" }] }
],
"variants": [
{
"id": 1543,
"sku": "LAMP-OAK-NAT",
"stock": 12,
"shippable": true,
"price": { "amount": 8900, "currency": "GBP", "formatted": "£89.00" },
"pricing": {
"price": { "amount": 8900, "currency": "GBP", "formatted": "£89.00" },
"sale_price": null,
"compare_price": null,
"on_sale": false,
"saving": null,
"badges": []
},
"options": [{ "option_id": 3, "option": "Finish", "value_id": 7, "value": "Natural" }]
}
],
"images": [
{ "id": 55, "name": "oak-desk-lamp", "url": "https://…/oak-desk-lamp.webp", "thumbnail_url": "https://…/oak-desk-lamp-thumb.webp" }
],
"collections": [{ "id": 4, "parent_id": null, "name": "Lighting", "slug": "lighting" }],
"offers": []
}
}GET /v1/products/{slug} always returns the full product, whatever you pass in include. Listings return only what you ask for.
Prices on a variant
| Field | Meaning |
|---|---|
pricing.price | The price for one unit. price on the variant is the same amount, kept for older clients. |
pricing.sale_price | The promotional sale price, when a catalogue sale applies and the store shows sale prices. Otherwise null. |
pricing.compare_price | The merchant's "was" price, only when it is above the price. |
pricing.on_sale | Whether to present the item as on sale. |
pricing.saving | What a promotion saves, as amount and percent_bp (basis points). |
pricing.badges | Badges to show, each with a label and kind. |
When the request carries a signed-in customer's bearer token, prices are that customer's customer-group prices. Otherwise they are the default prices a guest sees.
List products
curl -G https://api.elaraone.app/api/v1/products \
-H "X-Tenant-Key: $ELARAONE_TENANT_KEY" \
-H "X-Tenant-Secret: $ELARAONE_TENANT_SECRET" \
--data-urlencode "filter[collection]=lighting" \
--data-urlencode "filter[price_max]=10000" \
--data-urlencode "sort=-price" \
--data-urlencode "include=variants.prices,images" \
--data-urlencode "perPage=24"Filters
| Parameter | Example | Matches |
|---|---|---|
filter[collection] | filter[collection]=lighting | Products in the collection with this slug |
filter[search] | filter[search]=mug | Products whose name contains the text, ignoring case |
filter[price_min] | filter[price_min]=2000 | Products with a variant priced at or above this, in integer minor units |
filter[price_max] | filter[price_max]=5000 | Products with a variant priced at or below this, in integer minor units |
filter[attr][handle] | filter[attr][material]=linen | Products whose attribute has this value, ignoring case. Repeat for more attributes; they all have to match. |
Filters combine: every one you send has to match. filter[search] is a simple name match. For ranked, typo-tolerant search with facets, use search.
Sorts
sort takes one of name, created_at or price. Put - in front for descending order, for example sort=-created_at for newest first or sort=price for cheapest first.
Includes
include takes a comma-separated list:
| Include | Adds |
|---|---|
variants | The variants, without prices |
variants.prices | Each variant's price and pricing |
variants.options | The product's options and each variant's options |
images | The product's images |
collections | The collections the product is in |
offers | The promotions the product advertises, for badges, banners and bulk-price tables |
Ask only for what the page shows: a listing grid usually needs variants.prices,images.
An unknown filter, sort or include is refused with 400.
Pages
perPage sets the page size, from 1 to 100 (default 24), and page picks the page. Responses carry links and meta. See pagination.
Price bounds
GET /v1/products/price-bounds returns the price range of the products a listing would show with the same filter[collection], filter[search] and filter[attr]. Forward your listing's query string unchanged: filter[price_min] and filter[price_max] are accepted but ignored, so the slider's range never shrinks to the range the shopper just picked.
{
"data": {
"currency": "GBP",
"min": { "amount": 1800, "currency": "GBP", "formatted": "£18.00" },
"max": { "amount": 8900, "currency": "GBP", "formatted": "£89.00" }
}
}Promotions
GET /v1/promotions lists the store's live public offers, unpaginated, for an offers strip or banners.
{
"data": [
{
"id": 12,
"kind": "free_shipping",
"label": "Free delivery over £50",
"badge": null,
"banner": { "title": "Free delivery over £50", "body": "On every order to the UK mainland." },
"ends_at": "2026-11-30T23:59:59+00:00",
"target": null,
"scope": "all"
}
]
}kind is the rule kind: product_adjustment, cart_adjustment, bulk_tiered, bundle_set, buy_x_get_y or free_shipping. scope says what the rule covers: all (store-wide), collection or product.
Offers on a single product come back in its offers (always on GET /v1/products/{slug}, and on listings with include=offers). A bulk rule adds a bulk_table of quantity bands, each with min_qty, max_qty, unit_price and percent_bp.
Countries
GET /v1/countries lists the countries the store sells and ships to. Use it to build the country picker in address forms: the country's id is the country_id that cart and account addresses take.
{
"data": [
{ "id": 235, "iso2": "GB", "iso3": "GBR", "name": "United Kingdom", "phonecode": "44" }
],
"meta": { "default_country": "GB" }
}