Skip to main content

Pagination

Every list endpoint uses cursor-based pagination. There are no offset or page-number alternatives.

The list envelope​

{
"object": "list",
"url": "/v1/cards",
"data": [
{ "id": "card_01HX8ZQ2K4M7P9RSTV", "object": "card" },
{ "id": "card_01HX8ZQ2K4M7P9RSTW", "object": "card" }
],
"has_more": true,
"next_cursor": "eyJpZCI6ImNhcmRfMDFIWDhaUTJLNE03UDlSU1RXIn0"
}
FieldMeaning
dataThe current page. Always an array, though it can be empty
has_moreWhether there are more results
next_cursorPass this as starting_after to fetch the next page. null when has_more is false

Paging forward​

Start with the first page:

curl "https://api.aletaplanet.com/v1/cards?limit=50" \
-H "Authorization: Bearer $ALETA_ACCESS_TOKEN"

Then use the returned next_cursor for the next page:

curl "https://api.aletaplanet.com/v1/cards?limit=50&starting_after=eyJpZCI6…" \
-H "Authorization: Bearer $ALETA_ACCESS_TOKEN"

Keep going until has_more is false.

Don't use an empty data array as your stopping condition. A filtered page can be empty even when more results are available.

Parameters​

ParameterTypeDefaultMaxNotes
limitinteger25100Values above the maximum are rejected, not clamped
starting_afterstringCursor for the next page
ending_beforestringCursor for the previous page

starting_after and ending_before can't be used together.

Cursors​

Cursors are opaque. Don't decode them, create your own, or rely on their internal format. The encoding isn't part of the API contract and can change.

A cursor is valid for 24 hours. After that, the API returns 400 with expired_cursor. Start the traversal again with a fresh cursor.

Why not offsets​

Offsets seem simple, but they can cause problems when the collection is changing.

For example, if new records are added between fetching page 1 and page 2, an offset-based page 2 can repeat records you've already seen or skip records entirely. On a transaction list, that could mean silently missing transactions during reconciliation.

Cursors point to a position in a stable sort, so the page boundary stays consistent even as new records are added.

The trade-off is that you can't jump directly to page 47. If you need that kind of UI, a narrower filter is usually a better approach.

Iterating safely​

async function* allCards(aleta, params = {}) {
let cursor;

do {
const page = await aleta.cards.list({
...params,
limit: 100,
starting_after: cursor
});

yield* page.data;
cursor = page.next_cursor;
} while (cursor);
}

There are two things to keep in mind:

  • Loop on next_cursor, not data.length.
  • Don't parallelize a traversal. Each page gives you the cursor needed to request the next one, so the requests are sequential by design.

For very large exports, use the reports endpoints instead of walking the API page by page. A complete transaction history is better treated as a file than as one huge list traversal.