Pagination

List endpoints that support cursor pagination accept an opaque ?cursor= query parameter — never a page number. New integrations should use cursor exclusively.

Cursor format

A cursor is an opaque, base64url-encoded token. Treat it as a black box — don't decode it, don't construct one by hand, don't assume its format is stable across releases. Pass back exactly what the API gave you.

curl
# First page
curl -sS "https://api.owlmar.com/v1/maintenance/vessel/$VESSEL_ID?limit=25" \
-H "Authorization: Bearer $OWLMAR_API_KEY"
# Response includes pagination.cursor — pass it back verbatim for the next page
curl -sS "https://api.owlmar.com/v1/maintenance/vessel/$VESSEL_ID?limit=25&cursor=eyJpZCI6Ii4uLiJ9" \
-H "Authorization: Bearer $OWLMAR_API_KEY"

hasMore / total

json
{
"data": [ /* up to `limit` rows */ ],
"pagination": { "cursor": "eyJpZCI6Ii4uLiJ9", "limit": 25, "hasMore": true, "total": 143 }
}
  • hasMoretrue if there's another page. When false, cursor is null — stop paging.
  • total — the full filtered row count, not just the current page. Safe to show in a UI ("143 results") without fetching every page first.
  • limit — defaults to 25, maximum 100.
Note

Cursors degrade gracefully. If the row a cursor pointed at was deleted between requests, pagination silently resumes from approximately where it left off — you won't get a "cursor invalid" error, and you won't skip or repeat an entire page the way an offset-based scheme can under concurrent writes.

The 3 known-gap endpoints

Not every list endpoint has cursor support yet — three are offset-only for structural reasons, not oversights:

EndpointWhy
GET /v1/vesselsComputing the multi-vessel downgrade-lock annotation (isLocked) needs the full owned-vessel set, not one page at a time. Also a deliberately bare array for backward compatibility.
GET /v1/vessels/:id/teamSame structural reason — the downgrade-lock computation needs the full crew roster. Also a genuinely bounded list, not a growing log.
GET /v1/compliance/vessel/:vesselId/drills/cadencesNot a list at the top level ({ data: { cadences, overdue, dueSoon } }) and bounded by ISM drill-type count — cursor pagination doesn't apply.

A handful of other endpoints have narrower gaps on specific query branches only (hybrid search, a couple of alternate sort orders) — each endpoint's own reference page documents exactly which branch supports cursor.

?page= sunset

Some list endpoints still accept legacy ?page=&limit= for backward compatibility. New integrations should not use it. Using it triggers deprecation headers on the response:

text
Sunset: Sat, 30 Jan 2027 00:00:00 GMT
Deprecation: true

Sunset date: 2027-01-30 — minimum 12 months' notice from the day the header first appeared, per this API's versioning policy. After that date, ?page= support may be removed from a given endpoint. ?cursor= is not going anywhere.

Warning

If you're paging through more than one screenful of results today with ?page=, migrate to ?cursor= now rather than waiting for the sunset date — the response shape difference is small (an opaque cursor string instead of a page number) but touches your loop's stop condition.

See Fleet Expense Reconciliation for a full worked example of paging through a large result set with ?cursor=.