---
title: "Pagination"
description: "Cursor pagination and walking every page of a list."
url: https://developer.tendralhealth.com/docs/pagination
---

# Pagination

List endpoints return a page at a time and a cursor for the next page. Order is stable, so walking a list never skips or repeats a row.

## The list shape

```json List response
{
  "object": "list",
  "livemode": true,
  "data": [],
  "has_more": true,
  "next_cursor": "eyJhdCI6IjIwMjYtMDQtMjdUMTM6MDI6MDBaIn0",
  "url": "/v1/…?limit=100"
}
```

- `data` holds up to `limit` items. `limit` defaults to 100 and takes a whole number from 1 to 500; anything else gets `400` with code [`INVALID_PARAMETER`](/docs/errors#INVALID_PARAMETER).
- `has_more` is `true` when there is another page.
- `next_cursor` goes back as `?cursor=` for the next page. It is `null` on the last page.
- `url` echoes the path and query you sent.

## Cursors

A cursor is opaque. Pass it back unchanged; a cursor the API did not return gets `400` with code [`INVALID_PARAMETER`](/docs/errors#INVALID_PARAMETER). Cursors are built from a timestamp and an id together, so rows that share a timestamp still come back in one fixed order.

## Walk every page

```typescript Node
async function* everyItem(firstPageUrl: string) {
  let cursor: string | null = null
  do {
    const url = new URL(firstPageUrl)
    if (cursor) url.searchParams.set('cursor', cursor)
    const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.TENDRAL_API_KEY}` } })
    const page = await res.json()
    yield* page.data
    cursor = page.has_more ? page.next_cursor : null
  } while (cursor)
}
```
