How-to

Paginate lists

Walk any list with a cursor. The SDKs fetch each page only as your loop needs it.

Iterate over every page

pagination-iterate.sh

# The next page: pass the previous page's nextCursor back, verbatim, as cursor, until hasMore is# false (nextCursor is present only while hasMore is true).curl "https://api.qbitflow.app/v2/payments?createdAfter=2026-10-01T00:00:00Z&limit=50&cursor=$NEXT_CURSOR" \  -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a page of Payment ({ items, hasMore, nextCursor? })
// Every payment of the last 30 days: the iterator fetches the pages one at a time, as the loop// consumes them, and stops when you break (or after the page whose hasMore is false).const since = new Date(Date.now() - 30 * 24 * 3600 * 1000);let count = 0;for await (const payment of client.payments.iterate({ createdAfter: since, limit: 50 })) {	count++;	console.log(payment.uuid, payment.amountUsd, 'USD');}console.log(`${count} payments over the last 30 days`);
# Fetches one page at a time, only as the loop consumes it; break stops the fetching. It# stops after the page whose has_more is False.for payment in client.payments.iterate(limit=50):    print(payment.uuid, payment.amount_usd, "USD")
// Fetches one page at a time, only as the loop consumes it; break stops the fetching.// It stops after the page whose HasMore is false.count := 0for payment, err := range client.Payments.Iterate(ctx, &qbitflow.PaymentListParams{Limit: 50}) {	if err != nil {		log.Fatal(err)	}	count++	fmt.Println(payment.UUID, payment.AmountUSD, "USD")}fmt.Println(count, "payments")
// The next page is fetched when the loop needs it; it stops after the page whose hasMore is false.$count = 0;foreach ($client->payments->iterate(new \QBitFlow\Params\PaymentListParams(limit: 50)) as $payment) {	$count++;	echo "{$payment->uuid} {$payment->amountUsd} USD\n";}echo "{$count} payments\n";

Without an SDK

  1. Your code → QBitFlow API: GET the list, with limit
  2. QBitFlow API → Your code: items, hasMore: true, nextCursor: A
  3. Your code → QBitFlow API: The same list, cursor=A
  4. QBitFlow API → Your code: items, hasMore: true, nextCursor: B
  5. Your code → QBitFlow API: The same list, cursor=B
  6. QBitFlow API → Your code: The last items, hasMore: false
  1. Ask for the first page with limit and no cursor.
  2. Each page is { "items": [ … ], "hasMore": true, "nextCursor": "…" }.
  3. Send nextCursor back as cursor, verbatim.
  4. Stop when hasMore is false (nextCursor is then absent). A full last page already answers false: no extra empty page.

The rules

  • limit is clamped, never refused. Absent or 0: the default; above the maximum: the maximum. A limit that isn't an integer answers 400 validation_failed.
  • No skips, no repeats on the newest-first lists of payments, bills and subscriptions (createdAt, then uuid). Newer rows wait for the next walk.
  • A cursor belongs to its list. A cursor the API didn't give answers 400 validation_failed on cursor: start again.
  • Filters may change between pages; the cursor keeps its place.
  • Dates (createdAfter, createdBefore) are RFC 3339 instants or plain dates (midnight UTC), both bounds excluded. Encode a + offset as %2B.

Avoid

Poll for new payments

Polling burns your rate limits and still lags behind.

Do

Let webhooks tell you

Must poll? Overlap each window and deduplicate on uuid.

Page sizes

Every list takes the same limit: 20 by default, at most 100. A short catalog (currencies, your wallets) answers its one page with hasMore: false, and ignores limit and cursor.