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

# Products

> List and fetch products for your storefront

## `shop.products.list(params?)`

Returns a paginated list of visible products for the shop.

```typescript theme={null}
const { products, total, limit, offset } = await shop.products.list({
  limit: 12,
  offset: 0,
  sort_by: 'created_at',
  sort_order: 'desc',
});
```

### Parameters

| Param        | Type            | Default  | Description                      |                |                 |                |            |
| ------------ | --------------- | -------- | -------------------------------- | -------------- | --------------- | -------------- | ---------- |
| `limit`      | `number`        | `50`     | Max products to return (max 100) |                |                 |                |            |
| `offset`     | `number`        | `0`      | Pagination offset                |                |                 |                |            |
| `category`   | `string`        | —        | Filter by category name          |                |                 |                |            |
| `search`     | `string`        | —        | Search by product name           |                |                 |                |            |
| `sort_by`    | \`'created\_at' | 'name'   | 'price'                          | 'sales'        | 'updated\_at'\` | `'created_at'` | Sort field |
| `sort_order` | \`'asc'         | 'desc'\` | `'desc'`                         | Sort direction |                 |                |            |

### Response

```typescript theme={null}
{
  products: Product[];
  total: number;   // total matching products (for pagination)
  limit: number;
  offset: number;
}
```

### Product type

```typescript theme={null}
{
  id: string;
  shop_id: string;
  name: string;
  slug: string;
  description: string | null;
  price: number;                    // in smallest currency unit (e.g. cents)
  compare_at_price: number | null;  // original price for sale display
  category: string | null;
  images: string[];                 // array of image URLs
  is_visible: boolean;
  stock_quantity: number | null;
  track_inventory: boolean;
  sku: string | null;
  weight: number | null;
  variants: ProductVariant[] | null;
  created_at: string;
  updated_at: string;
}
```

### Example — Product grid with pagination

```typescript theme={null}
let currentOffset = 0;
const PAGE_SIZE = 12;

async function loadProducts() {
  const { products, total } = await shop.products.list({
    limit: PAGE_SIZE,
    offset: currentOffset,
    sort_by: 'created_at',
    sort_order: 'desc',
  });

  renderGrid(products);

  const hasMore = currentOffset + PAGE_SIZE < total;
  loadMoreBtn.style.display = hasMore ? 'block' : 'none';
}

loadMoreBtn.addEventListener('click', () => {
  currentOffset += PAGE_SIZE;
  loadProducts();
});
```

### Example — Category filter

```typescript theme={null}
async function filterByCategory(category: string) {
  const { products } = await shop.products.list({ category });
  renderGrid(products);
}
```

### Example — Search

```typescript theme={null}
searchInput.addEventListener('input', async (e) => {
  const query = (e.target as HTMLInputElement).value;
  if (query.length < 2) return;

  const { products } = await shop.products.list({ search: query });
  renderGrid(products);
});
```

***

## `shop.products.getBySlug(slug)`

Returns a single product by its URL slug.

```typescript theme={null}
const product = await shop.products.getBySlug('red-cotton-t-shirt');
```

### Parameters

| Param  | Type     | Description            |
| ------ | -------- | ---------------------- |
| `slug` | `string` | The product's URL slug |

### Example — Product detail page

```typescript theme={null}
const slug = window.location.pathname.split('/').pop()!;
const product = await shop.products.getBySlug(slug);

productName.textContent = product.name;
productPrice.textContent = formatPrice(product.price);

if (product.compare_at_price) {
  originalPrice.textContent = formatPrice(product.compare_at_price);
  saleBadge.style.display = 'block';
}

product.images.forEach(url => {
  const img = document.createElement('img');
  img.src = url;
  gallery.appendChild(img);
});
```

<Note>
  Returns `ShopiError` with `status: 404` if the product does not exist or is not visible.
</Note>
