Skip to main content

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

ParameterMeaning
limitRecords per page, 1 to 100, default 25
starting_afterReturn records after this cursor, moving forward
ending_beforeReturn 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.