Pagination
OneRoster collection endpoints support two ways of paging:
| Parameters | Use it for | |
|---|---|---|
| Offset | limit, offset | Jumping to an arbitrary page, small result sets |
| Keyset (cursor) | limit, afterSourcedId | Full pulls and any large collection |
Both take limit, which defaults to 100.
Offset pagination
GET /enrollments?limit=1000&offset=2000Page until you receive fewer rows than limit. Results are ordered by sourcedId ascending unless you pass sort.
The catch is cost: the database still has to walk past every row you skipped, so page 500 is far more expensive than page 1. On a large tenant a full offset-based pull gets progressively slower the deeper it goes.
Keyset pagination
afterSourcedId takes the sourcedId of the last row of the previous page and returns the rows after it, in sourcedId order. There is nothing to skip, so every page costs the same.
First page — no cursor:
GET /enrollments?limit=1000Then take the sourcedId of the last item in the response and pass it back:
GET /enrollments?limit=1000&afterSourcedId=abc-123:class-7b:org-12Repeat until you receive fewer than limit rows.
Rules and caveats
offset,sortandorderByare ignored whenafterSourcedIdis present. Results are always ordered bysourcedIdascending. If you need a different sort order, you must use offset pagination — and accept its cost.filterworks normally and is applied alongside the cursor.X-Total-Countis the total number of matching rows, not the number remaining after the cursor. It does not shrink as you page. Don't use it to decide whether to continue; use "fewer rows thanlimit".- The cursor is a
sourcedId, not an opaque token. It must be a realsourcedIdfrom the collection you are paging; passing an id from another collection returns whatever sorts after that string. - Concurrent changes. A row inserted with a
sourcedIdthat sorts before your cursor will be missed by the current pass, and a row that sorts after will be picked up. This is the same eventual-consistency trade-off as offset paging, but without offset paging's risk of skipping or duplicating rows when the underlying set shifts mid-pull.
Link header
Responses carry an RFC 5988 Link header. For keyset requests it contains:
rel="first"— the start of the collection (offset-based,offset=0)rel="next"— the next page, already built with the correctafterSourcedId, present only when there are more rows
Following rel="next" until it is absent is the simplest correct way to page. Note that rel="prev" and rel="last" are not emitted for keyset requests — a cursor can only go forward.
Offset-based requests get the full first / prev / next / last set.
Endpoints supporting afterSourcedId
Rostering (/ims/oneroster/rostering/v1p2)
/users,/students,/teachers/orgs/classes/courses/academicSessions/enrollments/afterschools/{afterschoolSourcedId}/users
Gradebook (/ims/oneroster/gradebook/v1p2)
/lineItems,/categories,/results/assessmentLineItems,/assessmentResults
Absence
/absences/detail,/absences/summary
Nested collection endpoints not listed above (for example /classes/{sourcedId}/students) currently accept limit/offset only.