Pagination

Every list endpoint in the Flint API paginates the same way. You request a page with page_size, move forward with page_token, and stop when a response arrives without a next_page_token. Learn the pattern once and it applies to orders, customers, refunds, webhook events, and every other list.

How It Works#

Flint uses cursor pagination. Each page of results includes a token that marks where the list left off, and you hand that token back to get the next page. Three parameters drive everything:

page_sizeinteger

How many records to return per page, from 1 to 100. Defaults to 20 when omitted. Out-of-range values are rejected with a 400, not clamped.

page_tokenstring

The cursor from the previous response's next_page_token. Omit it on the first request.

next_page_tokenstring

Present in a list response when more results exist. Pass it back as page_token to fetch the next page. Absent on the final page.

Fetch the first page of orders:

Bash
curl "https://api.withflintpay.com/v1/orders?page_size=2" \
  -H "Authorization: Bearer YOUR_API_KEY"

Every list response uses the same envelope: a data array of results, plus next_page_token when there is more to read. The order objects below are trimmed for brevity.

JSON
{
  "data": [
    {
      "order_id": "ord_01KWJZAEESGE95VN6GP5J7H6MQ",
      "status": "open",
      "pricing_amounts": {
        "total_money": { "amount": 5444, "currency": "USD" }
      },
      "created_at": "2026-07-02T18:04:11Z"
    },
    {
      "order_id": "ord_01KWJYV5B8T0AHXQ2M4E7RCN2D",
      "status": "paid",
      "pricing_amounts": {
        "total_money": { "amount": 12000, "currency": "USD" }
      },
      "created_at": "2026-07-02T17:31:48Z"
    }
  ],
  "next_page_token": "Zm9yd2FyZC1vbmx5LW9wYXF1ZS1jdXJzb3I",
  "request_id": "e1ce49e4-bf35-4f14-8d06-8a354f276190"
}

Fetching the Next Page#

Send the same request again with page_token set to the token you just received:

Bash
curl "https://api.withflintpay.com/v1/orders?page_size=2&page_token=$NEXT_PAGE_TOKEN" \
  -H "Authorization: Bearer YOUR_API_KEY"

Two rules keep a pagination run valid:

  • Keep the query identical. Use the same endpoint, filters, and sort as the first page; only the token changes. Changing a filter or the sort mid-run returns a 400 INVALID_PAGE_TOKEN.
  • Use each token once, in order. A cursor points at a position in one specific run. There are no page numbers, and you cannot skip ahead.

page_size is the one exception: it applies per request, so you can read 100 records at a time and drop to 10 later without invalidating the cursor.

When a response comes back without next_page_token, you have reached the end of the list.

Listing Every Record#

With the Node SDK#

The Node SDK manages cursors for you. Every list method returns an async iterable that fetches pages lazily as you consume it:

TypeScript
import { Flint } from "@flintpay/node";

const flint = new Flint({
  apiKey: process.env.FLINT_API_KEY!,
});

for await (const order of flint.orders.list({ limit: 100 })) {
  console.log(order.orderId, order.status);
}

To work a page at a time instead, await the same call. The SDK names the controls limit and pageToken, and every page exposes data, nextPageToken, and hasMore:

TypeScript
const page = await flint.orders.list({ limit: 100 });

if (page.hasMore) {
  const next = await flint.orders.list({
    limit: 100,
    pageToken: page.nextPageToken,
  });
}

Over Direct HTTP#

Loop until the response has no next_page_token:

JavaScript
const orders = [];
let pageToken;

do {
  const url = new URL("https://api.withflintpay.com/v1/orders");
  url.searchParams.set("page_size", "100");
  if (pageToken) url.searchParams.set("page_token", pageToken);

  const response = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.FLINT_API_KEY}` },
  });
  if (!response.ok) throw new Error(`List failed: ${response.status}`);

  const page = await response.json();
  orders.push(...page.data);
  pageToken = page.next_page_token;
} while (pageToken);

Bulk reads count against your read rate limits (60 requests per second per key by default). At 100 records per page that leaves comfortable headroom for most exports; add backoff on 429 if you parallelize.

If you are paginating on a schedule to detect new activity, webhooks push those events to you instead: no polling loop, no missed pages.

Sorting#

Lists return newest first by default (created_at, descending). Endpoints that support other orderings accept two parameters:

  • sort_by: the field to order by. Each resource supports its own set. Orders sort by created_at, updated_at, total, or amount_due_money; customers add name and email. The API reference lists the fields per endpoint.
  • sort_direction: asc or desc. Defaults to desc.
Bash
curl "https://api.withflintpay.com/v1/customers?sort_by=name&sort_direction=asc&page_size=50" \
  -H "Authorization: Bearer YOUR_API_KEY"

The sort you choose on the first page is locked in for the rest of the run: the cursor remembers it, and a follow-up request with a different sort is rejected.

Filtering#

Filter parameters combine freely with pagination, and the cursor carries your filters through every page. Most lists support some mix of:

  • Date ranges: created_after, created_before, updated_after, updated_before (RFC 3339 timestamps), plus resource-specific ranges like due_after on invoices.
  • Status filters: status on orders and invoices, state on payment intents, and similar per-resource enums.
  • Relationships: customer_id, order_id, payment_intent_id, and other resource links.
  • Search: query for free-text matching on resources like customers, orders, and products.
Bash
curl "https://api.withflintpay.com/v1/orders?status=open&created_after=2026-01-01T00:00:00Z&sort_by=created_at&sort_direction=asc&page_size=50" \
  -H "Authorization: Bearer YOUR_API_KEY"

Flint validates query parameters strictly. A parameter the endpoint does not support (a typo, or a filter borrowed from another resource) is rejected with UNSUPPORTED_QUERY_PARAM rather than silently ignored, so mistakes surface on the first request instead of skewing your results.

What You Can Rely On#

  • One pattern everywhere. Every list endpoint takes the same pagination parameters and returns the same envelope.
  • Stable pages. Ordering is deterministic. A record that existed when you started appears exactly once in a full run: no duplicates, no skips, even while new records are being created.
  • New records do not disturb a run in progress. With the default newest-first ordering, records created after your first page will not appear later in the run; start a new run to pick them up. Walking oldest-first (sort_by=created_at&sort_direction=asc), new records join at the end and your loop reaches them.
  • Tokens are opaque. The token format is not part of the API contract and can change at any time. Do not parse, construct, or store tokens durably. Treat a token as valid for the run you are in; if one is ever rejected, restart from the first page.
  • No total counts. List responses do not include a count of all matching records. If you need running totals, maintain them from webhooks or count during a full run.

Pagination Errors#

Pagination mistakes return HTTP 400 with type: validation_error and the offending parameter in param. See Error Handling for the envelope and retry guidance.

CodeCauseFix
INVALID_PAGE_SIZEpage_size is not an integer from 1 to 100Stay within 1 to 100, or omit it for the default of 20
INVALID_PAGE_TOKENThe token is malformed, no longer valid, or the request's filters or sort changed mid-runSend the token exactly as received, with the original query; otherwise restart from the first page
INVALID_SORT_BYThe field is not sortable on this endpointUse one of the endpoint's documented sort fields
INVALID_SORT_DIRECTIONA value other than asc or descUse asc or desc
UNSUPPORTED_QUERY_PARAMThe endpoint does not recognize a query parameterCheck the spelling against the endpoint's reference

Common Mistakes#

  • Treating cursors like page numbers. There is no offset parameter and no way to jump to page five. Cursors move forward one page at a time.
  • Bookmarking tokens. A next_page_token is meant for the request that follows it, not for storage. To make a long-running sync resumable, persist your own progress marker (for example, the created_at of the last record you processed) and resume with an overlapping created_after window, de-duplicating by ID.
  • Mutating the query mid-run. Adding a filter or flipping the sort while reusing an old token returns INVALID_PAGE_TOKEN. Decide the full query first, then paginate.
  • Polling lists for changes. If the goal is reacting to new payments or orders, webhooks deliver those events without spending rate limit on repeated runs.

Next Steps#

  • Node SDK: auto-pagination with for await and typed list parameters
  • Rate Limits: the read budgets to plan bulk exports around
  • Error Handling: the error envelope and retry guidance
  • Webhooks: push notifications instead of polling lists
Rate this doc