In Design B2B catalogs you used Product Selections and Stores to control which Products a buyer sees. Each buyer also trades through a distribution Channel that carries their negotiated Price, which you configure in Configure B2B pricing; for search, what matters is that this Channel is the key you scope prices by. Search is where those two decisions become visible to the buyer: the same query, run for Horizon Hotels and for Pacific Property Group, must return each buyer's assortment at each buyer's price. This page shows how to express that scoping with Product Search. For the storefront mechanics of Product Search, such as faceting, filtering, and pagination, see the Implement product discovery and presentation path.
Use the GraphQL productsSearch query
Product Search is ID-first, so a raw search returns Product IDs and you fetch the data to render separately. The recommended way to do both in one request is the GraphQL
productsSearch query: you search and select only the fields you need for the catalog page in a single call, which keeps payloads small.The older REST data-integration path, the
productProjectionParameters field on ProductSearchRequest (the ProductSearchProjectionParams type, with priceCurrency, priceChannel, and priceCustomerGroup), was deprecated in December 2025. Use the GraphQL productsSearch query for new B2B implementations. The productsSearch query is currently in public beta.Two concerns: scope the match, then resolve the price
B2B search scoping is two separate mechanisms, and keeping them apart is the key to getting it right:
- Scope which variants match: constrain the search result to the buyer's context using
exactexpressions in thequeryon searchable price fields:variants.prices.channel,variants.prices.customerGroup,variants.prices.currencyCode, andvariants.prices.country. Assortment scope usesstores,variants.stores,productSelections, andvariants.productSelections. - Resolve the price to display: select the variant's
price(...)field in the GraphQL response, passing the same context as price-selection arguments (currency,country,channelId,customerGroupId). This returns the single Price the buyer should see, applying the same price selection the platform uses at Cart time.
Filtering decides which Products appear; the
price field decides what number is shown for each. A query that filters by Channel but forgets to resolve the price would return the right Products with no price; one that resolves the price but forgets to filter would show every Product, priced for one buyer.Worked example: Horizon Hotels' scoped catalog
Horizon Hotels trades through the
horizon-hotels-pricing distribution Channel (id chan-hh-001) in AUD. The following productsSearch query returns laptops in their assortment, each with the negotiated Channel-scoped Price resolved for display.{
productsSearch(
query: {
and: [
{ fullText: { field: "name", value: "laptop", language: "en-AU" } }
{ exact: { field: "variants.prices.channel", value: "chan-hh-001" } }
{ exact: { field: "variants.prices.currencyCode", value: "AUD" } }
]
}
limit: 20
) {
total
results {
product {
masterData {
current {
masterVariant {
sku
price(currency: "AUD", channelId: "chan-hh-001") {
value {
centAmount
currencyCode
}
}
}
}
}
}
}
}
}
Reading the query:
- The
andblock combines a full-text match onnamewith twoexactfilters that scope the match to Products having anAUDPrice on Horizon Hotels' Channel. - The
price(currency: "AUD", channelId: "chan-hh-001")selection resolves the price to the buyer's negotiated Channel-scoped Price. The argument names arechannelIdandcustomerGroupId(the resource IDs), not the resource objects. - Selecting only
skuandpricekeeps the response small because the ID-first design rewards requesting only what the catalog page renders.
The example selects the
masterVariant to keep the query compact. In a production catalog, confirm that the variant you display is the one the buyer's search matched. Product Search can match only some Product Variants when you filter on variants.* fields, so use matching-variant handling when the catalog page must show the exact variant that carried the buyer-scoped Price.To scope a different buyer, change the Channel id (and add
customerGroupId if the buyer's pricing is Customer-Group-scoped) in both the filter and the price selection. Run the identical shape for Pacific Property Group's Channel and the same catalog returns that buyer's prices instead.Connecting search to assortment
Filtering by price scopes pricing; it does not by itself limit the catalog to a buyer's permitted assortment. That limit comes from the Store and Product Selection scope you built in Module 3, exposed in the search index as
stores, variants.stores, productSelections, and variants.productSelections. To return only Products available in a buyer's Store, add an exact filter on variants.stores (or stores) for that Store's id.{ exact: { field: "variants.stores", value: "store-hh-au" } }
The mental model: Product Selections and Stores decide which Products are in scope; Channel and Customer Group decide the price; Product Search combines both into one buyer-specific catalog view.
Product Search must be activated for the Project before it returns results, and it deactivates automatically after 30 days with no search calls. If search requests fail with an
ObjectNotFound error, the API is not enabled. Activate it in the Merchant Center under Settings > Project settings > Storefront Search, or with the Change Product Search Indexing Enabled update action.Key takeaways
- Use the GraphQL
productsSearchquery to search and fetch only the product fields you need in one call; the RESTproductProjectionParameterspath is deprecated. - B2B scoping is two mechanisms:
exactfilters onvariants.prices.*and assortment fields scope which variants match, and the variantprice(...)field resolves what price to display. - Pass price context as
currency,country,channelId, andcustomerGroupIdarguments to thepricefield; they take resource IDs. - When filters target
variants.*fields, make sure the catalog page displays the matched Product Variant, not an unrelated variant from the same Product. - Scope a buyer's assortment with
variants.stores/storesandvariants.productSelections/productSelections, the index fields backed by Module 3's Store and Product Selection design. - Product Search must be activated and stays active only while it is queried; an unconfigured API returns
ObjectNotFound.