Every product in Channel3 has a category, and every category has a set of structured attributes — machine-extracted features like material, connectivity, power source, and thousands of others. Combining category detail lookup with the attributes search filter lets you express queries that plain text search can’t — for example, finding sofas that have USB charging ports.
The two-step pattern
- Discover the category and its attributes — look up the category detail to see which attribute keys are defined and what values they accept.
- Search with an attribute filter — pass those key/value pairs to
/v1/search to get precisely matched products.
Step 1 — Get a category slug
You need a category slug (for example sofas) before you can inspect its attributes. Either find one by text, or grab it from a product you already have.
Option A — Search for the category
Use the category search endpoint to look up by free text:
import Channel3 from "@channel3/sdk";
const client = new Channel3();
const categoryResults = await client.categories.search({ query: "sofas" });
// Pick the best-matching category
const category = categoryResults.categories[0];
console.log(category.slug); // e.g. "sofas"
console.log(category.title); // "Sofas"
from channel3_sdk import Channel3
client = Channel3()
category_results = client.categories.search(query="sofas")
category = category_results.categories[0]
print(category.slug) # e.g. "sofas"
print(category.title) # "Sofas"
curl "https://api.trychannel3.com/v1/categories/search?query=sofas" \
-H "x-api-key: $CHANNEL3_API_KEY"
Option B — Grab the category from an existing product
If you already have a representative product — from a search result, lookup, or your own catalog — read its category.slug instead of searching:
const product = await client.products.retrieve("2yh8WH5");
const slug = product.category.slug; // e.g. "sofas"
product = client.products.retrieve("2yh8WH5")
slug = product.category.slug # e.g. "sofas"
curl "https://api.trychannel3.com/v1/products/2yh8WH5" \
-H "x-api-key: $CHANNEL3_API_KEY"
Step 2 — Retrieve category detail to see its attributes
Call the category detail endpoint with the slug to get the full list of attributes for that category. Each attribute has a handle (the key you’ll pass to the filter) and a list of known values.
const categoryDetail = await client.categories.retrieve("sofas");
// Print all attributes and their example values
for (const attr of categoryDetail.attributes) {
console.log(`${attr.handle}: ${attr.values.slice(0, 5).join(", ")}`);
}
// connectivity: USB, Bluetooth, None
// material: Leather, Velvet, Linen, Microfiber, ...
// seating_capacity: 2, 3, 4, Sectional
// style: Mid-Century Modern, Contemporary, ...
// color: Beige, Gray, Navy, ...
category_detail = client.categories.retrieve("sofas")
for attr in category_detail.attributes:
print(f"{attr.handle}: {', '.join(attr.values[:5])}")
# connectivity: USB, Bluetooth, None
# material: Leather, Velvet, Linen, Microfiber, ...
# seating_capacity: 2, 3, 4, Sectional
# style: Mid-Century Modern, Contemporary, ...
curl "https://api.trychannel3.com/v1/categories/sofas" \
-H "x-api-key: $CHANNEL3_API_KEY"
The attributes available vary by category. Always retrieve category detail
first — don’t guess attribute handles.
Step 3 — Search with the attribute filter
Now you have the attribute key and its accepted values. Pass them to POST /v1/search via the attributes filter. The attributes filter takes a map of handle → [values].
const results = await client.products.search({
query: "sofa",
filters: {
category: "sofas",
attributes: {
connectivity: ["USB"],
},
},
limit: 20,
});
console.log(`Found ${results.products.length} sofas with USB charging`);
results.products.forEach((p) => {
console.log(p.title, p.offers[0]?.price);
});
results = client.products.search(
query="sofa",
filters={
"category": "sofas",
"attributes": {
"connectivity": ["USB"],
},
},
limit=20,
)
print(f"Found {len(results.products)} sofas with USB charging")
for p in results.products:
print(p.title, p.offers[0].price if p.offers else "")
curl -X POST https://api.trychannel3.com/v1/search \
-H "x-api-key: $CHANNEL3_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "sofa",
"filters": {
"category": "sofas",
"attributes": { "connectivity": ["USB"] }
},
"limit": 20
}'
Full end-to-end example
Here’s the complete three-step flow in a single function — discover the category, inspect its attributes, then search:
import Channel3 from "@channel3/sdk";
const client = new Channel3();
async function findSofasWithUSB() {
// 1. Find the category
const { categories } = await client.categories.search({ query: "sofas" });
const slug = categories[0].slug; // "sofas"
// 2. Inspect its attributes
const detail = await client.categories.retrieve(slug);
const connectivityAttr = detail.attributes.find(
(a) => a.handle === "connectivity",
);
if (!connectivityAttr?.values.includes("USB")) {
console.warn("USB not listed as a connectivity value for this category");
}
// 3. Search with the attribute filter
const results = await client.products.search({
query: "sofa",
filters: {
category: slug,
attributes: { connectivity: ["USB"] },
},
limit: 20,
});
return results.products;
}
findSofasWithUSB().then((products) => {
console.log(`Found ${products.length} sofas with USB ports:`);
products.forEach((p) => console.log(" -", p.title));
});
from channel3_sdk import Channel3
client = Channel3()
def find_sofas_with_usb():
# 1. Find the category
category_results = client.categories.search(query="sofas")
slug = category_results.categories[0].slug # "sofas"
# 2. Inspect its attributes
detail = client.categories.retrieve(slug)
connectivity = next(
(a for a in detail.attributes if a.handle == "connectivity"), None
)
if connectivity and "USB" not in connectivity.values:
print("Warning: USB not listed as a connectivity value for this category")
# 3. Search with the attribute filter
results = client.products.search(
query="sofa",
filters={
"category": slug,
"attributes": {"connectivity": ["USB"]},
},
limit=20,
)
return results.products
products = find_sofas_with_usb()
print(f"Found {len(products)} sofas with USB ports:")
for p in products:
print(f" - {p.title}")
Combining multiple attributes
Pass multiple keys to narrow further. Values within a key are OR’d; keys are AND’d — so the example below finds sofas that are (USB OR Bluetooth) connectivity and Velvet material:
const results = await client.products.search({
query: "sofa",
filters: {
category: "sofas",
attributes: {
connectivity: ["USB", "Bluetooth"],
material: ["Velvet"],
},
},
});
results = client.products.search(
query="sofa",
filters={
"category": "sofas",
"attributes": {
"connectivity": ["USB", "Bluetooth"],
"material": ["Velvet"],
},
},
)
Reading structured_attributes on a product
Products returned from /v1/products/{id} and /v1/lookup include a structured_attributes field — a map of attribute handles to their extracted values for that specific product. Use it to display feature chips on a PDP, or to understand what attributes a specific product carries.
const product = await client.products.retrieve("2yh8WH5");
const attrs = product.structured_attributes;
// {
// connectivity: "USB",
// material: "Velvet",
// color: "Navy Blue",
// seating_capacity: "3",
// }
console.log(`Connectivity: ${attrs.connectivity}`);
console.log(`Material: ${attrs.material}`);
structured_attributes is populated on product detail (GET /v1/products/{id} and POST /v1/lookup). It is not returned on search results.