# Pagination

> Cursors, page sizes, and the order of every list.

Every list endpoint returns a page:

```json
{
  "data": [ … ],
  "next_cursor": "eyJmIjoi…",
  "has_more": true
}
```

- `limit` sets the page size: 50 by default, 200 at most. A larger value is lowered to 200.
- When `has_more` is `true`, pass `next_cursor` back as `cursor` to get the next page. On the last page,
  `next_cursor` is `null`.
- The next page's address is also in the `Link` header, for clients that follow links:
  `Link: <https://getrefract.app/v1/library?cursor=…>; rel="next"`.

## Cursors are opaque and bound to the query

Pass a cursor back exactly as it came, with the same filters. A cursor that was edited, or sent with other filters
or to another endpoint, answers `400` with [`INVALID_CURSOR`](/developers/errors/invalid-cursor): start again
from the first page.

## The order of each list

Lists follow a stable order, so walking every page visits each item once.

| Endpoint | Order |
|---|---|
| `/v1/library`, `/v1/watchlist` | Stable, by Refract id. Not chronological. |
| `/v1/history/episodes` | By show, then season, then episode. |
| `/v1/history/movies` | Stable, by play id. Sort by `watched_at` on your side for a timeline. |
| `/v1/ratings`, `/v1/lists` | Stable, by id. |
| `/v1/lists/{id}/items` | The list's own order (`position`), then id. |

A title added while you page may or may not appear in the pages you have left. To keep a copy in step with a
member's account, a feed of changes arrives in a later release.
