# Pagination

> Lists page with limit and an opaque cursor. Follow nextCursor until it's null.

Source: https://taseli.co.il/en/developers/pagination

Response 200:

```json
{
  "data": [{ "id": "0b6f3c1e-7a42-4c55-9b1d-2f6e8a1c4d90" }],
  "total": 134,
  "nextCursor": "eyJvIjoyMH0"
}
```

| Field |  |
| --- | --- |
| `data` | This page. |
| `total` | How many in the whole list. |
| `nextCursor` | Pass it as `cursor` for the next page. Null on the last. |

## All pages

```js
async function all(path) {
  const items = [];
  let cursor = null;
  do {
    const url = new URL(`https://taseli.co.il/api/v1${path}`);
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);
    const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.TASELI_KEY}` } });
    if (!res.ok) throw new Error((await res.json()).error.code);
    const page = await res.json();
    items.push(...page.data);
    cursor = page.nextCursor;
  } while (cursor);
  return items;
}

const accounts = await all("/accounts");
```

## What pages

| Endpoint | Default `limit` | Max | Whole list |
| --- | --- | --- | --- |
| `GET /accounts` | 50 | 100 | Every account |
| `GET /proposals` | 20 | 100 | The latest 200 |
| `GET /activity` | 50 | 100 | The latest 200 |

`GET /creatives` takes `limit` up to 100 and `GET /competitors/ads` up to 30, with no cursor: `shown` and `of` say how much came back; narrow with filters for the rest. `GET /webhooks` returns all of them (at most 5). `by=campaign` metrics return the 200 highest spenders. For metrics over 400 days, ask period by period.

## Rules

- Treat a cursor as opaque. Don't build one, and don't store one for later.
- A cursor is a position, not a snapshot. If the list changes between pages, a row can repeat or be skipped. For `status=waiting`, which sorts by value, expect it.
- A `limit` out of range or an unreadable `cursor` answers `400 invalid_parameter`.
