GiftingStack
Sign in Get started
Docs menu

Docs

Pagination

Lists come back newest first in pages. Keep paging until has_more is false, and pass next_cursor back as starting_after.

Checked against the API and the live OpenAPI on 2026-11-04.

The shape of a page

GET /v1/users/ava/ledger?limit=50

{
  "data": [ { "id": "led_01JA0Q8X2M4N6P8R0T2V4W6Y8Z", … }, … ],
  "has_more": true,
  "next_cursor": "led_01J9ZZ3K7T1B5C9D2E4F6G8H0J"
}

GET /v1/users/ava/ledger?limit=50&starting_after=led_01J9ZZ3K7T1B5C9D2E4F6G8H0J
  • limit: items per page, 1 to 100. The default is 50 (the inbox: 20).
  • starting_after: the next_cursor of the previous page. Leave it out for the first page. Treat the cursor as an opaque string.
  • has_more: true while older items exist.
  • next_cursor: what to send as starting_after for the next page. It is null on the last page.

A cursor points at an item, not at a page number, so new activity never shifts the pages you have not read yet: new items appear at the front of the list and your cursor keeps its place.

Which endpoints page this way

EndpointOrderCursor
GET /v1/users/{external_id}/ledgerNewest firstnext_cursor as starting_after
GET /v1/gift-transactionsNewest firstnext_cursor as starting_after
GET /v1/users/{external_id}/rejected-requestsNewest firstThe id of the last item as starting_after; has_more says whether to continue
GET /v1/users/{external_id}/inbox and GET /v1/client/me/inboxNewest first, last 24 hoursnext_cursor as cursor (Inbox)
GET /v1/eventsOldest firstnext_cursor as cursor, or the last sequence you processed as after_sequence (Webhooks)

Filtered pages can be short

The ledger and gift transaction lists accept filters: type, reason, reference, from and to on the ledger; sender, receiver, gift_id, status, from and to on gift transactions. A filtered page looks at no more than 2,000 rows for one request, so that no single call can run long. When fewer rows than that match, you get a full page as usual. When the matches are rare, a page can hold fewer than limit items, even none, while has_more is still true: the scan stopped at its cap, not at the end of your data.

So never treat a short or empty page as the end. Stop only when has_more is false, and always send next_cursor on:

async function allGiftsOf(user, { gs }) {
  const out = [];
  let cursor;
  do {
    const q = new URLSearchParams({ sender: user, status: "completed", limit: "100", from, to });
    if (cursor) q.set("starting_after", cursor);
    const page = await gs("/v1/gift-transactions?" + q);
    out.push(...page.data);
    cursor = page.has_more ? page.next_cursor : undefined;
  } while (cursor);
  return out;
}

Date ranges

from is inclusive and to is exclusive, both ISO 8601 timestamps. GET /v1/gift-transactions lists the whole project, so without a sender or receiver it needs both from and to, and the range may span at most 31 days. A longer range, or one that ends before it starts, is a 400 invalid_request with details.field set to to. To cover a longer period, ask for one month at a time, each with its own cursor. With a sender or receiver the range is optional and has no length limit.

GET /v1/gift-transactions?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&limit=100

Things worth knowing

  • Reads are not counted towards your plan, whatever the page size.
  • A page is a snapshot of what existed when it was read. A gift reversed later changes that item's status the next time you read it.
  • An unknown starting_after or one of the wrong kind is a 400 invalid_request; start again from the first page.
  • Leaderboards are not paged: they return the top 100 at most (Leaderboards, away and inbox).

Questions or something unclear? support@giftingstack.com.