Pagination
Every list endpoint in eFakturuj returns the same envelope: a slim
items array plus the total row count, the page index, and the page
size. Iterate by incrementing page until you've covered total —
there is no cursor today.
Query parameters
All list endpoints (GET /invoices, GET /customers) accept the
same paging surface:
| Param | Type | Default | Range | Notes |
|---|---|---|---|---|
page | int | 1 | >= 1 | 1-based page index. |
page_size | int | 20 | 1 – 100 | Hard cap is 100 — larger values are rejected with 422. |
q | string | none | <= 200 chars | Server-side substring search (case-insensitive). On invoices matches invoice number, supplier name, buyer name. On customers matches name and VAT id. |
GET /invoices adds extra filters and sort: comma-separated
status=draft,validated, ISO-date from_date / to_date against
issue_date, a created_by UUID, and a sort clause that lists
columns separated by commas with an optional - prefix for
descending order:
?sort=-issue_date,total_gross
GET /customers does not support sort — results are always
returned alphabetically by name.
Response envelope
Both endpoints return the same shape (InvoiceListPaginated /
CustomerListPaginated in the OpenAPI):
{
"items": [ /* page_size rows max */ ],
"total": 123,
"page": 1,
"page_size": 20
}
items carries a list-row projection — slim columns for table
rendering. The full record is one fetch away (GET /invoices/{id},
GET /customers/{id}).
Iteration recipe
Loop until page * page_size >= total. Pull the first page first so
you know total before deciding whether to keep going.
# curl — first page
curl -H 'Authorization: Bearer EFK_TOKEN' \
'https://api.efakturuj.sk/api/v1/invoices?page=1&page_size=100'
# Python — drain every page
import httpx
def all_invoices(token: str):
page = 1
with httpx.Client(headers={"Authorization": f"Bearer {token}"}) as c:
while True:
r = c.get(
"https://api.efakturuj.sk/api/v1/invoices",
params={"page": page, "page_size": 100},
)
r.raise_for_status()
body = r.json()
yield from body["items"]
if page * body["page_size"] >= body["total"]:
return
page += 1
// Dart — drain every page
Future<List<Map<String, dynamic>>> allInvoices(String token) async {
final out = <Map<String, dynamic>>[];
var page = 1;
while (true) {
final res = await dio.get(
'https://api.efakturuj.sk/api/v1/invoices',
queryParameters: {'page': page, 'page_size': 100},
options: Options(headers: {'Authorization': 'Bearer $token'}),
);
out.addAll(List<Map<String, dynamic>>.from(res.data['items']));
if (page * (res.data['page_size'] as int) >=
(res.data['total'] as int)) break;
page += 1;
}
return out;
}
Performance tips
- Set
page_size=100for full drains. The 20-row default exists for human dashboards — programmatic callers should always paginate at the cap. - Filter on the server, not the client. Pass
q,status,from_date/to_date, andcreated_byto the API rather than pulling everything and filtering in your code. The same indexes power list and search. - Don't paginate when you're really polling. If you only care
about new rows since last sync, scope with
from_dateand remember the lastcreated_atyou saw. Webhooks (Webhooks) remove the need to poll for invoice status changes entirely. - Cursor pagination is on the roadmap for very large drains
(10k+ rows) where deep
pagevalues become slow on the indexed scan. Today thepage×page_sizeinterface is consistent across both list endpoints — track Changelog for cursor support.