Skip to content
Online store

Catalog

Search, filter, and page through products, and show a product page with its variants.

Show the cotton T-shirts from your Lima store between S/ 40 and S/ 80, size M, in stock.

Requirements

  • The store slug or a domain, in x-Store-Domain. You don't need a token.
  • Products published in the store.

List products

GET /v1/products returns one page of the published catalog.

curl "https://storefront.ecomiq.pe/v1/products?pageSize=24&sort=priceAsc&includeFilters=false" \
  -H "x-Store-Domain: mi-tienda"
Response
{
  "totalCount": 58,
  "totalPages": 3,
  "products": [
    {
      "id": "7204558912004322039",
      "name": "Polo de algodón pima",
      "slug": "polo-de-algodon-pima",
      "url": "/products/polo-de-algodon-pima",
      "imageUrl": "https://…/polo.webp",
      "price": 59.9,
      "compareAtPrice": 79.9,
      "isAvailable": true
    }
  ],
  "filters": []
}

The next examples reuse API and headers.

  • price is the price of the cheapest variant. If compareAtPrice is higher, show it struck through.
  • With isAvailable: false, no variant can be sold. Show "Sold out".
  • Link each product to its url. Fetch its page with slug.
  • Without a filter panel, send includeFilters=false: filters comes back empty.

sort accepts priceAsc, priceDesc, newest, oldest, nameAsc, nameDesc, and bestSelling. Without sort, recently edited products come first. Within a single collection, without sort, products follow the merchant's order.

Search by text

Send what the shopper typed in search.

curl -G https://storefront.ecomiq.pe/v1/products \
  -H "x-Store-Domain: mi-tienda" \
  --data-urlencode "search=polo algodón"
  • It searches name, description, brand, category, tags, SKU, barcode, and searchable specifications.
  • Without sort, results are ordered by relevance.
  • Combine it with filters. The filters in the response follow the search.
  • To suggest products while the shopper types, use autocomplete.

Filter the catalog

Every filter is optional, and they combine. Copy the values from the response's filters.

ParameterFilters byGroup in filters
categoryIdOne category, by id. Without subcategories.—
categoriesCategories, by name.category
collectionIdOne collection, by id.—
collectionsCollections, by slug.collection
brandsBrands, by name.brand
tagsTags.tag
productTypesproduct, service, or bundle.productType
sellersSellers, by slug.—
productIdsProducts, by id. Up to 100.—
minPrice, maxPricePrice, in the store currency.price
inStocktrue: in stock. false: sold out.availability
filtersOptions and specifications.option:…, attribute:…
curl -G https://storefront.ecomiq.pe/v1/products \
  -H "x-Store-Domain: mi-tienda" \
  --data-urlencode "categories=Polos" \
  -d "minPrice=40" -d "maxPrice=80" -d "inStock=true"
  • For several values, repeat the parameter: brands=Andina&brands=Kuna. One match is enough.
  • A product matches when its variants' price range overlaps yours.
  • With productIds, the order is not the one you sent. Sort the list yourself.

Build the filters

Without includeFilters=false, the response includes filters: one group per filter, with its values and counts.

Response
{
  "filters": [
    {
      "id": "price",
      "label": "Price",
      "type": "price_range",
      "min": 29.9,
      "max": 149.9,
      "selectedMin": 40,
      "selectedMax": 80
    },
    {
      "id": "option:Talla",
      "label": "Talla",
      "type": "list",
      "values": [
        { "value": "M", "count": 12, "label": "M", "selected": true },
        { "value": "L", "count": 10, "label": "L", "selected": false }
      ]
    }
  ]
}

When the shopper checks a value, send it in its group's parameter. In availability, in_stock is inStock=true and out_of_stock is inStock=false.

In filters, join the group id, :, and the value. For size M and cotton, send filters=option:Talla:M and filters=attribute:Material:Algodón.

  • option groups are variant options. attribute groups are product specifications.
  • Pick one value per option. With two, you get a 400.
  • Several options must match the same variant. With inStock=true, that variant is also in stock.
  • You can send several values of one attribute. One match is enough.
  • Send up to 100 filters, each up to 250 characters.
  • Built-in groups have an English label. Show your own text based on id.
  • If a value's label is null, show value.
  • In price, min and max ignore your range. selectedMin and selectedMax are yours.
  • Counts apply all your filters. Once you pick a brand, brand only lists that one.
  • Only options and specifications the merchant marked as filterable show up.

Page through results

Move page forward and keep the same filters and sort.

curl "https://storefront.ecomiq.pe/v1/products?categories=Polos&page=2&pageSize=24" \
  -H "x-Store-Domain: mi-tienda"
  • page starts at 1 and goes up to 100. Outside that range, you get a 400.
  • pageSize is 24 if you don't send it. The maximum is 100.
  • After totalPages, products comes back empty.
  • With more than 100 pages, you can't reach the end. Raise pageSize or ask the shopper to filter.

Show a product

GET /v1/products/{slug} returns the full product page. Use the slug from the list.

curl https://storefront.ecomiq.pe/v1/products/polo-de-algodon-pima \
  -H "x-Store-Domain: mi-tienda"
Response
{
  "id": "7204558912004322039",
  "name": "Polo de algodón pima",
  "price": 59.9,
  "compareAtPrice": 79.9,
  "isAvailable": true,
  "images": [{ "url": "https://…/polo.webp", "order": 1, "type": "image" }],
  "specifications": [{ "name": "Material", "value": "Algodón" }],
  "variants": [
    {
      "id": "7204558912004323197",
      "price": 59.9,
      "compareAtPrice": 79.9,
      "quantity": 4,
      "isAvailable": true,
      "options": [
        { "key": "talla", "name": "Talla", "value": "M" },
        { "key": "color", "name": "Color", "value": "Azul", "color": "#1F3A93" }
      ]
    },
    {
      "id": "7204558912004323198",
      "isAvailable": false,
      "options": [
        { "key": "talla", "name": "Talla", "value": "L" },
        { "key": "color", "name": "Color", "value": "Azul" }
      ]
    }
  ]
}
  • The product page applies the store's active price lists. If it differs from the list, the product page wins.
  • price and compareAtPrice are those of the cheapest variant.
  • Sort images by order. type is image or video: videos sit among the photos, where the merchant placed them.
  • metafields holds only the metafields the store chose to show, with name ready to display and value.
  • brandSlug leads to the brand page; it is null when the product has no brand or the brand is inactive.
  • specifications is the spec sheet. sizeGuide is the size guide, or null.
  • priceTiers holds quantity prices: unitPrice from minQuantity on.
  • If the product doesn't exist or isn't published, you get a 404.

With the previewKey from the admin preview link, you see an unpublished product. It comes with isPreview: true: don't cache it or index it.

Pick the variant

Build one selector per key in variants[].options: name is the title and each value is a button. Then find the variant that matches all of them.

const groups = new Map();
for (const variant of product.variants) {
  for (const option of variant.options) {
    const group = groups.get(option.key) ?? { name: option.name, values: [] };
    if (!group.values.includes(option.value)) group.values.push(option.value);
    groups.set(option.key, group);
  }
}

const selected = { talla: "M", color: "Azul" };
const variant = product.variants.find((item) =>
  item.options.every((option) => selected[option.key] === option.value),
);
const canBuy = variant !== undefined && variant.isAvailable !== false;
  • If no variant matches, that combination doesn't exist. Disable it.
  • With isAvailable: false, the variant is sold out. Disable the buy button.
  • Check isAvailable, not quantity. Without stock tracking or with backorders, a variant sells at quantity 0.
  • Show the variant's price, compareAtPrice, and images. Without images, use the product's.
  • color holds the hex code for the color swatch.
  • When the page opens, select the first available variant.

Add to cart

Send the variant id as variantId to POST /v1/carts/{cartToken}/items. The cart checks stock again. If there isn't enough, you get a 400 with Catalog.InsufficientStock. Continue in Cart.

Show a brand

GET /v1/brands/{slug} returns the active brand with its logo (imageUrl), its description, the search engine texts (metaTitle and metaDescription) and productCount. If the brand doesn't exist or is inactive, you get a 404. To list its products, filter the catalog with its name in brands. With GraphQL, brand(slug:) returns the brand and products(input:) its products in the same query.

GET /v1/sitemap includes /brands/{slug} for each active brand with visible products. If your store builds its own sitemap, list those URLs only when it has the brand page.

Errors

codeStatusWhat to do
Common.Validation400Check errors: page, pageSize, filters, or sort is not valid, or a parameter has the wrong type.
Common.BadRequest400x-Store-Domain is missing.
Common.NotFound404The store or the collectionId collection doesn't exist or is inactive.
Product.NotFound404The product doesn't exist, isn't published, or previewKey is invalid.
Common.Unauthorized401The store is private. Ask for the password.
Common.ServiceUnavailable503Search is not responding. Retry in a few seconds.

Next steps

Last updated:

On this page