Get filtered products
Fetch filtered/sorted products for a collection or search page, along with the matching filter options and their values, in a single request.
What this does
Returns the product list for a given page (a collection, the search page, or your home page) along with the filter options and values that should be shown alongside it — the same data the app’s own storefront filter sidebar uses, so you can build a fully custom filtering UI against it.
Endpoint
GET /sfs/v1/filterRequires the read:catalog scope. See API access & authentication.
Identifying which page you’re requesting
There’s no required parameter — calling /filter with no params returns your “All Products” filter and its full catalog. To scope the request to a specific page, use page_type and/or collection together:
page_type |
Meaning | collection |
|---|---|---|
collection (or omitted) |
A collection page | Pass its numeric Shopify collection ID to scope to that collection. Omit collection, or pass 0, to mean “All Products”. |
search |
The search results page | Not used. |
index |
The home page | Not used. |
collection must be the plain numeric Shopify collection ID (for example 278901234567) — not a handle, not a gid://... string.
If the page_type/collection combination doesn’t match any filter you’ve configured for that scope, the request still succeeds (200) and returns your products unfiltered, rather than an error — check whether the response’s filters array is empty if you need to detect this case.
Parameters
None are required — see above for what you get back if you omit all of them.
| Parameter | Required | Type | Example | Description |
|---|---|---|---|---|
collection |
Optional | numeric string | 278901234567 |
Shopify collection ID. See above. |
page_type |
Optional | string | collection |
collection, search, or index. See above. |
sort_by |
Optional | string | best-selling |
best-selling, manual, relevance, price-ascending, price-descending, title-ascending, title-descending, created-ascending, created-descending, sale-ascending, sale-descending, published-ascending, published-descending, updated-ascending, updated-descending, stock-descending, inventory-ascending, inventory-descending, or mf-<metafield> for a metafield sort. Defaults to created-descending. Any value not recognized falls back to the default. |
page |
Optional | integer | 1 |
Page number, 1–9999. Defaults to 1. |
limit |
Optional | integer | 24 |
Products per page. Defaults to your store’s configured products-per-page setting; capped at 200 regardless of what you pass. |
q |
Optional | string | red shoes |
Free-text search within the collection/search page. |
filter |
Optional | array | filter[color][]=Red |
Selected filter option values, keyed by filter option ID or handle — mirrors what the storefront filter sidebar sends when a shopper checks a value. |
form |
Optional | array | form[99][]=Toyota |
Vehicle Finder (YMM) field selections, merged into filter. |
form_id |
Optional | integer | 88 |
The Vehicle Finder field-set ID to use with form. Must be a real, existing filter ID — an unrecognized form_id returns an error (see below). |
tags |
Optional | array | tags[]=Sale |
Only include products with these tags. |
exclude_tags |
Optional | array | exclude_tags[]=Hidden |
Exclude products with these tags. |
vendor |
Optional | string | Nike |
Filter to a single vendor. |
type |
Optional | string | Shoes |
Filter to a single product type. |
locale |
Optional | string | fr |
Storefront locale — only applied if your store has more than one language configured. |
currency |
Optional | string | EUR |
Presentment currency — only applied if multi-currency is enabled on your store. |
country |
Optional | string | US |
Country context for Shopify Markets pricing. |
market_id |
Deprecated | string | 123456789 |
No longer used — accepted for backward compatibility but ignored. Rounding and catalog resolution are based on country/company_location_country instead. |
catalog_ids |
Optional | array | catalog_ids[]=1 |
B2B catalog IDs, for stores using B2B catalogs & pricing. |
company_location_id |
Optional | string | 998877 |
B2B company location ID. |
company_location_country |
Optional | string | US |
B2B company location country; falls back to country if omitted. |
rate |
Optional | number | 1.08 |
Currency conversion rate applied to displayed prices. Defaults to 1. |
adjustment |
Optional | number | 1 |
Price adjustment multiplier. Defaults to 1. |
Response
{ "products": [ { "id": ..., "handle": ..., "title": ..., "variants": [...], "...": "..." } ], "filters": [ { "id": ..., "handle": "color", "label": "Color", "attribute": "option:color", "values": [ { "value": "Red", "label": "Red", "count": 12, "selected": false } ] } ], "has_filter": true, "pagination": { "from": 1, "to": 24, "total": 132, "per_page": 24, "current_page": 1, "last_page": 6, "hasMorePages": true }}products— each entry is a full product document (id, handle, title, vendor, product type, tags, options, variants, images, metafields, and similar fields), plus your appliedrate/adjustmentreflected in variant pricing.filters(orformwhen requesting viaform_id) — the filter options relevant to this page, each with its currentvaluesand per-value productcount. APricefilter option additionally includesranges/presentment_rangesinstead ofvalues.pagination— standard page/offset info for the current request.
If your store is still finishing its initial product sync, you’ll get {"processing": 1, "message": "..."} instead — retry after a short delay.
Errors
{"error": 1, "message": "No Filter"}— returned whenform_idis supplied but doesn’t match an existing filter.- See API access & authentication for the shared
401/403/429cases.
Related pages
Was this helpful?
Thanks for your feedback! Tell us what was missing
Report an issue