Skip to content
ShaireDevelopers

Pagination and filtering

Two endpoints page. The rest take a cap, and a cap is not a page size.

The two that page

GET /api/notifications and GET /api/audit return a page and a cursor:

{
  "items": [],
  "nextCursor": "2026-08-30T14:12:03.221Z|0f2a..."
}

Pass it back as ?cursor= to get the next page. When nextCursor is null, you have reached the end.

nextCursor is the only end signal. A page shorter than the limit does not mean there is nothing after it. The cursor is taken from the last row fetched rather than the last one that survived filtering, so a page can come back with three items, a non-null cursor, and more waiting behind it. A loop that stops on a short page stops early.

The cursor is a timestamp and an id joined by a pipe, and it is deliberately not encoded. Two values a developer benefits from seeing in a URL are worth more than the appearance of opacity. Treat it as a token to pass back rather than something to construct, and expect a 400 if you send one that does not parse.

Everything else takes a cap

GET /api/tickets accepts limit between 1 and 500, defaulting to 200. That is a maximum, not a page size. There is no cursor and no page two: a list of 600 tickets returns 500 and the response says nothing about the rest.

This is the mistake worth avoiding on this page. A client that treats the limit as a page size and looks for an offset finds none, quietly processes the first 500, and reports success.

Narrow with filters instead. GET /api/tickets takes the same filter shape the saved views in the app use: list, milestone, cycle, status, systemic state, assignee, tag, priority, due date, and a custom field with a value. Filtering by list and status usually turns “too many” into a number you can hold.

GET /api/search caps at 50 and defaults to 12.

Watching for changes

GET /api/activity/feed returns what changed across the workspace in one request, so a job on a timer can fetch only the objects that moved rather than re-reading a list. GET /api/notifications covers the same ground for events addressed to one person, and pages with a cursor as described above.

For push rather than poll, a workspace admin registers an endpoint under Settings → Webhooks in the app. A delivery describes the change and carries the subject’s type and id, so a receiver reads those and calls the API for whatever detail it needs.