> ## Documentation Index
> Fetch the complete documentation index at: https://help.experro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Search Products

> Primary product-search endpoint for the storefront. Returns the matching products, along with facets, banners, and result metadata.

Facet filters, structured filters, sorting, pagination, out-of-stock handling, variation-map grouping, and personalization context are all set in the request body.

Before calling this endpoint, make sure you've generated an API token and picked the correct domain. See [Authentication & Base URLs](/api-reference/authbaseurl).

Send `facets`, `filters`, and other structured fields as real JSON here. The [GET variant](/api-reference/discovery/search/get) accepts the same fields but requires them JSON-encoded into the query string.


## OpenAPI

````yaml POST /discovery/search
openapi: 3.1.0
info:
  title: OpenAPI Plant Store
  description: >-
    A sample API that uses a plant store as an example to demonstrate features
    in the OpenAPI specification
  license:
    name: MIT
  version: 1.0.0
servers:
  - url: https://apis.experro.app
    description: Admin Server
  - url: https://{base-address}
    description: Custom Server
    variables:
      base-address:
        default: ''
        description: Enter your custom domain or server address.
security:
  - bearerAuth: []
paths:
  /discovery/search:
    post:
      summary: Search Products
      description: >-
        Primary product-search endpoint for the storefront. Returns the matching
        products, along with facets, banners, and result metadata.


        Facet filters, structured filters, sorting, pagination, out-of-stock
        handling, variation-map grouping, and personalization context are all
        set in the request body.
      parameters:
        - $ref: '#/components/parameters/StorefrontTenantIdHeader'
        - $ref: '#/components/parameters/StorefrontWorkspaceIdHeader'
        - $ref: '#/components/parameters/StorefrontEnvironmentIdHeader'
        - name: catalog_id
          in: query
          description: The catalog to search. Every request is scoped to a single catalog.
          required: true
          schema:
            type: string
            format: uuid
        - name: q
          in: query
          description: >-
            The shopper's search term. Leave it out to browse instead of search
            — combine that with `category_id` or `collection_id` to list
            everything in a category or collection.
          schema:
            type: string
        - name: locale
          in: query
          schema:
            type: string
          description: >-
            Locale to run the search in, such as `en-us`. Determines which
            localized product content is matched and returned.
        - name: category_id
          in: query
          schema:
            type: string
          description: >-
            Restrict results to a single category, identified by its id in your
            commerce platform.
        - name: collection_id
          in: query
          schema:
            type: string
            format: uuid
          description: >-
            Restrict results to a single Experro collection, identified by its
            Experro collection id.
        - name: category_name
          in: query
          schema:
            type: string
          description: >-
            Restrict results to a single category by name rather than id. Use
            this when you have the category name but not its id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductSearchRequest'
      responses:
        '200':
          description: Success.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SearchResult'
        '400':
          description: >-
            The request could not be parsed or validated. `Invalid Input` almost
            always means a request-body problem rather than a bad field value:


            - The `Content-Type` is missing or is not `application/json`.

            - The body was sent as form data instead of raw JSON.

            - The body is empty, or is not valid JSON.

            - An optional field was sent **blank rather than omitted** — for
            example `"sort_order": ""`, `"hero_variant_option_override": {}`, or
            `"filters": [{}]`. Omit a field you are not using, or send `null` or
            `[]`; an empty string or empty object is rejected.

            - A numeric field was sent as a string, such as `"limit": "24"`
            instead of `"limit": 24`.


            A missing required query parameter returns a more specific message,
            for example `Query deserialize error: missing field \`catalog_id\``.
        '404':
          description: >-
            The catalog was not found. A `catalog_id` that does not exist on
            this domain returns `Invalid Catalog`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: An unexpected error occurred on the server.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      security: []
      servers:
        - url: https://{base-address}
          description: Custom Server
          variables:
            base-address:
              default: ''
              description: Enter your custom domain or server address.
components:
  parameters:
    StorefrontTenantIdHeader:
      name: x-tenant-id
      in: header
      required: true
      schema:
        type: string
      description: Identifies your organization. Ensures data isolation per tenant.
    StorefrontWorkspaceIdHeader:
      name: x-workspace-id
      in: header
      required: true
      schema:
        type: string
      description: >-
        Identifies the workspace within your organization. Scopes resources to a
        specific workspace.
    StorefrontEnvironmentIdHeader:
      name: x-environment-id
      in: header
      required: true
      schema:
        type: string
      description: >-
        Identifies the environment you are targeting. Scopes the call to a
        specific deployment environment.
  schemas:
    ProductSearchRequest:
      type: object
      properties:
        facets:
          type: array
          description: >-
            Facet selections used to narrow the result set. One entry per
            faceted field. A `field` that is not a configured facet on the
            catalog is silently ignored, so check the `facets` returned in the
            response for the field names this catalog uses — a price facet may
            be `calculated_price` rather than `price`.
          items:
            $ref: '#/components/schemas/FacetFilter'
          example:
            - field: brand
              value:
                value: Nike
            - field: price
              value:
                min: 20
                max: 200
        filters:
          type: array
          description: >-
            Filter criteria applied to the result set. Multiple entries are
            combined with **AND**, so for several conditions just add several
            entries. Use a `group` inside an entry only when you need OR logic.
          items:
            $ref: '#/components/schemas/FilterRule'
        fields:
          type: array
          description: >-
            Which product fields to return on each record. Requesting only the
            fields your page renders keeps responses small; omit it to get the
            default set.
          items:
            type: string
        selected_variant_fields:
          type: array
          description: >-
            Which variant fields to return inside `selected_variant` on each
            record.
          items:
            type: string
        skip:
          type: integer
          description: >-
            How many matching products to skip before the first one returned.
            Combine with `limit` to page through results.
        limit:
          type: integer
          description: How many products to return in this page of results.
        sort_by:
          type: string
          description: >-
            Name of the field to sort by, such as `price`. Leave it out to sort
            by relevance.
          example: price
        sort_order:
          type: string
          description: |-
            Which direction to sort in. Only meaningful together with `sort_by`.

            - `asc` — lowest to highest.
            - `desc` — highest to lowest. This is the default.

            Case-insensitive; any other value falls back to descending.
          enum:
            - asc
            - desc
          default: desc
        include_count:
          type: boolean
          description: >-
            Return the total number of matching products in `meta.total_count`.
            Leave it off if you only need the current page.
        is_auto_spell_correction:
          type: boolean
          description: >-
            Correct likely misspellings in `q` before searching, so a query such
            as `shose` still finds shoes.
        is_auto_spell_correction_zero_results:
          type: boolean
          description: >-
            Only apply spell correction when the original query returns nothing.
            Use this when you would rather show exact matches first and fall
            back to correction.
        did_you_mean_limit:
          type: integer
          description: >-
            How many "did you mean" alternatives to return in
            `meta.did_you_mean`.
        session_id:
          type: string
          description: >-
            Identifier for the shopper's current session. Groups their search
            activity for analytics and personalization.
        user_id:
          type: string
          description: >-
            Identifier for the shopper, used for personalization and
            recent-search history. This is the body field, separate from any
            `x-user-id` header.
        currency:
          type: string
          description: Currency to return prices in, such as `USD`.
        currency_conversion_rate:
          type: number
          description: >-
            Rate used to convert prices into `currency` when the catalog stores
            them in a different one.
        location:
          type: string
          description: >-
            Shopper location, used for location-aware results such as
            store-specific inventory or pricing.
        price_group:
          type: string
          description: >-
            Price group to resolve prices against, for customer-group or B2B
            pricing.
        out_of_stock:
          type: string
          description: |-
            What to do with products that are out of stock.

            - `Include` — included and ranked normally.
            - `Exclude` — removed from results entirely.
            - `Burry` — included but ranked lower.
          enum:
            - Include
            - Exclude
            - Burry
        auto_redirect:
          type: boolean
          description: >-
            Apply any configured query redirect that matches `q`. When one
            matches, the target is returned in `meta.redirect_url` so your
            storefront can redirect the shopper.
        skip_facet_generation:
          type: boolean
          description: >-
            Skip building facets for this request. Facets come back null and the
            response is faster — useful when you only need product records.
        variation_map_group_by:
          type: array
          description: >-
            Group variants of the same product together so you can render one
            card per color or size rather than one per variant.
          items:
            $ref: '#/components/schemas/VariationMapGroupBy'
        variation_map_group_values:
          type: array
          description: >-
            Which values to compute for each variation group, such as the first
            image or the lowest price. Results appear in `variants_map` on each
            record.
          items:
            $ref: '#/components/schemas/VariationMapGroupValue'
        hero_variant_option_override:
          $ref: '#/components/schemas/HeroVariantOptionOverride'
          description: >-
            Force which variant option is shown as the product's hero image,
            overriding the normal selection logic.
    SearchResult:
      type: object
      description: The product-search payload. Returned at the top level, with no envelope.
      properties:
        records:
          type: array
          description: Ranked product records for the current page.
          items:
            $ref: '#/components/schemas/SearchRecord'
        facets:
          type:
            - array
            - 'null'
          description: >-
            Facets generated for the result set. Null when
            `skip_facet_generation` is true.
          items:
            $ref: '#/components/schemas/SearchFacet'
        banners:
          type:
            - array
            - 'null'
          description: Banners to render alongside the results. Null when none apply.
          items:
            $ref: '#/components/schemas/SearchBanner'
        meta:
          $ref: '#/components/schemas/SearchMeta'
      additionalProperties: false
    Error404:
      type: object
      properties:
        Status:
          type: string
          example: failure
        Error:
          type: object
          properties:
            message:
              type: string
            name:
              type: string
            code:
              type: string
          required:
            - message
            - name
      required:
        - Status
        - Error
    Error500:
      type: object
      properties:
        Status:
          type: string
          example: failure
        Error:
          type: object
          properties:
            message:
              type: string
            name:
              type: string
            code:
              type: string
          required:
            - message
            - name
      required:
        - Status
        - Error
    FacetFilter:
      type: object
      description: >-
        One entry per faceted field you want to filter by. Use `value.value` for
        a categorical facet, or `value.min` and `value.max` for a numeric range
        facet. The two forms are mutually exclusive.
      properties:
        field:
          type: string
          description: Faceted field name.
          example: brand
        value:
          type: object
          description: >-
            Categorical facet: set `value` and omit `min` and `max`. Range
            facet: set `min` and `max` and omit `value`. Sending `null` for the
            unused keys works too.
          properties:
            value:
              type:
                - string
                - 'null'
              description: Selected option for a categorical facet.
              example: Nike
            min:
              type:
                - number
                - 'null'
              description: Lower bound for a range facet.
              example: 20
            max:
              type:
                - number
                - 'null'
              description: Upper bound for a range facet.
              example: 100
          additionalProperties: false
      required:
        - field
        - value
      examples:
        - field: brand
          value:
            value: Nike
        - field: price
          value:
            min: 20
            max: 200
      additionalProperties: false
    FilterRule:
      type: object
      description: >-
        One entry in the `filters` array.


        For a plain condition, fill `action`, `field`, and `value` and leave
        `group` out. **Multiple top-level entries are combined with AND**, so to
        filter on several conditions just add more entries — you do not need a
        group.


        For OR logic, fill `group` instead and leave `action`, `field`, and
        `value` empty.


        Do not leave an entry completely empty; an empty object is rejected with
        `400 Invalid Input`.
      properties:
        action:
          type: string
          description: Comparison applied between `field` and `value`.
          enum:
            - equals
            - not_equals
            - greater_than
            - less_than
            - greater_than_or_equal
            - less_than_or_equal
            - contains
            - not_contains
          example: equals
        field:
          type: string
          description: Field the condition applies to.
          example: brand
        value:
          description: >-
            Value to compare against. A single value for a plain condition
            (`"Nike"`), a two-element `[min, max]` array for a range condition
            (`[20, 100]`), or an array of values for a membership condition
            (`["red", "blue", "black"]`).
          example: Nike
        group:
          type: object
          description: >-
            Use a group **only when you need OR logic**. Separate top-level
            entries are already combined with AND, so a group is unnecessary for
            AND filters. Leave this out entirely for a plain condition.
          properties:
            operator:
              type: string
              description: How the rules inside this group combine.
              enum:
                - and
                - or
              example: or
            rules:
              type: array
              description: Conditions inside this group.
              items:
                $ref: '#/components/schemas/FilterCondition'
          additionalProperties: false
      examples:
        - action: equals
          field: brand
          value: Nike
        - action: greater_than_or_equal
          field: price
          value:
            - 20
            - 100
        - action: equals
          field: color
          value:
            - red
            - blue
            - black
        - group:
            operator: or
            rules:
              - action: equals
                field: brand
                value: Nike
              - action: greater_than
                field: price
                value: 50
      additionalProperties: false
    VariationMapGroupBy:
      type: object
      description: >-
        Groups variant results by a product attribute for variant and swatch
        mapping, for example grouping by color.
      properties:
        name:
          type: string
          description: Name for the group in the response.
          example: Color
        field:
          type: string
          description: Product attribute to group by.
          example: color
      required:
        - name
        - field
      additionalProperties: false
    VariationMapGroupValue:
      type: object
      description: >-
        Requests a computed aggregate value per variation group, for example the
        minimum price per color.
      properties:
        name:
          type: string
          description: Name for the value in the response.
          example: min_price
        aggregation:
          type: string
          description: Aggregate function computed per group.
          enum:
            - First
            - Last
            - Min
            - Max
            - Count
            - Avg
            - Sum
            - FieldCount
            - ValueCount
          example: Min
        field:
          type: string
          description: Source field to aggregate.
          example: price
        value:
          type:
            - string
            - 'null'
          description: Reserved. Send null.
      required:
        - name
        - aggregation
        - field
      additionalProperties: false
    HeroVariantOptionOverride:
      type: object
      description: >-
        Forces which variant option is shown as the hero, meaning the default
        image, for a product. Overrides the normal selection logic.
      properties:
        field:
          type: string
          description: Variant option field.
          example: color
        value:
          type: string
          description: Option value to treat as the hero.
          example: Red
      required:
        - field
        - value
      additionalProperties: false
    SearchRecord:
      type: object
      description: >-
        One product in the result set. Which fields appear depends on the
        `fields` you request; only `id` and `ranking_info` are always present.
      properties:
        id:
          type: string
          description: Experro product id.
        name:
          type: string
          description: Product name.
        sku:
          type: string
          description: Product SKU.
        provider_id:
          type: string
          description: Product id in the source commerce platform.
        brand:
          type: string
          description: Brand name.
        description:
          type: string
          description: Product description.
        url:
          type: string
          description: Product URL, relative to your storefront.
        primary_image_url:
          type: string
          description: Main product image.
        type:
          type: string
          description: Product type, for example `physical`.
        categories:
          type: array
          description: Category names the product belongs to.
          items:
            type: string
        category_ids:
          type: array
          description: Category ids the product belongs to.
          items:
            type: string
        tags:
          type: array
          description: Product tags.
          items:
            type: string
        calculated_price:
          type: number
          description: Price after any applicable rules.
        reviews_count:
          type:
            - number
            - 'null'
          description: Number of reviews.
        option_color:
          description: >-
            Color options. A string on some catalogs and an array of strings on
            others, depending on how the field is configured.
        selected_variant:
          $ref: '#/components/schemas/SearchSelectedVariant'
        variants_map:
          type: array
          description: >-
            One entry per variation group, present only when you send
            `variation_map_group_by`. The keys inside each entry are the `name`
            values you chose in `variation_map_group_values`, so this object is
            shaped by your request rather than fixed.
          items:
            $ref: '#/components/schemas/SearchVariantMapEntry'
        ranking_info:
          $ref: '#/components/schemas/SearchRankingInfo'
      additionalProperties: true
    SearchFacet:
      type: object
      description: A facet generated for the result set, with its display configuration.
      properties:
        name:
          type: string
          description: Display name, for example `Brand`.
        field:
          type: string
          description: Underlying field, for example `brand`.
        appearance:
          type: string
          description: How the facet is rendered, for example `terms`.
        values:
          type: array
          description: Available values with their result counts.
          items:
            $ref: '#/components/schemas/SearchFacetValue'
        range:
          type:
            - object
            - 'null'
          description: Bounds for a range facet. Null for a term facet.
        enable_multi_select:
          type: boolean
          description: Whether several values can be selected at once.
        items_to_show:
          type: integer
          description: How many values to show before a "show more" control.
        show_count:
          type: boolean
          description: Whether to display the result count beside each value.
        show_search_within_facet:
          type: boolean
          description: Whether to offer a search box inside the facet.
        default_collapse_on_desktop:
          type: boolean
          description: Whether the facet starts collapsed on desktop.
        default_collapse_on_mobile:
          type: boolean
          description: Whether the facet starts collapsed on mobile.
        display_tooltip:
          type: boolean
          description: Whether to show a tooltip.
        tooltip:
          type: string
          description: Tooltip text.
        layout_style:
          type: string
          description: Layout style.
        display_layout:
          type: string
          description: Display layout.
        star_color:
          type: string
          description: Star color, used by rating facets.
      additionalProperties: true
    SearchBanner:
      type: object
      description: A banner to render within the results.
      properties:
        id:
          type: string
          description: Banner id.
        position:
          type: integer
          description: 1-based result position the banner occupies.
        repeat_on_pagination:
          type: boolean
          description: Whether the banner repeats on later pages.
        banner:
          type: object
          description: Banner content and per-device configuration.
          properties:
            hide_on_desktop:
              type: boolean
              description: Hide on desktop.
            hide_on_tablet:
              type: boolean
              description: Hide on tablet.
            hide_on_mobile:
              type: boolean
              description: Hide on mobile.
            desktop_config:
              type: object
              description: Desktop layout and click behavior.
              additionalProperties: true
            tablet_config:
              type: object
              description: Tablet layout and click behavior.
              additionalProperties: true
            mobile_config:
              type: object
              description: Mobile layout and click behavior.
              additionalProperties: true
          additionalProperties: true
      additionalProperties: true
    SearchMeta:
      type: object
      description: Result metadata for the query.
      properties:
        total_count:
          type: integer
          description: Total matching products. Present when `include_count` is true.
        result_source:
          type: string
          description: Which search path produced the results, for example `keyword`.
        did_you_mean:
          type:
            - array
            - 'null'
          description: Did-you-mean suggestions.
          items:
            type: string
        corrected_search_term:
          type:
            - string
            - 'null'
          description: Term used after spell correction.
        auto_spell_correction_applied:
          type: boolean
          description: Whether spell correction changed the query.
        redirect_url:
          type:
            - string
            - 'null'
          description: URL to redirect to, when a query redirect matched.
        tagged_attributes:
          type: array
          description: Attributes detected in the query by NLP.
          items:
            type: object
        merchandising_rules_applied:
          type: array
          description: Merchandising rules that affected this result set.
          items:
            $ref: '#/components/schemas/SearchAppliedRule'
        is_price_filter_dropped:
          type: boolean
          description: Whether a price filter was dropped to avoid zero results.
        experiments:
          type:
            - object
            - 'null'
          description: Experiment assignment for this request.
      additionalProperties: true
    FilterCondition:
      type: object
      description: A single condition inside a filter group.
      properties:
        action:
          type: string
          description: Comparison applied between `field` and `value`.
          enum:
            - equals
            - not_equals
            - greater_than
            - less_than
            - greater_than_or_equal
            - less_than_or_equal
            - contains
            - not_contains
          example: equals
        field:
          type: string
          description: Field the condition applies to.
          example: brand
        value:
          description: >-
            Value to compare against. A single value for a plain condition
            (`"Nike"`), a two-element `[min, max]` array for a range condition
            (`[20, 100]`), or an array of values for a membership condition
            (`["red", "blue", "black"]`).
          example: Nike
      required:
        - action
        - field
        - value
      additionalProperties: false
    SearchSelectedVariant:
      type: object
      description: >-
        The variant chosen for display. Fields follow the
        `selected_variant_fields` you request.
      properties:
        id:
          type: string
          description: Variant id.
        name:
          type: string
          description: >-
            Variant name, typically the option values joined, such as color and
            size.
        sku:
          type: string
          description: Variant SKU.
        provider_id:
          type: string
          description: Variant id in the source commerce platform.
        brand:
          type: string
          description: Brand name.
        description:
          type: string
          description: Variant description.
        primary_image_url:
          type: string
          description: Variant image.
        categories:
          type: array
          description: Category names.
          items:
            type: string
        option_color:
          type: string
          description: Color of this variant.
        calculated_price:
          type: number
          description: Variant price.
      additionalProperties: true
    SearchVariantMapEntry:
      type: object
      description: >-
        One variation group. Property names are the `name` values from your
        `variation_map_group_values`, so they vary per request.
      additionalProperties: true
      example:
        color: <string>
        sku: <string>
        price: 123
        swatch_color: <string>
        primary_image_url: <string>
        product_url: <string>
    SearchRankingInfo:
      type: object
      description: >-
        Scores and position for this record. The individual score components are
        null unless the corresponding ranking signal ran.
      properties:
        overall_score:
          type:
            - number
            - 'null'
          description: Combined score used for ordering.
        relevance_score:
          type:
            - number
            - 'null'
          description: Relevance component.
        merchandising_score:
          type:
            - number
            - 'null'
          description: Merchandising component.
        performance_score:
          type:
            - number
            - 'null'
          description: Performance component.
        personalization_score:
          type:
            - number
            - 'null'
          description: Personalization component.
        newness_score:
          type:
            - number
            - 'null'
          description: Newness component.
        position:
          type: integer
          description: 1-based position of this record in the result set.
        source:
          type: string
          description: Where the record came from, for example `Original`.
        result_from:
          type: string
          description: Which stage produced the record.
      additionalProperties: true
    SearchFacetValue:
      type: object
      description: One selectable value within a facet.
      properties:
        value:
          type:
            - string
            - 'null'
          description: Value to send back in `facets` to filter by it.
        display_value:
          type:
            - string
            - 'null'
          description: Label to show the shopper.
        count:
          type: integer
          description: Number of matching products.
        image_url:
          type:
            - string
            - 'null'
          description: Image for a visual facet.
        color:
          type:
            - string
            - 'null'
          description: Swatch color.
        min:
          type:
            - number
            - 'null'
          description: Lower bound, for a range facet.
        max:
          type:
            - number
            - 'null'
          description: Upper bound, for a range facet.
      additionalProperties: true
    SearchAppliedRule:
      type: object
      description: A merchandising rule that was applied.
      properties:
        id:
          type: string
          description: Rule id.
        variant_id:
          type:
            - string
            - 'null'
          description: Experiment variant of the rule, when one applied.
      additionalProperties: true
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.