# Pagination

List endpoints page with opaque cursors. Do not build your own offsets.

A list response carries the page fields alongside `data`:

```json
{
  "data": [ ... ],
  "count": 24,
  "limit": 24,
  "offset": 0,
  "has_more": true,
  "next_cursor": "eyJvIjoyNCw…"
}
```

## Getting the next page

Pass `next_cursor` back as `cursor`. The cursor encodes the position and is tamper-checked, so you never compute an offset yourself.

```bash
# first page
curl "https://dev.ziffi.xyz/v1/search?q=serum&limit=24"
# next page, using next_cursor from the response
curl "https://dev.ziffi.xyz/v1/search?q=serum&limit=24&cursor=eyJvIjoyNCw…"
```

```javascript
let cursor = null;
do {
  const url = new URL("https://dev.ziffi.xyz/v1/search");
  url.searchParams.set("q", "serum");
  if (cursor) url.searchParams.set("cursor", cursor);
  const { data, next_cursor, has_more } = await (await fetch(url)).json();
  process(data);
  cursor = next_cursor;
  var more = has_more;
} while (more);
```

## Rules

- Stop when `has_more` is `false`.
- A cursor is stable for a bounded time. Treat it as opaque; do not parse or modify it.
- `limit` is capped per endpoint (commonly 100). Values above the cap are clamped.

## Next steps

- [Search products](#/search): the most common paged endpoint.
- [Browse and categories](#/browse): faceted paging.
