Skip to content
On this page

Developers › Building on the API

Pagination

Cursors, not page numbers.

Verified 6 November 2026

View as Markdown
ts
let page = await sp.paymentIntents.list({ limit: 100 });
const all = [...page.data];

while (page.has_more) {
  page = await sp.paymentIntents.list({
    limit: 100,
    starting_after: page.data[page.data.length - 1].id,
  });
  all.push(...page.data);
}

Going back

starting_after takes the id of the last object on the page you have and returns the next, older page. ending_before takes the id of the first object on the page you have and returns the page immediately before it — the limit objects nearest the cursor on the newer side, still newest first. From the fifth page it returns the fourth.

ts
const back = await sp.paymentIntents.list({
  limit: 100,
  ending_before: page.data[0].id,
});
back.has_more; // true while there are newer objects still

Going back, has_more means there is more in the direction you are travelling — newer objects — and it is false once you have reached the newest. Pass one cursor or the other, not both. This is the same contract as Stripe’s, deliberately.

Why not page numbers

Payments arrive constantly, and every new one shifts an offset-based page by one. Page two would skip a row or repeat one, quietly, and the row most likely to be affected is the newest — which is the one you were looking for. A cursor is stable regardless of what arrives while you are paging.

Filtering

Lists are newest first. Payments can be filtered by status, by when they were created, by customer, and by your own metadata:

ts
await sp.paymentIntents.list({
  status: "succeeded",
  metadata: { shop_id: "scuba-sensation" },
  created_gte: Math.floor(Date.now() / 1000) - 86_400,
});

Over HTTP the metadata filter is metadata[shop_id]=scuba-sensation, once per key; a payment matches when it has every pair, compared exactly. Only the payments list filters on metadata today — the other lists return metadata but cannot be searched by it.

limit is between 1 and 100, and 10 if you leave it out. Asking for more is an error rather than a silent clamp, so you never quietly get fewer rows than you planned for.