Pagination
All list endpoints in the Moloni ON API support offset-based pagination through the options.pagination argument. This guide explains how pagination works, what defaults are applied, and how to page through large result sets.
How it works
List queries (e.g. customers, products, documents) accept an options argument containing a pagination object with two fields:
| Field | Type | Description |
|---|---|---|
page | Int! | The page number to retrieve (1-indexed) |
qty | Int! | The number of items per page |
The response includes an options.pagination object with the same fields plus a count, the total number of matching records across all pages.
Defaults and limits
| Rule | Value |
|---|---|
Default page | 1 |
Default qty | 50 |
Minimum page | 1 (values ≤ 0 are corrected to 1) |
Minimum qty | 1 (values ≤ 0 are corrected to 1) |
Maximum qty | 1000 (values above are capped to 1000) |
If you omit pagination entirely, the API defaults to page 1 with 50 items.
Basic example
Fetch the first 10 customers:
query {
customers(companyId: 1, options: {
pagination: { page: 1, qty: 10 }
}) {
errors { field msg }
data {
customerId
name
vat
}
options {
pagination {
page
qty
count
}
}
}
}
Response:
{
"data": {
"customers": {
"errors": [],
"data": [
{ "customerId": 1, "name": "João Silva", "vat": "123456789" },
{ "customerId": 2, "name": "Maria Santos", "vat": "987654321" }
],
"options": {
"pagination": {
"page": 1,
"qty": 10,
"count": 127
}
}
}
}
}
In this example, there are 127 total customers. At 10 per page, that's 13 pages.
Calculating total pages
Use the returned count and qty to calculate the total number of pages:
totalPages = ceil(count / qty)
Using the example above: ceil(127 / 10) = 13 pages.
Paging through results
To fetch the next page, increment the page value:
query {
customers(companyId: 1, options: {
pagination: { page: 2, qty: 10 }
}) {
errors { field msg }
data {
customerId
name
}
options {
pagination { page qty count }
}
}
}
A typical loop to fetch all pages looks like this:
let page = 1;
const qty = 50;
let allRecords = [];
while (true) {
const response = await fetch("https://api.molonion.pt/v1", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessToken}`,
},
body: JSON.stringify({
query: `
query {
customers(companyId: 1, options: { pagination: { page: ${page}, qty: ${qty} } }) {
errors { field msg }
data {
customerId
name
}
options {
pagination { page qty count }
}
}
}
`,
}),
});
const { data } = await response.json();
const result = data.customers;
if (result.errors.length) {
throw new Error(result.errors[0].msg);
}
allRecords.push(...result.data);
const { count } = result.options.pagination;
const totalPages = Math.ceil(count / qty);
if (page >= totalPages) break;
page++;
}
Combining with sorting and search
Pagination works alongside order and search within the same options object. Filters are applied before pagination, so count reflects the filtered total:
query {
customers(companyId: 1, options: {
pagination: { page: 1, qty: 20 }
order: { field: name, sort: ASC }
search: { field: ALL, value: "Silva" }
}) {
errors { field msg }
data {
customerId
name
}
options {
pagination { page qty count }
}
}
}
Here count returns only the number of customers matching the search, not the total customer count.
Omitting pagination
If you don't pass pagination at all, the API still paginates your results using the defaults (page 1, 50 items). Pagination is always applied to list endpoints.
# This returns the first 50 results by default
query {
customers(companyId: 1) {
data { customerId name }
}
}
Next steps
- Filtering, Search & Ordering: Combine pagination with filters and search in the same
optionsobject - Error Handling: Understand the error model returned by all list operations