Skip to main content
List endpoints return a consistent envelope:
Two paging modes are available on most list endpoints:
  • Offset (limit + offset) — simple, fine for small result sets and one-off reads.
  • Cursor (cursor) — recommended for large exports and incremental syncs: constant cost at any depth, and rows inserted while you page can never make you skip or double-read a record.

Parameters

integer
default:"20"
Records per page. Minimum 1, maximum 100. Applies in both modes.
integer
default:"0"
Number of records to skip. Combine with limit to page: ?limit=50&offset=100 returns records 101–150. Ignored when cursor is set.
string
Opaque token from a previous response’s meta.next_cursor. When set, results page by a stable (created_at, id) keyset — newest first — and offset is ignored. Available on every list endpoint except /tutors, /students, /parents, /conversations, and the /coverage/* routes (offset-only).

The meta object

Cursor-mode responses also carry a standard Link: <...>; rel="next" header, so generic HTTP paginators can follow pages without parsing the body.

Polling for new records

Tools like Zapier and Make poll a list endpoint on a timer, read only the first page, and treat any record ID they haven’t seen before as new. They do not page backwards through history. That only works if new records sort to the top. /students and /parents are ordered newest enrolment first (enrolled_at descending) for exactly this reason — poll page 1 on your interval and you will see every new record.

/sessions has two sort axes

/sessions accepts ?sort=created_at (default) or ?sort=starts_at:
  • created_at (default) — when the session was booked. Poll page 1 on this axis and you will see every new booking, same as every other endpoint above.
  • starts_at — when the session is scheduled to happen. Useful for an agenda/calendar view (“what’s coming up”), but do NOT poll it to detect new bookings: a session booked today for next month sorts above one booked today for tomorrow, and a session backfilled for a past date sorts near the bottom.
from/to always filter on starts_at, regardless of which sort you use — so a calendar consumer typically sets both sort=starts_at and a from/to window. If you’d rather be pushed than poll, webhooks deliver the same records the moment they change.

enrolled_at is the cursor, not created_at

On /students and /parents these two fields mean different things:
  • enrolled_at — when they joined your organisation. This is the sort key.
  • created_at — when the underlying account was created: possibly in a different organisation, possibly years earlier.
A student who has had an account since 2024 but joined your org this morning has an enrolled_at of this morning and sits at the top of page 1. Sort and filter on enrolled_at. If you’d rather be pushed than poll, webhooks deliver the same records the moment they change.

Paging through everything

Preferred (cursor): start with cursor=start, then follow meta.next_cursor until it comes back null:
On endpoints whose default order is already newest-created-first (most of them), a full plain offset page also includes a next_cursor, so you can switch to cursor paging from page 2 without starting over. Cursor pages are always ordered newest-created first regardless of the endpoint’s offset-mode order (so /sessions by cursor walks booking order, not schedule order — exactly what a sync wants). Legacy (offset): increase offset by limit until a page returns fewer than limit records (or, where total is present, until offset + count >= total). Offset paging keeps working forever, but deep offsets get slower and a record created mid-walk shifts the window, so a sync can miss or double-read a row.
Single-record and rollup endpoints (e.g. /sessions/{id}, /reports/summary, /parents/{id}/balance) return the object directly, with no data/meta envelope.