How to Use Dynamic Filter and Dynamic Exclude
Dynamic Filter and Dynamic Exclude let any HTTP client shape search results at request time by passing extra filter or exclusion conditions as request headers. The feature sits entirely on the Commerce (omnishop) side; the client — whether a BFF (Backend For Frontend) service, a mobile application, or any other HTTP consumer — only needs to attach the appropriate header.
Dynamic Filter narrows the result set to only the products matching the conditions you specify — useful when you want to work within a defined subset of products (e.g. recently viewed, mobile-exclusive, country-specific catalogue).
Dynamic Exclude removes specific products from the result set — useful when you want the full catalogue minus certain items (e.g. discontinued variants, channel-restricted brands).
Unlike static facet configuration, which is set once at the channel level in the admin, dynamic filter and exclude conditions are sent per-request by the client and take effect immediately with no configuration change needed.
Both headers can be sent together in the same request. Commerce evaluates both conditions simultaneously as part of a single Elasticsearch query — results must match the filter conditions AND not match the exclude conditions.
For the low-level configuration reference (facet field definitions, SEARCH_DYNAMIC_FILTER_ACTIVE, etc.) see Facet and Sort Configuration.
Enabling the Feature
Set the environment variable below to True in your Commerce project before using any of the examples in this tutorial.
SEARCH_DYNAMIC_FILTER_ACTIVE=TrueIf you are using omnife as a BFF, set the same variable there as well so that omnife forwards the headers to the Commerce project.
SEARCH_DYNAMIC_FILTER_ACTIVE=TrueIf you are using products.attributes_* fields as filter or exclude conditions, the corresponding attribute must have is_filterable set to true in the attribute configuration in Omnitron.
How It Works
Difference from Standard Query Parameter Filters
The Commerce /list/ endpoint also accepts standard query parameter filters such as /list/?attributes_size=S. These are user-driven: the end user selects them from the filter panel, they appear in the URL, and they require a FacetConfiguration to be defined in the admin first.
Dynamic Filter and Dynamic Exclude are client-driven: they are set programmatically by the client (a BFF service, a mobile app, etc.), are invisible to the end user in the URL, and cannot be manipulated by the end user. No admin configuration is needed beyond enabling the feature.
Both mechanisms work simultaneously — for example, a header can narrow the catalogue to country-specific products while the user applies a size filter via query parameters on top of that.
Headers
Dynamic Filter and Dynamic Exclude are supported on both the /list/ and /autocomplete/ endpoints.
Every request to these endpoints may carry two optional headers:
X-SEARCH-DYNAMIC-FILTER
Include only products that match these conditions
X-SEARCH-DYNAMIC-EXCLUDE
Exclude products that match these conditions
Both headers carry a base64-encoded JSON object. The JSON object maps Elasticsearch field names to lists of values.
Encoding a filter in Python:
Logic rules:
Multiple values in the same list → OR (any value matches)
Multiple keys in the same object → AND (all conditions must match)
_any_ofkey with a list of condition objects → OR across groups
Use Cases
1. Mobile-Exclusive Products
A mobile application sells products that are marked with a custom attribute is_mobile set to the string value "true". By attaching the filter header on every search request the mobile client makes, those products become visible only to that client.
JSON payload:
curl example:
The same pattern works in reverse: a web client can exclude mobile-only products by sending the same payload as an X-SEARCH-DYNAMIC-EXCLUDE header, so each channel naturally sees only its own catalogue without any separate index or product configuration.
2. Country-Specific Catalogues (Multi-site)
A single Commerce instance serves multiple storefronts — for example, example.com.tr and example.com.fr. Products carry an available_country attribute set to tr or fr. Each storefront's BFF applies the appropriate filter on every outgoing request so users never see products meant for another market.
TR site — JSON payload:
FR site — JSON payload:
omnife GLOBAL_HEADERS_FUNCTION example (TR site settings):
GLOBAL_HEADERS_FUNCTION is called for every request omnife forwards to the Commerce project. The function receives the current Django request object, so the header value can be built from session data, request metadata, or any other runtime context.
3. Recently Viewed Products
A "Continue browsing" section on the homepage needs to retrieve the last few products a user viewed, preserving their original order. The client stores the viewed product PKs (e.g. in a cookie or localStorage) and sends them as a filter.
JSON payload:
curl example:
To return results in exactly the order the PKs were listed, combine with default_sorting_deactivated (see Use Case 6).
4. Products You've Already Ordered
A "Your Previous Purchases" page shows only the products a logged-in user has ordered before. The client fetches the user's order history from the /users/orders/ endpoint, collects the product IDs, and sends them as a filter so Commerce returns only those products with their current stock and pricing.
JSON payload:
curl example:
5. Filtering with Multiple Condition Groups (_any_of)
Use _any_of when you need OR logic across different fields. Each object inside _any_of is evaluated with AND logic; the objects themselves are combined with OR logic. In the example below, the request returns products that are either (Red AND size M) or (Blue AND size L).
JSON payload:
curl example:
6. Editorial Product Ordering
A merchandiser wants a curated landing page that shows three specific products in a fixed, hand-picked order — not ranked by relevance or popularity. Setting default_sorting_deactivated to true inside the filter payload disables the default sort and returns products in the order their PKs appear in the list.
JSON payload:
curl example:
7. Combining Filter and Exclude
Both headers can be used together in the same request. In the example below, the result set is narrowed to TR market products and a specific brand is suppressed from those results.
Filter payload:
Exclude payload:
curl example:
Field Name Reference
Product PK
products.pk
[101, 102]
SKU
products.sku.raw
["ABC-001"]
Base code
products.base_code.raw
["BASE-001"]
Category IDs
products.category_ids
[12, 45]
Product attribute
products.attributes_{key}
products.attributes_color
Product attribute keys come from the key field on the Attribute object in Commerce. An attribute must have is_filterable = true set before it can be used as a filter or exclude condition.
Client Implementation Notes
The feature is client-agnostic. Any HTTP client that can set request headers can use it.
Generic pattern (any client):
Build a JSON object with the desired conditions.
Encode it with base64.
Attach the result as
X-SEARCH-DYNAMIC-FILTERand/orX-SEARCH-DYNAMIC-EXCLUDEon the request to the Commerce/list/endpoint.
omnife BFF — site-wide headers via GLOBAL_HEADERS_FUNCTION:
Implement the function in your omnife project's settings file. It receives the Django request object and must return a plain dict. Any key-value pairs in that dict are merged into the default headers omnife sends to the Commerce project on every request.
omnife BFF — per-request headers (pass-through):
If the storefront or mobile client already sends the dynamic filter header, omnife passes it through to the Commerce project automatically when SEARCH_DYNAMIC_FILTER_ACTIVE=True. No additional configuration is needed on the BFF side for this case.
Last updated
Was this helpful?

