# Pagination

> Cursor pagination on the native /v1 lists, and which lists keep their upstream shapes.

Source: https://tickwire.io/docs/pagination/

The native `/v1` lists return one page at a time and hand you a cursor for the next one.

## Request and response

| Query | Meaning |
|---|---|
| `limit` | Page size, 1 to 500. Default 100. |
| `cursor` | The `next_cursor` from the previous page, passed back unchanged. Omit it for the first page. |

Every page carries the list's items plus:

| Field | Meaning |
|---|---|
| `has_more` | `true` when another page follows. |
| `next_cursor` | The cursor for the next page, or `null` on the last page. |

```bash
curl -s "https://api.tickwire.io/v1/pretrade-holds?status=pending&limit=2" -H "X-API-Key: $TW_KEY"
```

```json
{
  "data": [{ "hold_id": "hld_2", "status": "pending" }, { "hold_id": "hld_1", "status": "pending" }],
  "has_more": true,
  "next_cursor": "eyJ2IjoxLCJmIjoi…"
}
```

Call again with `cursor=eyJ2IjoxLCJmIjoi…` and the **same filters** until `has_more` is `false`.

## Guarantees

- **Keyset, not offset.** A page never repeats or skips an item because rows were added or removed
  between calls. Each list has a fixed order, documented on its route.
- **Cursors are opaque and bound to their list and filters.** A cursor that was edited, comes from
  another list, or is sent with different filters is rejected with
  [`400 invalid_cursor`](https://tickwire.io/docs/errors/invalid_cursor/). Start again without a cursor.
- A cursor reveals nothing about other tenants; every query is still scoped to your key.

## Which lists are paginated

| List | Items field | Order |
|---|---|---|
| `GET /v1/agent-keys` | `data` | Oldest first |
| `GET /v1/pretrade-hooks` | `data` | Oldest first |
| `GET /v1/pretrade-holds` | `data` | Newest first |
| `GET /v1/agent-kill-switches` | `accounts` | By account id |
| `GET /v1/agent-audit`, `GET /v1/agent/audit` | `entries` | Newest first (`before` still works) |
| `GET /v1/connected-apps` | `apps` | Oldest first |
| `GET /v1/agent/accounts` | `accounts` | By account id |

The compatible routes (`/users/current/…`, and `/v1/accounts`, `/v1/webhooks` and
`/mt4|mt5/{id}/…`) keep their existing shapes and are not paginated this way.
`GET /v1/webhooks/{id}/deliveries` keeps its existing `hasMore` and `nextCursor` fields.

## In the SDKs

Python: `for page in tw.pages(tw.list_holds, status="pending"): …` (also `async for` on
`AsyncTickwire`). TypeScript: `for await (const page of pages((cursor) => tw.listHolds({ cursor }))) …`.
