> ## Documentation Index
> Fetch the complete documentation index at: https://docs.waterr.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Pagination

> Cursor pagination on list endpoints — opt in with `limit`, walk with `next_cursor`.

List endpoints support cursor-based pagination. It is **opt-in**: send a
`limit` or a `cursor` and you get a paginated envelope; send neither and the
endpoint returns the same unpaginated body it always has.

<Note>
  Unpaginated list responses are supported but not recommended — they grow
  without bound as your workspace does. New integrations should paginate.
</Note>

## Requesting a page

```bash theme={null}
curl "https://api.waterr.ai/v1/scenarios?limit=25" \
  -H "Authorization: Bearer wai_your_key"
```

<ParamField query="limit" type="integer" default="25">
  Rows per page, between **1** and **100**. Out-of-range values are rejected
  with `invalid_pagination` rather than silently clamped — a request for 500
  rows that quietly returns 100 would leave you believing you had the whole
  list.
</ParamField>

<ParamField query="cursor" type="string">
  Opaque position marker. Pass the `next_cursor` from the previous response
  verbatim. `starting_after` is accepted as an alias.
</ParamField>

## The response

```json theme={null}
{
  "object": "list",
  "data": [ { "id": "...", "name": "..." } ],
  "has_more": true,
  "next_cursor": "eyJ2IjoxLCJ0IjoiMjAyNi0wOS0wMVQxMjozNDo1Ni4wMDBaIiwiaSI6ImFiYyJ9"
}
```

<ParamField body="has_more" type="boolean">
  Whether more rows exist after this page. Stop when it is `false`.
</ParamField>

<ParamField body="next_cursor" type="string | null">
  Position to resume from. `null` on the final page.
</ParamField>

Some endpoints wrap the list in their own envelope (`{ "status": "success", … }`).
Where they do, the pagination fields are merged in alongside — `data` still
holds the rows.

## Walking a full list

Loop until `has_more` is `false`. Do not construct cursors yourself; they
encode a sort position, not an offset, and their format is not part of the
public contract.

<CodeGroup>
  ```python Python theme={null}
  import httpx

  def all_scenarios(api_key):
      params = {"limit": 100}
      with httpx.Client(
          base_url="https://api.waterr.ai/v1",
          headers={"Authorization": f"Bearer {api_key}"},
      ) as client:
          while True:
              page = client.get("/scenarios", params=params).json()
              yield from page["data"]
              if not page["has_more"]:
                  return
              params["cursor"] = page["next_cursor"]
  ```

  ```javascript Node theme={null}
  async function* allScenarios(apiKey) {
    const params = new URLSearchParams({ limit: "100" });

    for (;;) {
      const res = await fetch(`https://api.waterr.ai/v1/scenarios?${params}`, {
        headers: { Authorization: `Bearer ${apiKey}` },
      });
      const page = await res.json();

      yield* page.data;
      if (!page.has_more) return;
      params.set("cursor", page.next_cursor);
    }
  }
  ```
</CodeGroup>

## Why cursors, not page numbers

Lists are ordered newest-first. With `?page=2&per_page=25`, any record created
between your first and second request shifts everything down by one — so page
2 repeats a row you already saw, and eventually a row is skipped entirely.
That is not a rare race; for a resource ordered by creation time it is the
normal case.

A cursor encodes *where you stopped*, not *how far in you were*, so
concurrent writes cannot make you skip or repeat a record. It also stays fast
at depth: there is no growing offset for the database to count past.

## Errors

A malformed or foreign cursor, or a `limit` outside 1–100, returns a `400`
with `error.code` of `invalid_pagination` and `error.param` naming the
offending parameter. See [Errors](/api-reference/errors).

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_pagination",
    "message": "`limit` must be between 1 and 100.",
    "param": "limit"
  },
  "request_id": "req_..."
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.