Pagination
List endpoints return a page at a time and hand you a cursor for the next one. There are no page numbers and no offsets.
{
"data": [],
"has_more": true,
"next_cursor": "b9c3f0e8-1111-4222-8333-444455556666"
}
Parameters
| Parameter | Meaning |
|---|---|
limit | Records per page, 1 to 100, default 25 |
starting_after | Return records after this cursor, moving forward |
ending_before | Return records before this cursor, moving backward |
Pass starting_after or ending_before, never both.
Walking a list
Read has_more to decide whether to continue and next_cursor to say where from. Do not
construct a cursor yourself and do not assume it is an id, a timestamp or anything else
inspectable. It is opaque and its format may change.
curl "https://api.epostix.com/v1/emails?limit=100" \
-H "Authorization: Bearer $EPOSTIX_API_KEY"
curl "https://api.epostix.com/v1/emails?limit=100&starting_after=b9c3f0e8-..." \
-H "Authorization: Bearer $EPOSTIX_API_KEY"
A loop that terminates:
let cursor = null;
do {
const url = new URL("https://api.epostix.com/v1/emails");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("starting_after", cursor);
const page = await fetch(url, {headers}).then((r) => r.json());
await handle(page.data);
cursor = page.next_cursor;
} while (cursor);
Stop on next_cursor being absent rather than on data being empty. They usually coincide,
but has_more and next_cursor are the fields that describe the list, and a page can be
shorter than limit without being the last.
Why cursors rather than offsets
Records arrive constantly. With ?page=2&per_page=100, a message that arrives between your
first and second request shifts everything down by one, so you see a record twice and miss
another. A cursor points at a fixed position in the list, so new records appear where they
belong instead of shuffling the page under you.
The practical consequence: you can page through a large list while sending continues, and still see every record exactly once.
Filtering
Filters combine with pagination and should be repeated on every request in a walk, since the cursor describes a position in the filtered list. Filtering after the fact in your own code means fetching far more than you need.