Pagination
PagePromise, for await over every item, page-by-page control, and the change feed's pages.
Every list method returns a PagePromise, which is two things at once:
- a promise of the first page:
awaitit for{ data, has_more, next_cursor }; - an async iterable of every item:
for awaitwalks the whole list, fetching each next page only when the current one is used up.
Nothing is requested until you do one or the other, and the first page is fetched once whichever comes first.
Every item
// Iterating fetches each next page only when the current one is used up.
for await (const product of furniops.products.list({ updated_since: "2026-10-01T00:00:00Z" })) {
console.log(product.id);
}Leaving the loop early (break, return, or an exception) stops fetching. Each page is one
request with the usual retries; a page that still fails ends the loop
with the error.
Set limit to 100 for big walks: fewer, larger pages spend less of your
rate budget.
Page by page
await the call for one page and drive the cursor yourself, for example to store it between
jobs:
let cursor: string | undefined;
do {
const page = await furniops.sources.list({ limit: 100, starting_after: cursor });
for (const source of page.data) console.log(source.id, source.name);
cursor = page.has_more ? (page.next_cursor ?? undefined) : undefined;
} while (cursor !== undefined);Backwards
ending_before walks the other way; keep passing next_cursor in the same parameter:
// ending_before walks toward newer items; next_cursor keeps going that way.
const newer = await furniops.webhookEndpoints.list({ ending_before: "eyJ2IjoxfQ", limit: 10 });
console.log(newer.data.length, newer.next_cursor);The change feed
changes.list returns a PagePromise too. Iterating it walks the feed from your cursor and
stops when it has caught up (caught_up: true), so a for await over it is a "read
everything new" step. To keep following the feed, loop on pages with the last cursor, as in
Sync a catalogue.