Guides

Pagination

List endpoints return one page at a time. Pass limit for the page size and the previous page's nextCursor to get the next one.

Every list endpoint takes the same two query parameters and returns nextCursor:

ParameterDefaultMaxWhat
limit50100Items per page
cursornoneThe nextCursor of the previous page. Omit it for the first page.

nextCursor is null on the last page. The list itself is in a field named after the resource, like accounts, batches or campaigns. Each endpoint's field is in the API reference.

Walk every page

First page:

curl "https://api.koast.ai/v1/brands?limit=1" \
  -H "Authorization: Bearer $KOAST_API_KEY"
{
  "accounts": [
    {
      "brandId": 1,
      "brandName": "Vela Supplements",
      "accountId": "act_000000000000001",
      "accountName": "Vela Supplements · US-1",
      "connected": true,
      "active": false,
      "organizationId": 1
    }
  ],
  "nextCursor": "eyJvIjoxfQ"
}

Next page, with the cursor:

curl "https://api.koast.ai/v1/brands?limit=1&cursor=eyJvIjoxfQ" \
  -H "Authorization: Bearer $KOAST_API_KEY"
{
  "accounts": [
    {
      "brandId": 2,
      "brandName": "Nimbus Sleep",
      "accountId": "biz_fixture0000001",
      "accountName": "Nimbus Sleep",
      "connected": true,
      "active": false,
      "organizationId": 1
    }
  ],
  "nextCursor": null
}

nextCursor is null, so you're done.

In code:

async function listAll(path, field) {
  const items = [];
  let cursor = null;
  do {
    const url = new URL(`https://api.koast.ai${path}`);
    url.searchParams.set('limit', '100');
    if (cursor) url.searchParams.set('cursor', cursor);
    const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.KOAST_API_KEY}` } });
    if (!res.ok) throw new Error((await res.json()).error.code);
    const page = await res.json();
    items.push(...page[field]);
    cursor = page.nextCursor;
  } while (cursor);
  return items;
}

const launches = await listAll('/v1/brands/1/launches', 'batches');

With the CLI, --all follows every page and prints one JSON line per item:

koast list-koast-batches --brand 1 --all

Rules

  • Cursors are opaque. Don't build, decode or edit them. Their format can change.
  • A cursor belongs to its endpoint and filters. Pass it back to the same endpoint with the same query parameters. Keep limit the same too.
  • A bad cursor is a 400. A cursor from another endpoint, or one from an older format, gets validation_failed with the message cursor is invalid or expired. Start again from the first page.
  • Order is newest first for launches, creatives, automations, workflows, templates and activity. New items created while you page don't shift the pages you haven't read yet.
  • Ad-platform lists (campaigns in an ad account, lead forms) page over what the platform returns at the time of the call.
{
  "error": {
    "code": "validation_failed",
    "message": "cursor is invalid or expired",
    "details": null,
    "requestId": "3d05e06c-74f6-454d-9a76-e47fc270faa6"
  }
}

Older page fields

Some list responses also carry page, limit and total from before cursors existed. total is the real total. Prefer cursor and nextCursor: they are the same on every list endpoint.

On this page