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:
| Parameter | Default | Max | What |
|---|---|---|---|
limit | 50 | 100 | Items per page |
cursor | none | The 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 --allRules
- 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
limitthe same too. - A bad cursor is a 400. A cursor from another endpoint, or one from an older format, gets
validation_failedwith the messagecursor 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.