Product Browsing Flow
Navigate the product catalog from categories down to individual product details.
What you'll build
A complete catalog-browsing experience: load categories, fetch available filters, list products by category with pagination, view enriched product detail (v2), then explore a brand and its products.
Prerequisites
- A Store UUID — e.g.
e87437f2-3e35-4738-af5e-6307e368255c(staging) - cURL or any HTTP client
No authentication is required. All endpoints in this flow use the
jwt_optional_authenticatedpipeline.
Step 1: Get Categories
Load the category tree to build the navigation menu.
cURL
curl -X GET https://ecom-api.staging.blaze.me/api/v1/products/categories \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c"
JavaScript (fetch)
const STORE_UUID = "e87437f2-3e35-4738-af5e-6307e368255c";
const BASE_URL = "https://ecom-api.staging.blaze.me";
const headers = {
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json",
"X-Store": STORE_UUID,
};
const catResponse = await fetch(`${BASE_URL}/api/v1/products/categories`, { headers });
const { data: categories } = await catResponse.json();
categories.forEach((cat) => {
console.log(`${cat.attributes.name} (${cat.attributes.count} products)`);
});
Response
{
"data": [
{
"id": "cat-flower-001",
"type": "product_categories",
"attributes": {
"name": "Flower",
"slug": "flower",
"description": "Cannabis flower buds",
"count": 48,
"position": 1,
"is_active": true
}
},
{
"id": "cat-edible-002",
"type": "product_categories",
"attributes": {
"name": "Edibles",
"slug": "edibles",
"description": "Cannabis-infused food products",
"count": 32,
"position": 2,
"is_active": true
}
}
]
}
Tip: Use
positionto order categories in the UI. Useslugwhen filtering products by category.
Step 2: Get Available Filters
Fetch the filter dimensions so the sidebar can show checkboxes, sliders, and counts.
cURL
curl -X GET https://ecom-api.staging.blaze.me/api/v1/products/filters \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c"
JavaScript (fetch)
const filterResponse = await fetch(`${BASE_URL}/api/v1/products/filters`, { headers });
const { data: filters } = await filterResponse.json();
console.log("Categories:", filters.attributes.categories);
console.log("Types:", filters.attributes.types);
console.log("Brands:", filters.attributes.brands);
console.log("Price range:", filters.attributes.price_ranges);
Response
{
"data": {
"id": "filters",
"type": "filters",
"attributes": {
"categories": [
{ "name": "Flower", "slug": "flower", "count": 48 },
{ "name": "Edibles", "slug": "edibles", "count": 32 }
],
"types": [
{ "name": "Hybrid", "count": 25 },
{ "name": "Indica", "count": 18 },
{ "name": "Sativa", "count": 12 }
],
"brands": [
{ "name": "Cookies", "slug": "cookies", "count": 15 },
{ "name": "Stiiizy", "slug": "stiiizy", "count": 22 }
],
"price_ranges": {
"min": 5.0,
"max": 120.0
}
}
}
}
Step 3: List Products by Category
The user clicks "Flower" — fetch the first page of products in that category.
cURL
curl -X GET "https://ecom-api.staging.blaze.me/api/v1/products?category=flower&limit=10&offset=0" \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c"
JavaScript (fetch)
const params = new URLSearchParams({
category: "flower",
limit: "10",
offset: "0",
});
const prodResponse = await fetch(`${BASE_URL}/api/v1/products?${params}`, { headers });
const { data: products, meta } = await prodResponse.json();
console.log(`Showing ${products.length} of ${meta.total} products`);
products.forEach((p) => {
const { name, unit_price, type, in_stock } = p.attributes;
console.log(`${name} — $${unit_price} (${type}) ${in_stock ? "✓" : "✗"}`);
});
Response
{
"data": [
{
"id": "b5c8d1e2-f3a4-5678-9012-abcdef123456",
"type": "store_products",
"attributes": {
"name": "Blue Dream",
"slug": "blue-dream",
"sku": "BD-001",
"description": "A popular sativa-dominant hybrid",
"type": "Hybrid",
"flower_type": "Hybrid",
"thc": "21%",
"cbd": "0.1%",
"in_stock": true,
"is_promoted": false,
"unit_price": 45.00,
"main_image": "https://images.example.com/blue-dream.jpg"
}
}
],
"meta": {
"total": 48,
"limit": 10,
"offset": 0
}
}
Pagination
Load the next page by incrementing offset:
# Page 2
curl -X GET "https://ecom-api.staging.blaze.me/api/v1/products?category=flower&limit=10&offset=10" \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c"
// Generic pagination helper
async function fetchPage(category, page, pageSize = 10) {
const params = new URLSearchParams({
category,
limit: String(pageSize),
offset: String((page - 1) * pageSize),
});
const res = await fetch(`${BASE_URL}/api/v1/products?${params}`, { headers });
return res.json();
}
const page2 = await fetchPage("flower", 2);
console.log(`Page 2: ${page2.data.length} products`);
Step 4: Get Product Detail (v2)
The user clicks a product — fetch the enriched v2 detail with variants, relationships, and included resources.
cURL
curl -X GET https://ecom-api.staging.blaze.me/api/v2/products/blue-dream \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c"
JavaScript (fetch)
const productSlug = "blue-dream";
const detailResponse = await fetch(`${BASE_URL}/api/v2/products/${productSlug}`, { headers });
const { data: product, included } = await detailResponse.json();
console.log(product.attributes.name); // "Blue Dream"
console.log(product.attributes.weight_prices); // variant pricing array
console.log(product.relationships); // linked brands, categories, images
// Resolve a relationship from the included array
const brandRef = product.relationships.product_brands?.data?.[0];
if (brandRef) {
const brand = included.find((r) => r.type === brandRef.type && r.id === brandRef.id);
console.log(`Brand: ${brand.attributes.name}`); // "Cookies"
}
Response
{
"data": {
"id": "b5c8d1e2-f3a4-5678-9012-abcdef123456",
"type": "store_products",
"attributes": {
"name": "Blue Dream",
"slug": "blue-dream",
"sku": "BD-001",
"type": "Hybrid",
"thc": "21%",
"cbd": "0.1%",
"in_stock": true,
"unit_price": 45.00,
"weight_prices": [
{ "weight": 1.0, "weight_unit": "g", "price": 15.00, "original_price": null, "in_stock": true },
{ "weight": 3.5, "weight_unit": "g", "price": 45.00, "original_price": null, "in_stock": true }
],
"unit_prices": [
{
"display_name": "1g",
"quantity": 1.0,
"price": { "amount": 15.00, "currency": "USD" }
}
]
},
"relationships": {
"product_brands": {
"data": [{ "id": "brand-001", "type": "product_brands" }]
},
"product_categories": {
"data": [{ "id": "cat-flower-001", "type": "product_categories" }]
}
}
},
"included": [
{
"id": "brand-001",
"type": "product_brands",
"attributes": {
"name": "Cookies",
"slug": "cookies"
}
}
]
}
v2 vs v1: The v2 product detail includes
weight_prices,unit_prices,relationships, and sideloadedincludedresources. Always prefer v2 for product detail pages.
Step 5: View Brand Detail
The user clicks the brand name — fetch brand info from the v2 brands endpoint.
cURL
curl -X GET https://ecom-api.staging.blaze.me/api/v2/products/brands \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c"
JavaScript (fetch)
const brandsResponse = await fetch(`${BASE_URL}/api/v2/products/brands`, { headers });
const { data: brands } = await brandsResponse.json();
const targetBrand = brands.find((b) => b.attributes.slug === "cookies");
console.log(targetBrand.attributes.name); // "Cookies"
console.log(targetBrand.attributes.description); // "Premium cannabis brand"
console.log(targetBrand.attributes.logo_url); // brand logo image
console.log(targetBrand.attributes.count); // 15 products
Response
{
"data": [
{
"id": "brand-001",
"type": "product_brands",
"attributes": {
"name": "Cookies",
"slug": "cookies",
"description": "Premium cannabis brand",
"logo_url": "https://images.example.com/brands/cookies.png",
"count": 15,
"is_promoted": true
}
},
{
"id": "brand-002",
"type": "product_brands",
"attributes": {
"name": "Stiiizy",
"slug": "stiiizy",
"count": 22,
"is_promoted": false
}
}
]
}
Step 6: List Products by Brand
Show all products from the selected brand.
cURL
curl -X GET "https://ecom-api.staging.blaze.me/api/v1/products?brand=cookies&limit=20" \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c"
JavaScript (fetch)
const brandParams = new URLSearchParams({
brand: "cookies",
limit: "20",
});
const brandProductsRes = await fetch(`${BASE_URL}/api/v1/products?${brandParams}`, { headers });
const { data: brandProducts, meta: brandMeta } = await brandProductsRes.json();
console.log(`${brandMeta.total} products by Cookies`);
brandProducts.forEach((p) => {
console.log(` ${p.attributes.name} — $${p.attributes.unit_price}`);
});
Putting It All Together
const STORE_UUID = "e87437f2-3e35-4738-af5e-6307e368255c";
const BASE_URL = "https://ecom-api.staging.blaze.me";
const headers = {
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json",
"X-Store": STORE_UUID,
};
// 1. Load categories for the nav menu
const { data: categories } = await (
await fetch(`${BASE_URL}/api/v1/products/categories`, { headers })
).json();
console.log(`${categories.length} categories loaded`);
// 2. Load filters for the sidebar
const { data: filters } = await (
await fetch(`${BASE_URL}/api/v1/products/filters`, { headers })
).json();
console.log(`Price range: $${filters.attributes.price_ranges.min}–$${filters.attributes.price_ranges.max}`);
// 3. User selects "Flower" category — list products
const { data: products, meta } = await (
await fetch(`${BASE_URL}/api/v1/products?category=flower&limit=10`, { headers })
).json();
console.log(`${meta.total} flower products, showing first ${products.length}`);
// 4. User clicks a product — get v2 detail
const firstProduct = products[0];
const { data: detail, included } = await (
await fetch(`${BASE_URL}/api/v2/products/${firstProduct.id}`, { headers })
).json();
console.log(`Product: ${detail.attributes.name}`);
console.log(`Variants: ${detail.attributes.weight_prices?.length ?? 0}`);
// 5. User clicks the brand — list brand products
const brandSlug = included?.find((r) => r.type === "product_brands")?.attributes?.slug;
if (brandSlug) {
const { data: brandProducts } = await (
await fetch(`${BASE_URL}/api/v1/products?brand=${brandSlug}&limit=20`, { headers })
).json();
console.log(`${brandProducts.length} products by ${brandSlug}`);
}
What's Next?
- Advanced search & filters: See the Search & Filters Flow example
- Sponsored product placements: See the Ads & Sponsored Flow example
- Add to cart: See the Cart & Checkout guide
- Product catalog reference: See the Products Catalog guide