Skip to main content

Pagination

Customer (/customers), invoice (/invoices), receipt (/receipts), product (/products), supplier (/suppliers), retention (/retentions), organization (/organizations) and webhook (/webhooks) listings, as well as catalog searches (/catalogs/...), return paginated results. On these endpoints, Facturapi offers two pagination modes that you can choose with the pagination parameter:

  • pagination=page — the classic mode, based on numbered pages. This is the default behavior when pagination is not sent.
  • pagination=cursor — the recommended mode, based on cursors. It traverses results sequentially, without the 3,000-result cap of page mode, and with stable pages even when the data changes.

Which mode should I use?

pagination=pagepagination=cursor
NavigationJump to a specific page (page=3)Sequential: next / previous
ReachUp to 3,000 results (30 pages × 100)All search results
StabilityPages shift when the data changesStable pages (position-based)
Recommended forCompatibility, UIs with page jumpsLarge lists, iteration, exports

For most cases, and especially for large lists, we recommend using pagination=cursor.

The most important practical difference between both modes is reach: pagination=page only gives access to the first 3,000 results (30 pages × 100 per page), while pagination=cursor can traverse all the search results, even when total_results is capped at 3,000. If you need to export or process more than 3,000 results, use pagination=cursor.

Common parameters

ParameterTypeDescription
limitintegerMaximum number of results to return, from 1 to 100.
paginationstringpage (default) or cursor.

The page, after and before parameters depend on the selected mode and are mutually exclusive between modes:

  • pagination=page accepts page, but not after or before.
  • pagination=cursor accepts after or before, but not page.

Sending after and before at the same time returns an error.

Filter format in the URL

Filters travel in the query string in two formats, the same ones the official SDKs send:

  • Objects and ranges: bracket notation, for example date[gte]=2026-01-01&date[lt]=2026-02-01 (the deepObject style).
  • Arrays: the key is repeated, for example status=valid&status=canceled (form with explode).

The API also accepts the most common array variants (status[]=valid&status[]=canceled and status[0]=valid&status[1]=canceled), so existing integrations keep working.

Page pagination (pagination=page)

This is the default mode. In addition to limit, it accepts:

ParameterDescription
pagePage number to return, starting from 1. The maximum depends on the 3,000-result cap (see below): with the default limit of 100, the maximum page is 30.

On the listings (/customers, /invoices, …) page is the shared parameter, starting at 1. Catalog searches (/catalogs/...) accept the same pagination parameters, with limit between 1 and 100. The maximum page is not fixed: it follows from the 3,000-result cap (30 pages with the default limit of 100).

Response:

{
"data": [ ... ],
"page": 1,
"total_pages": 3,
"total_results": 250,
"totals_are_capped": false
}

Cursor pagination (pagination=cursor)

Instead of a page number, each response returns two cursors that let you move forward or backward through the results:

ParameterDescription
afterReturns the results after the given cursor.
beforeReturns the results before the given cursor.

Response:

{
"data": [ ... ],
"previous_cursor": null,
"next_cursor": "d176717f4000000.i65a8282775d4e9263da48115",
"total_results": 250,
"totals_are_capped": false
}

total_results and totals_are_capped are only included on the first page of the search (when neither after nor before is sent): the total does not change between pages, so subsequent pages only return data and the cursors.

  1. First page: obtained by not sending after or before. previous_cursor returns null, meaning there are no previous pages.
  2. Next page: send after with the next_cursor value from the previous response.
  3. Previous page: send before with the previous_cursor value from the previous response.

The presence of previous_cursor and next_cursor is the only source of truth for whether there are more results: if next_cursor is null, you reached the end of the list; if previous_cursor is null, you are on the first page.

# First page
curl "https://www.facturapi.io/v2/invoices?pagination=cursor&limit=10" \
-G \
-H "Authorization: Bearer sk_test_API_KEY"

# Next page (use next_cursor from the previous response)
curl "https://www.facturapi.io/v2/invoices?pagination=cursor&limit=10&after=NEXT_CURSOR" \
-G \
-H "Authorization: Bearer sk_test_API_KEY"

# Previous page (use previous_cursor from the previous response)
curl "https://www.facturapi.io/v2/invoices?pagination=cursor&limit=10&before=PREVIOUS_CURSOR" \
-G \
-H "Authorization: Bearer sk_test_API_KEY"

Cursors are opaque tokens tied to the search that generated them: do not combine them with a different search or share them between queries. Using them with a different search may return unexpected results.

Totals and the 3,000 limit

Both modes return total_results with the number of elements matching the search, capped at 3,000:

  • If there are 3,000 or fewer results, total_results is exact and totals_are_capped is false.
  • If there are more than 3,000, total_results returns 3000 and totals_are_capped is true, indicating the real total is "more than 3,000".
{
"data": [ ... ],
"previous_cursor": null,
"next_cursor": "d176717f4000000.i65a8282775d4e9263da48115",
"total_results": 3000,
"totals_are_capped": true
}

In both modes, total_results is informational and the count is capped at 3,000 without traversing the whole collection. The practical advantage of pagination=cursor is not count speed (both modes cap it), but reach and stable pages.