# Pagination

Every list endpoint answers with the same envelope and pages by cursor. Cursors are item IDs, not page numbers.

## The envelope

The list envelope:

```text
{
  "object": "list",
  "has_more": true,
  "data": [ /* … */ ]
}
```

`has_more` is the only thing that tells you whether to keep going. There is no total count: counting every row of a large table on each request would be slow and would be stale by the time you read it.

## The parameters

- `limit` — 1–100, defaulting to 20.
- `after` — the ID of the last item you saw. Returns the page that follows it.
- `before` — the ID of the first item you saw. Returns the page that precedes it.

`after` and `before` are mutually exclusive; sending both is `422 invalid_parameter`.

> A cursor is an item ID, not an offset. Do not construct one, do not increment one, and do not store one as a position — take it from the item you actually received.

## Walking a list

Request the first page, take the ID of the last item in `data`, and pass it as `after` for the next one. Stop when `has_more` is false.

The next page:

```text
https://api.rasket.com/emails?limit=20&after=4ef9a417-02e9-4d39-ad75-9611e0fcc33c
```

Because the cursor is an ID rather than an offset, an item added while you are paging cannot shift a row onto a page you have already read — which is the failure mode of offset pagination and the reason this is a cursor.
