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

# Products API — Browse and Search the Product Catalog

> List and search customer-visible products, or fetch full product details including variants, pricing, and images. No authentication required.

The Products API gives you access to the full customer-facing product catalog. Use it to build storefront listing pages, search interfaces, and product detail views. Both endpoints are fully public — no session cookie is required. All money values are decimal strings (e.g., `"99.99"`), and all timestamps are ISO 8601 UTC.

***

## List Products

<Note>
  Only products that have at least one **ACTIVE** variant and have not been soft-deleted are included in the public listing. Draft, inactive, and archived variants are invisible to customers.
</Note>

Retrieve a paginated list of products. Optionally filter by brand or free-text search across name, brand, and description. Sort by name or recency.

```
GET /products
```

**Query parameters**

<ParamField query="page" type="integer" default="1">
  Page number (1-based).
</ParamField>

<ParamField query="limit" type="integer" default="20">
  Number of results per page. Minimum `1`, maximum `100`.
</ParamField>

<ParamField query="search" type="string">
  Free-text search across product name, brand, and description (trimmed, max 100 characters).
</ParamField>

<ParamField query="brand" type="string">
  Filter by exact brand name (trimmed, case-sensitive, max 255 characters). Example: `Nike`.
</ParamField>

<ParamField query="sort" type="string" default="-created_at">
  Sort field. Prefix with `-` for descending. Allowed values: `name`, `-name`, `created_at`, `-created_at`, `updated_at`, `-updated_at`.
</ParamField>

**Response fields**

<ResponseField name="success" type="boolean">
  Always `true` on a successful response.
</ResponseField>

<ResponseField name="data" type="array">
  Array of product summary objects.

  <Expandable title="Product summary object">
    <ResponseField name="public_id" type="string">
      Stable public identifier for the product. Pattern: `prd_*`. Use this ID with `GET /products/{product_public_id}`.
    </ResponseField>

    <ResponseField name="name" type="string">
      Display name of the product (e.g., `"Runner Sneaker"`).
    </ResponseField>

    <ResponseField name="slug" type="string">
      URL-safe slug derived from the product name (e.g., `"runner-sneaker"`).
    </ResponseField>

    <ResponseField name="brand" type="string | null">
      Brand name string, or `null` if no brand is set.
    </ResponseField>

    <ResponseField name="primary_image" type="object | null">
      The product's primary image, or `null` if no images have been uploaded.

      <Expandable title="primary_image fields">
        <ResponseField name="image_url" type="string">
          Fully-qualified ImageKit URL of the primary image.
        </ResponseField>

        <ResponseField name="alt_text" type="string | null">
          Alt text for the image, or `null` if not set.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="created_at" type="string">
      ISO 8601 UTC timestamp of when the product was created.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="meta" type="object">
  Pagination metadata.

  <Expandable title="Pagination meta fields">
    <ResponseField name="page" type="integer">Current page number.</ResponseField>
    <ResponseField name="limit" type="integer">Items per page.</ResponseField>
    <ResponseField name="total" type="integer">Total matching products.</ResponseField>
    <ResponseField name="totalPages" type="integer">Total number of pages.</ResponseField>
    <ResponseField name="hasNext" type="boolean">Whether a next page exists.</ResponseField>
    <ResponseField name="hasPrev" type="boolean">Whether a previous page exists.</ResponseField>
  </Expandable>
</ResponseField>

**Example — List the newest Nike products**

<CodeGroup>
  ```bash cURL theme={null}
  curl -G https://api.example.com/api/v1/products \
    --data-urlencode "brand=Nike" \
    --data-urlencode "sort=-created_at" \
    --data-urlencode "limit=20"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.example.com/api/v1/products?brand=Nike&sort=-created_at&limit=20'
  );
  const { data, meta } = await response.json();
  console.log(`Found ${meta.total} products, showing page ${meta.page}`);
  ```
</CodeGroup>

**Example response**

```json theme={null}
{
  "success": true,
  "data": [
    {
      "public_id": "prd_01H",
      "name": "Runner Sneaker",
      "slug": "runner-sneaker",
      "brand": "Nike",
      "primary_image": {
        "image_url": "https://ik.imagekit.io/demo/products/runner-sneaker.jpg",
        "alt_text": "Runner Sneaker front view"
      },
      "created_at": "2024-03-15T10:00:00.000Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 1,
    "totalPages": 1,
    "hasNext": false,
    "hasPrev": false
  }
}
```

***

## Get Product Details

Retrieve a single product by its public ID. The response includes the full image gallery (ordered by `display_order`) and all active variants with computed pricing.

```
GET /products/{product_public_id}
```

**Path parameters**

<ParamField path="product_public_id" type="string" required>
  The product's public ID. Pattern: `prd_*`.
</ParamField>

**Response fields**

<ResponseField name="data" type="object">
  Full product detail object.

  <Expandable title="Product detail fields">
    <ResponseField name="public_id" type="string">
      Public identifier. Pattern: `prd_*`.
    </ResponseField>

    <ResponseField name="name" type="string">
      Product display name.
    </ResponseField>

    <ResponseField name="slug" type="string">
      URL-safe slug.
    </ResponseField>

    <ResponseField name="description" type="string | null">
      Full product description in plain text, or `null` if not set.
    </ResponseField>

    <ResponseField name="brand" type="string | null">
      Brand name, or `null` if not set.
    </ResponseField>

    <ResponseField name="images" type="array">
      All product images, sorted by `display_order` ascending.

      <Expandable title="Image object fields">
        <ResponseField name="public_id" type="string">
          Image identifier. Pattern: `pimg_*`.
        </ResponseField>

        <ResponseField name="image_url" type="string">
          Fully-qualified ImageKit URL.
        </ResponseField>

        <ResponseField name="alt_text" type="string | null">
          Descriptive alt text, or `null` if not set.
        </ResponseField>

        <ResponseField name="display_order" type="integer">
          Sort position (0-based). The image with the lowest display order is shown first.
        </ResponseField>

        <ResponseField name="is_primary" type="boolean">
          `true` for exactly one image per product — the main thumbnail.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="variants" type="array">
      Active variants only (status `ACTIVE`). Each variant represents a purchasable SKU.

      <Expandable title="Variant object fields">
        <ResponseField name="public_id" type="string">
          Variant identifier. Pattern: `var_*`. Use this as `variant_public_id` when adding to cart.
        </ResponseField>

        <ResponseField name="sku" type="string">
          Stock-keeping unit code (e.g., `"RUN-001-BLK-42"`).
        </ResponseField>

        <ResponseField name="price" type="string">
          Base price as a decimal string (e.g., `"99.99"`).
        </ResponseField>

        <ResponseField name="final_price" type="string">
          Pre-computed sale price as a decimal string. Always display this value to customers — it is the authoritative price used at checkout.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<Tip>
  Always display `final_price` to customers — it is the authoritative price computed server-side and used at checkout. Do not compute discounts client-side.
</Tip>

**Example — Fetch a product detail page**

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.example.com/api/v1/products/prd_01H
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.example.com/api/v1/products/prd_01H'
  );
  const { data } = await response.json();

  // Find the primary image
  const primaryImage = data.images.find(img => img.is_primary);

  // List purchasable variants
  data.variants.forEach(v => {
    console.log(`${v.sku}: ${v.final_price} (was ${v.price})`);
  });
  ```
</CodeGroup>

**Example response**

```json theme={null}
{
  "success": true,
  "data": {
    "public_id": "prd_01H",
    "name": "Runner Sneaker",
    "slug": "runner-sneaker",
    "description": "Lightweight daily trainer with responsive cushioning.",
    "brand": "Nike",
    "images": [
      {
        "public_id": "pimg_01H",
        "image_url": "https://ik.imagekit.io/demo/products/runner-sneaker.jpg",
        "alt_text": "Runner Sneaker front view",
        "display_order": 0,
        "is_primary": true
      },
      {
        "public_id": "pimg_02H",
        "image_url": "https://ik.imagekit.io/demo/products/runner-sneaker-side.jpg",
        "alt_text": "Runner Sneaker side view",
        "display_order": 1,
        "is_primary": false
      }
    ],
    "variants": [
      {
        "public_id": "var_01H",
        "sku": "RUN-001-BLK-42",
        "price": "99.99",
        "final_price": "89.99"
      },
      {
        "public_id": "var_02H",
        "sku": "RUN-001-WHT-42",
        "price": "99.99",
        "final_price": "99.99"
      }
    ]
  }
}
```

**Error responses**

| Status | Description                                                 |
| ------ | ----------------------------------------------------------- |
| `400`  | Invalid query parameter format.                             |
| `404`  | Product not found, soft-deleted, or has no active variants. |
