Filter basics
The filter_group API parameter refines results by selecting only the products that match the given expressions and running the query on those products. The filter_group expects a filter expression.
{
"filter_group": {
"conditional": "AND",
"expressions": [
{
"property": "product_type",
"operator": "eq",
"values": ["Sweater"]
},
{
"property": "options.color",
"operator": "eq",
"values": ["RED"]
}
]
}
}
Expressions and operators
Expressions are the core of the filter language.
{
"property": "product_type",
"operator": "eq",
"values": ["Sweaters"]
}
property is the attribute of the field you want to filter on.
operator is the logical operator to apply (see full list below).
values is an array of values the operator compares against in the property.
Supported operators
| Operator | Description |
|---|
eq | Equal to a single value |
neq | Not equal to a single value |
in | Matches any value in the array |
notIn | Does not match any value in the array |
gt | Greater than |
gte | Greater than or equal to |
lt | Less than |
lte | Less than or equal to |
between | Within a range (pass exactly two values) |
notBetween | Outside a range (pass exactly two values) |
contains | String contains the given value (case-insensitive) |
doesNotContain | String does not contain the given value (case-insensitive) |
beginsWith | String starts with the given value (case-insensitive) |
endsWith | String ends with the given value (case-insensitive) |
doesNotBeginWith | String does not start with the given value (case-insensitive) |
doesNotEndWith | String does not end with the given value (case-insensitive) |
null | Value is null or does not exist |
notNull | Value is not null (exists) |
after | Date/timestamp value is strictly after the given bound (pass exactly one value) |
before | Date/timestamp value is strictly before the given bound (pass exactly one value) |
inLast | Date/timestamp value falls within the last N days, evaluated at query time (pass a single integer day count) |
geoRadius | Geo attribute lies within a radius of an origin (see Geospatial filtering) |
geoBoundingBox | Geo attribute lies inside a bounding box |
geoPolygon | Geo attribute matches a GeoJSON polygon |
Examples of basic conditions
A condition to find products of type Sweaters:
{
"property": "product_type",
"operator": "eq",
"values": ["Sweaters"]
}
A condition to find products created after 1 January 2020 (UNIX Epoch time):
{
"property": "created_at",
"operator": "gt",
"values": [1577836800]
}
A condition to exclude gift card products:
{
"property": "is_gift_card",
"operator": "eq",
"values": [false]
}
A condition to find bundle products (products with variants that require components):
{
"property": "has_variants_that_require_components",
"operator": "eq",
"values": [true]
}
A condition to find products within a price range (e.g., between 10and50):
{
"property": "variants.price",
"operator": "between",
"values": [10, 50]
}
A condition to find products with variants available at specific location IDs (e.g., locations 1001 and 1002):
The variants.in_stock_location_ids field is no longer filterable. It is now a sort-only field used for location-based product demotion in sort orders. See Sorting for details on using this field in priority rules.
{
"property": "variants.in_stock_location_ids",
"operator": "in",
"values": [1001, 1002]
}
A condition to find products whose title contains “organic”:
{
"property": "title",
"operator": "contains",
"values": ["organic"]
}
A condition to exclude products without a vendor (null check):
{
"property": "vendor",
"operator": "notNull",
"values": []
}
A condition to find products NOT in a price range (e.g., outside 10to50):
{
"property": "variants.price",
"operator": "notBetween",
"values": [10, 50]
}
A condition to find products published in the last 30 days. inLast is evaluated against NOW() at query time, so the window stays evergreen — you can save it once on a sort order or merchandising rule and it tracks the calendar without re-saving:
{
"property": "published_at",
"operator": "inLast",
"values": [30]
}
A condition to find products created strictly after a fixed date:
{
"property": "created_at",
"operator": "after",
"values": ["2025-01-01"]
}
A condition to find products created strictly before a fixed date:
{
"property": "created_at",
"operator": "before",
"values": ["2025-01-01"]
}
inLast, after, and before are designed for attributes whose value type is date or timestamp. Bounds are cast to the column’s type by Postgres, so both "2025-01-01" and "2025-01-01T00:00:00Z" are accepted.
Complex filter expressions
You can build advanced filter groups by combining expressions using AND and OR.
Example of complex expression
To include both Accessories and products from SUPREME published after 1 January 2020:
{
"conditional": "OR",
"expressions": [
{
"conditional": "AND",
"expressions": [
{
"property": "product_type",
"operator": "eq",
"values": ["Accessories"]
},
{
"property": "vendor",
"operator": "eq",
"values": ["Supreme"]
}
]
},
{
"conditional": "AND",
"expressions": [
{
"property": "published_at",
"operator": "gt",
"values": ["1577836800"]
}
]
}
]
}
Best practices
When crafting filter expressions:
- Test complex expressions in a controlled environment before deployment.
- String operators (
contains, doesNotContain, beginsWith, endsWith, doesNotBeginWith, doesNotEndWith) are case-insensitive. For example, filtering where title contains “organic” also matches “Organic” and “ORGANIC”.
- Use the
between operator instead of combining gte and lte for range queries.
- Nested filter groups support up to two levels of depth.
Troubleshooting
If your filter expression isn’t returning the expected results:
- Verify that operator names use the exact camelCase format shown above (e.g.,
notIn, not not_in).
- Confirm that attribute names and values match exactly with the data.
- Ensure the
values field is always an array, even for single-value operators like eq.
- Check that the
conditional field uses uppercase AND or OR.