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

# Get Top Recommendations

> Fetch products by a ranking strategy (bestsellers, new arrivals, trending) with rich criteria filters, full-text query, cursor pagination, and facet aggregations.

## Overview

The **top recommendations** endpoint lets headless storefronts fetch products **without a seed anchor** — unlike `/recommendations` and `/recommendations/automatic`, no `product_ids` are required. It is the right endpoint for:

* Catalog / collection landing pages ("Bestsellers", "New arrivals", "Trending now")
* Search results constrained by a strategy (e.g. trending products matching "wireless charger")
* Merchandising widgets that need filtered top-N lists with facet sidebars

The filter contract is **identical to `/recommendations/automatic`** — same `filter` shape, same Joi schema — extended with four additional dimensions (`excluded_tags`, `collections`, `product_types`, `price`). Those extensions are also accepted by the automatic endpoint going forward.

<Note>
  **Authentication.** Pass `Authorization: Bearer <shop storefront token>` and `x-shop: <myshopify domain>` on every call. See [Get Started → Headless Authentication](/api-reference/recommendations/headless/init) for token provisioning.
</Note>

## Ranking strategies

| Strategy       | What it returns                                                                                                              |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `BESTSELLERS`  | Products ranked by revenue over the last 7, 15, or 30 days (selectable via `strategy_options.sales_time_period`, default 7). |
| `NEW_ARRIVALS` | Most recently published products.                                                                                            |
| `TRENDING`     | Products currently gaining popularity based on recent sales velocity and acceleration.                                       |

## Filter dimensions

`filter` is fully optional. Each sub-field is independently optional and combined as an AND:

* **`tags`** / **`excluded_tags`** — include / exclude by product tag.
* **`vendors`** — vendor names (exact match).
* **`product_types`** — product type values.
* **`collections`** — collection IDs the product must belong to.
* **`price.min`** / **`price.max`** — price range in shop currency.
* **`variants.is_in_stock`**, **`variants.available_for_sale`** — variant-level availability (matches any variant).
* **`variants.options[]`** — variant option matches (e.g. `Color: Black`).
* **`variants.sku[]`** — variant SKU match.
* **`metafields[]`** — match by `{namespace, key, value}`; requires a `filterKey` to exist on the indexed product.

<Tip>
  **Array or comma-separated string.** The list-style fields `tags`, `excluded_tags`, `vendors`, `product_types`, and `collections` accept **either** a JSON array of strings **or** a single comma-separated string. Useful for clients whose query serializers can't construct arrays from variables. Both forms below are equivalent:

  ```json theme={null}
  { "filter": { "tags": ["new", "sale", "featured"] } }
  { "filter": { "tags": "new,sale,featured" } }
  ```

  Whitespace around commas is trimmed.
</Tip>

## Text query

When `query` is provided, a `multi_match` clause is added across `title^3`, `description`, `tags`, `vendor`, `productType` with `operator: AND`, `lenient: true`. The **strategy sort still wins** — products matching the text query are ranked by sales (or recency / trending score), not by BM25 relevance. For relevance-first text search, use the [AI Search](/api-reference/search/introduction) endpoint instead.

## Facets

Pass `facets: ["vendor", "product_type", "tag", "price"]` (any subset) to receive aggregations alongside the products. Each facet returns top values with counts. The `price` facet returns fixed buckets (`0-50`, `50-100`, `100-250`, `250+`).

Facet counts are computed using a **post-filter** model — each facet's counts ignore that dimension's filter so a sidebar UI can show "all available vendors" even when the user has selected one. Other filters still apply.

## Pagination

Cursor-based, matching the rest of the v3 headless surface. The first request can pass `pagination.limit` (1–100, default 24). Subsequent pages pass the `cursor` returned in the previous response.

<Warning>
  When paginating with a `cursor`, the `filter`, `query`, and `strategy` must **stay identical** to the request that produced the cursor — changing them invalidates the sort context and returns incoherent ordering.
</Warning>

The response includes `pagination.total` and `pagination.totalRelation` (`"eq"` or `"gte"`). Elasticsearch caps totals at 10,000 by default — when the relation is `"gte"`, the real total is ≥ that value.

## Examples

<CodeGroup>
  ```bash Minimal — Bestsellers theme={null}
  curl -sS -X POST 'https://storefront.glood.ai/api/storefront/v3/headless/recommendations/top' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer <shop-storefront-token>' \
    -H 'x-shop: shop.myshopify.com' \
    -d '{
      "strategy": "BESTSELLERS",
      "view": "product_details"
    }'
  ```

  ```bash New Arrivals + text + price filter theme={null}
  curl -sS -X POST 'https://storefront.glood.ai/api/storefront/v3/headless/recommendations/top' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer <shop-storefront-token>' \
    -H 'x-shop: shop.myshopify.com' \
    -d '{
      "strategy": "NEW_ARRIVALS",
      "view": "product_details",
      "query": "wireless charger",
      "filter": {
        "price": { "min": 20.00, "max": 80.00 },
        "variants": { "is_in_stock": true }
      },
      "facets": ["vendor", "price"],
      "pagination": { "limit": 24 }
    }'
  ```

  ```bash Trending — full filter surface theme={null}
  curl -sS -X POST 'https://storefront.glood.ai/api/storefront/v3/headless/recommendations/top' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer <shop-storefront-token>' \
    -H 'x-shop: shop.myshopify.com' \
    -d '{
      "strategy": "TRENDING",
      "view": "product_details",
      "query": "cable",
      "filter": {
        "tags": ["holiday-2026"],
        "excluded_tags": ["clearance", "discontinued"],
        "vendors": ["UGREEN"],
        "product_types": ["Cable", "Hub"],
        "collections": ["345678901", "456789012"],
        "metafields": [
          { "namespace": "custom", "key": "gender", "value": "unisex" }
        ],
        "variants": {
          "is_in_stock": true,
          "available_for_sale": true,
          "options": [{ "name": "Length", "value": "2m" }]
        },
        "price": { "min": 5.00, "max": 50.00 }
      },
      "facets": ["vendor", "product_type", "tag", "price"],
      "pagination": { "limit": 24 }
    }'
  ```

  ```bash Bestsellers — 30-day window theme={null}
  curl -sS -X POST 'https://storefront.glood.ai/api/storefront/v3/headless/recommendations/top' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer <shop-storefront-token>' \
    -H 'x-shop: shop.myshopify.com' \
    -d '{
      "strategy": "BESTSELLERS",
      "strategy_options": { "sales_time_period": 30 },
      "view": "product_details",
      "filter": { "collections": ["123456789"] },
      "pagination": { "limit": 12 }
    }'
  ```

  ```bash Paginate — second page via cursor theme={null}
  curl -sS -X POST 'https://storefront.glood.ai/api/storefront/v3/headless/recommendations/top' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer <shop-storefront-token>' \
    -H 'x-shop: shop.myshopify.com' \
    -d '{
      "strategy": "BESTSELLERS",
      "view": "product_details",
      "filter": { "price": { "min": 20.00, "max": 80.00 } },
      "pagination": {
        "cursor": "eyJzIjpbMTIzLjQ1LCI5ODc2NTQzMjEiXSwic2Vzc2lvbiI6IjU1ZGE0NjM5LTUyMTYtNGE4Mi1iMmNlLTUxYTU3MmZjMzVjNCJ9",
        "limit": 24
      }
    }'
  ```

  ```bash Filter lists as CSV strings theme={null}
  curl -sS -X POST 'https://storefront.glood.ai/api/storefront/v3/headless/recommendations/top' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer <shop-storefront-token>' \
    -H 'x-shop: shop.myshopify.com' \
    -d '{
      "strategy": "BESTSELLERS",
      "view": "product_details",
      "filter": {
        "tags": "new,sale,featured",
        "excluded_tags": "clearance,discontinued",
        "vendors": "UGREEN,Anker",
        "product_types": "Cable,Hub",
        "collections": "345678901,456789012"
      },
      "pagination": { "limit": 24 }
    }'
  ```

  ```bash IDs only (lightweight) theme={null}
  curl -sS -X POST 'https://storefront.glood.ai/api/storefront/v3/headless/recommendations/top' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer <shop-storefront-token>' \
    -H 'x-shop: shop.myshopify.com' \
    -d '{
      "strategy": "BESTSELLERS",
      "view": "product_ids",
      "pagination": { "limit": 50 }
    }'
  ```

  ```bash Edge endpoint theme={null}
  curl -sS -X POST 'https://edge.glood.ai/api/edge/storefront/v3/headless/recommendations/top' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer <shop-storefront-token>' \
    -H 'x-shop: shop.myshopify.com' \
    -d '{
      "strategy": "TRENDING",
      "view": "product_details",
      "filter": { "variants": { "is_in_stock": true } },
      "facets": ["vendor", "price"],
      "pagination": { "limit": 24 }
    }'
  ```
</CodeGroup>

<ResponseExample>
  ```json 200 theme={null}
  {
    "ok": true,
    "serve_id": "7c3f5e8a-2b14-49c1-a7d3-93f4e2bb1c8e",
    "strategy": "TRENDING",
    "products": [
      {
        "handle": "uno-usb-c-cable-100w",
        "product_id": 8123456789012,
        "title": "Uno USB-C to USB-C Cable 100W",
        "vendor": "UGREEN",
        "product_type": "Cable",
        "tags": ["cable", "usb-c", "fast-charge"],
        "is_available": true,
        "price": 9.34,
        "compare_at_price": 14.99,
        "image": "https://cdn.shopify.com/s/files/.../cable.jpg",
        "variants": [],
        "options": []
      }
    ],
    "pagination": {
      "cursor": "eyJzIjpbMC44Mzc3LCI4MTIzNDU2Nzg5MDEyIl0sInNlc3Npb24iOiI1NWRhNDYzOS01MjE2LTRhODItYjJjZS01MWE1NzJmYzM1YzQifQ==",
      "hasMore": true,
      "total": 327,
      "totalRelation": "eq",
      "limit": 24
    },
    "facets": {
      "vendor": [
        { "value": "UGREEN", "count": 142 },
        { "value": "Anker",  "count": 98 }
      ],
      "price": [
        { "min": 0,   "max": 50,  "count": 210 },
        { "min": 50,  "max": 100, "count": 78 },
        { "min": 100, "max": 250, "count": 39 }
      ]
    }
  }
  ```

  ```json 400 theme={null}
  {
    "errors": [
      {
        "message": "\"strategy\" is required",
        "path": ["strategy"],
        "type": "any.required",
        "context": { "label": "strategy", "key": "strategy" }
      }
    ]
  }
  ```

  ```json 401 theme={null}
  {
    "error": "Invalid authentication"
  }
  ```

  ```json 429 theme={null}
  {
    "error": "Too many requests, please try again later."
  }
  ```
</ResponseExample>


## OpenAPI

````yaml POST /api/storefront/v3/headless/recommendations/top
openapi: 3.0.1
info:
  title: Glood.AI API Reference
  description: Glood.AI API Reference
  license:
    name: MIT
  version: 1.0.0
servers:
  - url: https://storefront.glood.ai
security:
  - bearerAuth: []
paths:
  /api/storefront/v3/headless/recommendations/top:
    post:
      description: >-
        Fetch products by a ranking strategy (BESTSELLERS, NEW_ARRIVALS,
        TRENDING) with rich criteria filters, full-text query, cursor
        pagination, and optional facet aggregations. Unlike `/recommendations`
        and `/recommendations/automatic`, this endpoint does not require seed
        `product_ids` — it is the right choice for catalog landing pages, search
        results constrained by a strategy, and merchandising widgets that need
        filtered top-N lists.
      parameters:
        - name: x-shop
          in: header
          description: Shopify store URL
          required: true
          schema:
            type: string
            example: shop.myshopify.com
      requestBody:
        description: Request body
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TopRecommendationsRequest'
      responses:
        '200':
          description: Top recommendations
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TopRecommendationsResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AutomaticRecommendationsError'
components:
  schemas:
    TopRecommendationsRequest:
      type: object
      required:
        - strategy
        - view
      properties:
        strategy:
          type: string
          description: Ranking strategy to apply
          enum:
            - BESTSELLERS
            - NEW_ARRIVALS
            - TRENDING
        strategy_options:
          type: object
          description: Strategy-specific tuning. Currently only BESTSELLERS uses this.
          properties:
            sales_time_period:
              type: integer
              description: BESTSELLERS only. Sales window in days. Defaults to 7.
              enum:
                - 7
                - 15
                - 30
        view:
          type: string
          description: Response shape for each product entry.
          enum:
            - product_details
            - product_ids
        query:
          type: string
          description: >-
            Optional free-text query. Applied as a multi_match across title,
            description, tags, vendor, and product_type. The strategy sort still
            wins; products matching the text query rank by
            sales/recency/trending score, not BM25.
        filter:
          type: object
          description: >-
            Same filter shape as `/recommendations/automatic`, extended with
            `excluded_tags`, `collections`, `product_types`, and `price`.
          properties:
            variants:
              type: object
              properties:
                options:
                  type: array
                  items:
                    type: object
                    required:
                      - name
                      - value
                    properties:
                      name:
                        type: string
                        description: Option name (e.g. Size, Color)
                      value:
                        type: string
                        description: Option value
                is_in_stock:
                  type: boolean
                  description: Match products with at least one in-stock variant.
                sku:
                  type: array
                  items:
                    type: string
                  description: Match products with at least one of the given SKUs.
                available_for_sale:
                  type: boolean
                  description: Match products with at least one variant available for sale.
            tags:
              type: array
              items:
                type: string
              description: >-
                Include products carrying any of these tags. **Also accepts a
                comma-separated string** (e.g. `"new,sale,featured"`) for
                clients whose serializers can't construct arrays.
            excluded_tags:
              type: array
              items:
                type: string
              description: >-
                Exclude products carrying any of these tags. **Also accepts a
                comma-separated string** (e.g. `"clearance,discontinued"`).
            vendors:
              type: array
              items:
                type: string
              description: >-
                Include products from these vendors. **Also accepts a
                comma-separated string** (e.g. `"UGREEN,Anker"`).
            product_types:
              type: array
              items:
                type: string
              description: >-
                Include products of these product types. **Also accepts a
                comma-separated string** (e.g. `"Cable,Hub"`).
            collections:
              type: array
              items:
                type: string
              description: >-
                Include products belonging to any of these collection IDs.
                **Also accepts a comma-separated string** (e.g.
                `"345678901,456789012"`).
            price:
              type: object
              properties:
                min:
                  type: number
                  description: Minimum price (inclusive).
                max:
                  type: number
                  description: Maximum price (inclusive).
            metafields:
              type: array
              items:
                type: object
                required:
                  - namespace
                  - key
                  - value
                properties:
                  namespace:
                    type: string
                  key:
                    type: string
                  value:
                    type: string
              description: >-
                Match products with these metafield namespace/key/value
                combinations.
        facets:
          type: array
          items:
            type: string
            enum:
              - vendor
              - product_type
              - tag
              - price
          description: >-
            Facet dimensions to return alongside the products. Facets are
            computed only when this array is non-empty.
        pagination:
          type: object
          properties:
            cursor:
              type: string
              description: >-
                Cursor from a previous response. When provided, `filter`,
                `query`, and `strategy` must stay identical to the request that
                produced it.
            limit:
              type: integer
              description: Page size (1-100). Default 24.
              minimum: 1
              maximum: 100
    TopRecommendationsResponse:
      type: object
      properties:
        ok:
          type: boolean
          enum:
            - true
        serve_id:
          type: string
          description: Unique identifier for this serve.
        strategy:
          type: string
          description: Echo of the ranking strategy that produced the results.
        products:
          type: array
          items:
            type: object
            description: >-
              Same product object as the other v3 headless endpoints
              (product_details view). With product_ids view, each entry is `{
              product_id }` only.
        pagination:
          type: object
          properties:
            cursor:
              type: string
              nullable: true
              description: Cursor for the next page. `null` when there are no more pages.
            hasMore:
              type: boolean
              description: Whether more pages exist. Equivalent to `cursor !== null`.
            total:
              type: integer
              description: >-
                Total matching products. Capped at 10,000 by ES default — see
                `totalRelation`.
            totalRelation:
              type: string
              enum:
                - eq
                - gte
              description: Whether `total` is exact (`eq`) or a lower bound (`gte`).
            limit:
              type: integer
        facets:
          type: object
          description: >-
            Present only when the request specified `facets`. Each requested
            dimension is a top-level key.
          additionalProperties:
            type: array
            items:
              type: object
              properties:
                value:
                  type: string
                min:
                  type: number
                max:
                  type: number
                count:
                  type: integer
      example:
        ok: true
        serve_id: 7c3f5e8a-2b14-49c1-a7d3-93f4e2bb1c8e
        strategy: TRENDING
        products:
          - product_id: 8123456789012
            title: Uno USB-C to USB-C Cable 100W
            handle: uno-usb-c-cable-100w
            vendor: UGREEN
            product_type: Cable
            price: 9.34
            compare_at_price: 14.99
        pagination:
          cursor: >-
            eyJzIjpbMC44Mzc3LCI4MTIzNDU2Nzg5MDEyIl0sInNlc3Npb24iOiI1NWRhNDYzOS01MjE2LTRhODItYjJjZS01MWE1NzJmYzM1YzQifQ==
          hasMore: true
          total: 327
          totalRelation: eq
          limit: 24
        facets:
          vendor:
            - value: UGREEN
              count: 142
            - value: Anker
              count: 98
          price:
            - min: 0
              max: 50
              count: 210
            - min: 50
              max: 100
              count: 78
            - min: 100
              max: 250
              count: 39
    AutomaticRecommendationsError:
      type: object
      properties:
        errors:
          type: array
          items:
            type: object
            properties:
              message:
                type: string
                description: Human readable error message
              path:
                type: array
                items:
                  type: string
                description: Path to the field that caused the error
              type:
                type: string
                description: Type of validation error
              context:
                type: object
                properties:
                  limit:
                    type: number
                    description: Maximum allowed value
                  value:
                    type: number
                    description: Actual value provided
                  label:
                    type: string
                    description: Field label
                  key:
                    type: string
                    description: Field key
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````