# Pagination

Long lists come a page at a time. Most use page numbers; the list of runs can also use a cursor.

## Page numbers

[List workflows](https://docs.gorunner.io/api/workflows/list-workflows), [List runs](https://docs.gorunner.io/api/runs/list-runs), [List a workflow’s runs](https://docs.gorunner.io/api/runs/list-workflow-runs) and [List versions](https://docs.gorunner.io/api/publish/list-workflow-versions) take two parameters:

- `page`: From 1. Below 1, or not a number, is read as 1.
- `limit`: Rows per page, 1 to 100. 20 if you leave it out. Out of range is read as 20, not 100.

Beside `data`, the answer has a `pagination` block:

```bash
curl "$GORUNNER/api/workflows?view=summary&limit=3&page=2" \
  -H "Authorization: Bearer $GORUNNER_TOKEN" | jq .pagination
```

```text
{
  "page": 2,
  "limit": 3,
  "total": 11,
  "totalPages": 4
}
```

`limit` is the one applied, which may not be the one you sent. A page past the end has an empty `data` and the same block.

**JavaScript**

```js
const headers = { Authorization: `Bearer ${TOKEN}` };
const all = [];
for (let page = 1; ; page++) {
  const url = `${GORUNNER}/api/workflows?view=summary&limit=100&page=${page}`;
  const { data, pagination } = await (await fetch(url, { headers })).json();
  all.push(...data);
  if (page >= pagination.totalPages) break;
}
```

**Python**

```python
headers = {"Authorization": f"Bearer {TOKEN}"}
all, page = [], 1
while True:
    body = requests.get(f"{GORUNNER}/api/workflows", headers=headers,
                        params={"view": "summary", "limit": 100, "page": page}).json()
    all += body["data"]
    if page >= body["pagination"]["totalPages"]:
        break
    page += 1
```

## A cursor, for runs

[List runs](https://docs.gorunner.io/api/runs/list-runs) also pages with a cursor, which stays right while new runs arrive. Send `cursor` empty for the first page. Then send the `nextCursor` you got, until it is `null`.

```bash
curl "$GORUNNER/api/runs?limit=2&cursor=" \
  -H "Authorization: Bearer $GORUNNER_TOKEN" | jq .pagination
```

```text
{
  "limit": 2,
  "nextCursor": "MjAyNi0xMC0yNVQxODoyMTo1MC40MDQ4OTZa…"
}
```

**JavaScript**

```js
const runs = [];
let cursor = "";
do {
  const url = `${GORUNNER}/api/runs?limit=100&cursor=${encodeURIComponent(cursor)}`;
  const { data, pagination } = await (await fetch(url, { headers })).json();
  runs.push(...data);
  cursor = pagination.nextCursor;
} while (cursor);
```

**Python**

```python
runs, cursor = [], ""
while True:
    body = requests.get(f"{GORUNNER}/api/runs", headers=headers,
                        params={"limit": 100, "cursor": cursor}).json()
    runs += body["data"]
    cursor = body["pagination"]["nextCursor"]
    if not cursor:
        break
```

- **No total**: A cursor page doesn’t count the rows. Its rows leave out `triggerData`: read one run for it.
- **Order**: Newest first, or oldest first with `sort=oldest`. `sort=duration` is `400`: page that by number.
- **A wrong cursor**: One this list didn’t hand out is `400 VALIDATION_ERROR`. Don’t build or change one.

## Other lists

Connections, folders, templates and apps come whole, in one answer. The [Partner API](https://docs.gorunner.io/embed/api) pages with a cursor too.

Related: [List runs](https://docs.gorunner.io/api/runs/list-runs) · [List workflows](https://docs.gorunner.io/api/workflows/list-workflows) · [Errors](https://docs.gorunner.io/api/errors)

Source: https://docs.gorunner.io/api/pagination
