Filter products
Filter products
Build faceted product filtering with parameters, categories, and brands. Control filter logic with includeMode and facet count calculation with filterMode.
Prerequisites
- Merchant API key
Goals
- Fetch available facets for product filtering
- Filter products using facet IDs with the
includefield - Understand
includeMode(INTERSECT vs UNION) for combining filters - Understand
filterMode(BY_GROUP vs CURRENT) for facet counts - Work with different facet types and ID formats
- Build interactive faceted navigation
Architecture at a glance
- Query
products→ Get available facets → User selects filters → Queryproductswith facets applied → Get filtered results
APIs used
- Merchant API:
https://merchantapi.geins.io/graphql
Step-by-step
Get available facets
First, fetch products and include the filters field to get available facets. This shows you what filters users can apply.
Request example
query getAvailableFacets(
$categoryAlias: String
$skip: Int
$take: Int
$channelId: String
$languageId: String
$marketId: String
) {
products(
categoryAlias: $categoryAlias
skip: $skip
take: $take
channelId: $channelId
languageId: $languageId
marketId: $marketId
) {
products {
productId
name
canonicalUrl
}
count
filters {
facets {
filterId
type
group
label
order
values {
facetId
label
count
order
hidden
}
}
}
}
}
{
"Accept": "application/json",
"X-ApiKey": "{MERCHANT_API_KEY}"
}
{
"categoryAlias": "headphones",
"skip": 0,
"take": 12,
"channelId": "{CHANNEL_ID}",
"languageId": "{LANGUAGE_ID}",
"marketId": "{MARKET_ALIAS}"
}
curl -X POST https://merchantapi.geins.io/graphql \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-ApiKey: {MERCHANT_API_KEY}" \
-d '{"query":"query getAvailableFacets($categoryAlias: String, $skip: Int, $take: Int, $channelId: String, $languageId: String, $marketId: String) { products(categoryAlias: $categoryAlias, skip: $skip, take: $take, channelId: $channelId, languageId: $languageId, marketId: $marketId) { products { productId name canonicalUrl } count filters { facets { filterId type group label order values { facetId label count order hidden } } } } }","variables":{"categoryAlias":"headphones","skip":0,"take":12,"channelId":"{CHANNEL_ID}","languageId":"{LANGUAGE_ID}","marketId":"{MARKET_ALIAS}"}}'
channelId, languageId, and marketId arguments are optional and can be left out to use default values.Response example
200 OK{
"data": {
"products": {
"products": [
{
"productId": 1234,
"name": "Premium Wireless Headphones",
"canonicalUrl": "/p/premium-wireless-headphones"
}
],
"count": 47,
"filters": {
"facets": [
{
"filterId": "Brand",
"type": "Brand",
"group": null,
"label": "Brand",
"order": 0,
"values": [
{
"facetId": "b_audiopro",
"label": "AudioPro",
"count": 15,
"order": 0,
"hidden": false
},
{
"facetId": "b_soundmax",
"label": "SoundMax",
"count": 8,
"order": 1,
"hidden": false
}
]
},
{
"filterId": "Color",
"type": "Parameter",
"group": "Specifications",
"label": "Color",
"order": 1,
"values": [
{
"facetId": "p_1_5_black",
"label": "Black",
"count": 23,
"order": 0,
"hidden": false
},
{
"facetId": "p_1_5_white",
"label": "White",
"count": 12,
"order": 1,
"hidden": false
},
{
"facetId": "p_1_5_silver",
"label": "Silver",
"count": 7,
"order": 2,
"hidden": false
}
]
},
{
"filterId": "Connectivity",
"type": "Parameter",
"group": "Features",
"label": "Connectivity",
"order": 2,
"values": [
{
"facetId": "p_2_8_bluetooth",
"label": "Bluetooth",
"count": 35,
"order": 0,
"hidden": false
},
{
"facetId": "p_2_8_wired",
"label": "Wired",
"count": 18,
"order": 1,
"hidden": false
}
]
}
]
}
}
}
}
Filter products with multiple facets
Apply multiple filters using the include field with facet IDs. The default INTERSECT mode groups facets and combines them logically.
Request example
query filterProducts(
$categoryAlias: String
$include: [String]
$includeMode: IncludeMode
$filterMode: FilterMode
$skip: Int
$take: Int
$channelId: String
$languageId: String
$marketId: String
) {
products(
categoryAlias: $categoryAlias
skip: $skip
take: $take
filter: {
include: $include
includeMode: $includeMode
filterMode: $filterMode
}
channelId: $channelId
languageId: $languageId
marketId: $marketId
) {
products {
productId
name
canonicalUrl
productImages {
fileName
}
unitPrice {
sellingPriceIncVat
sellingPriceIncVatFormatted
}
brand {
name
alias
}
}
count
}
}
{
"Accept": "application/json",
"X-ApiKey": "{MERCHANT_API_KEY}"
}
{
"categoryAlias": "headphones",
"include": ["b_audiopro", "b_soundmax", "p_1_5_black", "p_1_5_white", "p_2_8_bluetooth"],
"includeMode": "INTERSECT",
"filterMode": "BY_GROUP",
"skip": 0,
"take": 12,
"channelId": "{CHANNEL_ID}",
"languageId": "{LANGUAGE_ID}",
"marketId": "{MARKET_ALIAS}"
}
curl -X POST https://merchantapi.geins.io/graphql \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-ApiKey: {MERCHANT_API_KEY}" \
-d '{"query":"query filterProducts($categoryAlias: String, $include: [String], $includeMode: IncludeMode, $filterMode: FilterMode, $skip: Int, $take: Int, $channelId: String, $languageId: String, $marketId: String) { products(categoryAlias: $categoryAlias, skip: $skip, take: $take, filter: { include: $include, includeMode: $includeMode, filterMode: $filterMode }, channelId: $channelId, languageId: $languageId, marketId: $marketId) { products { productId name canonicalUrl productImages { fileName } unitPrice { sellingPriceIncVat sellingPriceIncVatFormatted } brand { name alias } } count } }","variables":{"categoryAlias":"headphones","include":["b_audiopro","b_soundmax","p_1_5_black","p_1_5_white","p_2_8_bluetooth"],"includeMode":"INTERSECT","filterMode":"BY_GROUP","skip":0,"take":12,"channelId":"{CHANNEL_ID}","languageId":"{LANGUAGE_ID}","marketId":"{MARKET_ALIAS}"}}'
channelId, languageId, and marketId arguments are optional and can be left out to use default values.Response example
200 OK{
"data": {
"products": {
"products": [
{
"productId": 1234,
"name": "Premium Wireless Headphones - Black",
"canonicalUrl": "/p/premium-wireless-headphones",
"productImages": [
{
"fileName": "headphones-black.jpg"
}
],
"unitPrice": {
"sellingPriceIncVat": 299.00,
"sellingPriceIncVatFormatted": "$299.00"
},
"brand": {
"name": "AudioPro",
"alias": "audiopro"
}
},
{
"productId": 1235,
"name": "Premium Wireless Headphones - White",
"canonicalUrl": "/p/premium-wireless-headphones-white",
"productImages": [
{
"fileName": "headphones-white.jpg"
}
],
"unitPrice": {
"sellingPriceIncVat": 299.00,
"sellingPriceIncVatFormatted": "$299.00"
},
"brand": {
"name": "SoundMax",
"alias": "soundmax"
}
}
],
"count": 12
}
}
}
includeMode: INTERSECT (default), this returns products from (AudioPro OR SoundMax) AND (black OR white) AND (Bluetooth). See Understanding include modes below for details.- UNION mode: Set
includeMode: UNIONto apply OR logic across all facets (products match ANY selected facet). - Exclude facets: Use
exclude: ["facetId"]to filter out products with specific attributes. - Alternative methods:
brandIds,categoryIds,excludeBrandIds, andexcludeCategoryIdsprovide shortcuts, but facets are more flexible.
::
Understanding include modes
The includeMode parameter controls how multiple facets are combined logically.
INTERSECT mode (default)
Facets are grouped by type or parameter group, then combined using:
- OR within groups - Products match any value in the group
- AND between groups - Products must match at least one value from each group
Example:
include: ["b_audiopro", "b_soundmax", "p_1_5_black", "p_1_5_white", "p_2_8_bluetooth", "p_2_8_wired"]
// Automatic grouping:
// - Brand: b_audiopro, b_soundmax
// - Color (param group 1): p_1_5_black, p_1_5_white
// - Connectivity (param group 2): p_2_8_bluetooth, p_2_8_wired
// Logic applied:
(b_audiopro OR b_soundmax) AND (p_1_5_black OR p_1_5_white) AND (p_2_8_bluetooth OR p_2_8_wired)
UNION mode
All facets treated as a flat list with OR logic:
Example:
include: ["b_audiopro", "p_1_5_black", "p_2_8_bluetooth"]
includeMode: UNION
// Logic: b_audiopro OR p_1_5_black OR p_2_8_bluetooth
Understanding filter mode
The filterMode parameter controls how facet counts are calculated in the filter results. This affects the numbers shown next to each filter option in your UI.
BY_GROUP mode (recommended)
Most common when users interact with filters. Shows counts as if each facet group is selected independently, helping users understand available combinations.
How it works:
- For each facet, shows the count excluding filters in its own group
- But including filters from other groups
- Users see realistic counts for what they'll get if they change their selection within a group
Example: User has selected "AudioPro" brand and "Black" color:
filter: {
include: ["b_audiopro", "p_1_5_black"]
includeMode: INTERSECT
filterMode: BY_GROUP
}
// Results show:
// Brands:
// - AudioPro (15) ← count excludes brand filter, includes color filter
// - SoundMax (8) ← count excludes brand filter, includes color filter
// Colors:
// - Black (15) ← count excludes color filter, includes brand filter
// - White (10) ← count excludes color filter, includes brand filter
CURRENT mode
Shows counts for the current filter results exactly as applied. All selected filters affect all facet counts.
Example: Same selection as above:
filter: {
include: ["b_audiopro", "p_1_5_black"]
includeMode: INTERSECT
filterMode: CURRENT
}
// Results show:
// Brands:
// - AudioPro (15) ← count includes all filters
// - SoundMax (0) ← filtered out by current selection, not included in results
// Colors:
// - Black (15) ← count includes all filters
// - White (0) ← filtered out by current selection, not included in results
Understanding facet types
Facets can represent different types of filterable attributes. The type field in FilterType indicates what kind of filter it is:
Facet types
Parameter- Product parameters/attributes (e.g., Color, Size, Material, Connectivity)- Grouped by parameter group (specified in
groupfield) - Custom sort order supported via
orderfield - Most common type for product filtering
- Grouped by parameter group (specified in
Brand- Product brands- No parameter group
- Used for brand filtering
Category- Product categories- Can have hierarchical structure (use
parentIdinFilterValueType) - Used for category-based filtering
- Can have hierarchical structure (use
Sku- SKU-level attributes- Represents variant-specific attributes
type field to organize filters in your UI. Group all Parameter type filters together, and display Brand and Category filters separately.Facet ID formats
Facet IDs follow specific naming patterns based on the facet type:
Standard facet formats
- Brand facets:
b_{brandAlias}(e.g.,b_audiopro,b_soundmax) - Category facets:
c_{categoryAlias}(e.g.,c_headphones,c_electronics) - Parameter facets:
p_{groupId}_{parameterId}_{value}(e.g.,p_1_5_black,p_2_8_bluetooth) - SKU facets:
sku_{value}(e.g.,sku_large,sku_xl)
Special facet formats
- Stock status facets:
- In stock:
ss_in_stock - Out of stock:
ss_out_of_stock - Order item:
ss_order_item
- In stock:
- Discount campaign facets:
dc_{campaignAlias}_{currency}(e.g.,dc_summer-sale_usd) - Price range facets:
price_{from}_{to}_{currency}(e.g.,price_100_500_usd) - Reduced price facets:
rp_{type}_{currency}(e.g.,rp_sale_usd,rp_campaign_eur)
filters response, there are valid use cases for using known facet IDs directly—such as programmatically excluding out-of-stock items with exclude: ["ss_out_of_stock"] or filtering by specific campaigns. However, use this approach with caution: facet ID formats and availability can vary based on configuration and market settings. When possible, rely on dynamically fetched facets to ensure compatibility.Advanced filtering options
Combining with other filters
Facets can be combined with sorting, price filters, and search:
filter: {
include: ["b_audiopro", "p_1_5_black"]
includeMode: INTERSECT
sort: PRICE
price: { min: 50.00, max: 500.00 }
searchText: "wireless"
}
Alternative methods
For simple filtering, you can use shortcuts (converted to facets internally):
brandIds: [123]- Filter by brand IDscategoryIds: [456]- Filter by category IDsproductIds: [789]- Filter by specific product IDsarticleNumbers: ["SKU-001"]- Filter by article numbersexcludeBrandIds/excludeCategoryIds- Exclude specific brands/categories
Multi-market support
The query supports optional parameters for multi-market configurations:
Common pitfalls
- Not showing facet counts - Display the
countfield to help users understand filter options - Using hardcoded facet IDs - Always fetch facets dynamically; they change based on available products
- Ignoring hidden facets - Check the
hiddenfield before displaying facet values - Misunderstanding INTERSECT mode - Remember: OR within groups, AND between groups (see Understanding include modes)
- Not refreshing facets - Re-fetch
filtersafter applying filters to show relevant options only