docs
API

Paging results

Which nima endpoints return pages, the page parameters each takes, the 3000-row ceiling, and which lists are capped arrays instead of pages.

Seven endpoints you can call with an API key or token return a page. Every other list-shaped response they give is a bounded array, a text window, or a cursor.

Paging is offset based. There are no cursors on the list endpoints and no Link header. Some endpoints spell the page size limit and some spell it pageSize, and a wrong spelling is ignored without an error.

Paging is on for every workspace. The outbound-action list stays empty until a workspace owner or admin turns outbound dispatch on for the workspace; see Actions. For the operations list, see Operations.

Paginated endpoints and their page parameters

Seven endpoints take page and a page size and answer with data and meta.

PathScopeMCP toolPage parameters
GET /v1/signalssignals:readsearch_signalspage, limit
GET /v1/beaconssignals:readlist_beaconspage, limit
GET /documentsdocuments:readlist_specspage, pageSize, sort
GET /documents/{id}/versionsversions:readnonepage, pageSize, sort
GET /v1/operationsdocuments:readlist_operationspage, pageSize
GET /v1/inboxinbox:readget_inboxpage, limit
GET /v1/dispatch/actions (alias GET /v1/actions)actions:approvelist_pending_actionspage, limit

/documents carries no /v1 prefix. That is its real path today.

A document keeps only its 10 most recent versions, so /documents/{id}/versions always fits on one page at the default size.

actions:approve cannot be minted for an API key or OAuth client today, so GET /v1/dispatch/actions is reachable only with a signed-in session. See Scopes.

ParameterDefaultMinimumMaximum over HTTP
page11none on /v1/operations; on the other six, page × page size must stay at or under 3000 (see the 3000-row paging ceiling)
limit201100
pageSize201100

Through MCP, search_signals, list_beacons and list_specs cap the page size at 50. list_operations, get_inbox and list_pending_actions allow 100.

The two names are not interchangeable. pageSize sent to /v1/signals, /v1/beacons, /v1/inbox or /v1/dispatch/actions is ignored and you get 20 rows. limit sent to /documents or /v1/operations is ignored and you get 20 rows. Read meta.pageSize to see what the server actually used.

Page response shape

Every paginated endpoint answers with data (the page) and meta (counts).

curl -s "https://api.getnima.io/v1/signals?page=2&limit=25&sortBy=newest" \
  -H "Authorization: Bearer $NIMA_API_KEY"
{
  "data": [
    {
      "id": "3f8a1c62-9d47-4b21-8e0a-5c7d2b64e913",
      "preview": "Bulk export times out on workspaces over 40k rows",
      "status": "ready",
      "classification": "bug",
      "impact": "high",
      "source": "slack",
      "sourceCount": 3,
      "createdAt": "2026-09-18T09:14:02.441Z"
    }
  ],
  "meta": { "page": 2, "pageSize": 25, "totalItems": 148, "totalPages": 6 }
}

One item is shown and its fields are abbreviated. The full object is on the resource page.

FieldTypeMeaning
dataarrayThe page. Empty array past the last page, never null.
meta.pagenumberThe page you got, 1 based.
meta.pageSizenumberRows per page after clamping, which is not always what you asked for.
meta.totalItemsnumberRows matching the query.
meta.totalPagesnumbertotalItems / pageSize rounded up, and exactly 0 when nothing matched.
meta.countMode"exact" or "estimated"How totalItems was obtained. Absent on almost every response, and absent means exact.

The same meta comes back through the MCP tools. search_signals, list_beacons and list_specs trim each item in data to a summary; the other list tools return the same items as HTTP. The operations list returns only the first line of each operation's endDetail, at most 300 characters, on both surfaces; call get_operation for the full text.

Signal grouping changes the response shape

GET /v1/signals accepts groupBy=impact, groupBy=product_area or groupBy=classification. Any of those replaces the response with a different object, and meta loses everything but the total:

{
  "groups": [
    { "key": "high", "label": "High Impact", "count": 12, "data": [] },
    { "key": "low", "label": "Low Impact", "count": 8, "data": [] }
  ],
  "meta": { "totalItems": 148 }
}

Each group's data holds its signals, left out above. The groups are built from the rows on the current page only, so count is a count within that page and never a count across the query. groupBy=none returns the normal paged shape. Any other value returns a single group keyed all. Grouping is an HTTP parameter; the search_signals tool does not accept it.

The 3000-row paging ceiling

page multiplied by the effective page size must be 3000 or less on /v1/signals, /v1/beacons, /documents, /documents/{id}/versions, /v1/inbox and /v1/dispatch/actions. The check runs before the query, so it costs you nothing but a 400. The page size is clamped to 100 first, which means a larger page size buys no extra depth.

GET /v1/operations is the exception: it has no ceiling and pages by plain offset.

Page sizeLast reachable page
20150
25120
5060
10030
curl -s "https://api.getnima.io/v1/signals?page=200&limit=20" \
  -H "Authorization: Bearer $NIMA_API_KEY"
{
  "statusCode": 400,
  "error": "VALIDATION_ERROR",
  "message": "Cannot page beyond 3000 rows. Narrow the filter instead of paging deeper.",
  "details": { "page": 200, "pageSize": 20, "maxOffset": 3000 },
  "timestamp": "2026-09-22T11:04:18.902Z",
  "path": "/v1/signals?page=200&limit=20"
}

There is no export endpoint and no cursor to fall back on. To reach rows past the ceiling, narrow the query instead: filter by status, classification, source or impact, or sort by newest and poll the top of the list rather than walking to the bottom of it.

Walk a paginated list in a loop

To read every page, repeat the request with page increasing until a page comes back shorter than the page size. Keep the filters and the page size identical across pages; changing either mid-walk reshuffles what lands on which page.

page=1
while [ "$page" -le 150 ]; do
  body=$(curl -s "https://api.getnima.io/v1/signals?page=$page&limit=20" \
    -H "Authorization: Bearer $NIMA_API_KEY")
  echo "$body" | jq -c '.data[]'
  [ "$(echo "$body" | jq '.data | length')" -lt 20 ] && break
  page=$((page + 1))
done

Stop on a short page rather than on totalPages, because totalPages can be an estimate (next section). Every ordering on the signal, beacon and document lists ends in a unique tiebreak key, so page boundaries hold for as long as the underlying rows do. Version history orders on creation time alone.

None of these endpoints carries a route limit of its own; all fall to the default of 100 requests per 60 seconds, counted per client IP address rather than per key. A tight walk of 150 pages will hit it. A 429 carries a Retry-After header in seconds. See Rate limits.

Estimated counts on filtered signal lists

meta.countMode appears on GET /v1/signals only, and only when the request carried a filter whose predicate no index can drive: the fields origin, productArea, stakeholder, inBeacon and hasSpec, or any or group with more than one condition. Every other request, and every other endpoint, counts exactly and omits the field.

When countMode is "estimated", totalItems is the query planner's guess and totalPages is derived from that guess. Both can move between two pages of the same query. Treat them as a rough size, not as a loop bound.

A short page is always the last page, so it reports "exact" with a real total no matter what the estimate said. If the estimate is unavailable the request falls back to an exact count silently, and countMode is absent again.

Sort parameters on paginated lists

EndpointParameterValuesDefaultDirection
/v1/signalssortBypriority, newest, volumeprioritysortOrder=asc or desc, honoured on newest and volume only
/v1/beaconssortByrank, newest, evidencerankalways descending, there is no sortOrder
/documentssortJSON array, see belowposition, then most recently updatedfixed
/documents/{id}/versionssortJSON array, see belownewest firstfixed
/v1/operations, /v1/inbox, /v1/dispatch/actionsnonenonenewest firstfixed

An unrecognised sortBy is not an error on either list. It falls back to the default, so sortBy=rank on signals silently returns the priority order. priority is always descending and ignores sortOrder.

sort on the two document endpoints is a JSON-encoded array of {"field": "...", "direction": "asc"|"desc"}. An entry whose field is not a real column of the listed resource is dropped without an error, and the entries that survive are appended after the endpoint's own ordering, so they can only break ties. The document list already ends in a unique key, which means sort changes nothing there at all.

Invalid page parameters and the errors they return

Signals and beacons parse leniently and never reject a bad page or limit:

SentYou get
limit=020
limit=abc20
limit=25xyz25
limit=-51
limit=1000100
page=0, page=-3, page=abc1

The other endpoints validate strictly and return a 400 for page=0, a page size of 0, a page size above 100, or a non-numeric value. That covers /documents, /documents/{id}/versions, /v1/operations, /v1/inbox and /v1/dispatch/actions. On /documents, /documents/{id}/versions, /v1/inbox and /v1/dispatch/actions the 400 body carries error: "Bad Request" and no field name, so match on the status code. On /v1/operations the 400 carries error: "VALIDATION_ERROR" and the message Invalid operation list query.

Unknown query parameters are ignored everywhere over HTTP. Through MCP the same tools reject an unknown argument outright with an invalid-arguments error.

StatuserrorWhen
400VALIDATION_ERRORPast the 3000-row ceiling, a malformed filter, an unusable source cursor, an invalid /v1/operations query.
400Bad RequestA page parameter failed strict validation on the document, inbox or actions lists.
403INSUFFICIENT_PERMISSIONSThe key or token is missing the scope in the table above.
429RATE_LIMITEDOver the request budget. Retry-After says how many seconds to wait.

Capped lists that are not paginated

These operations return a plain array or object with a fixed maximum. They take no page, and rows past the cap are not reachable.

OperationShapeCap
GET /v1/watches (list_watches)Array of your own watches200. Active first, then paused and expired, newest first within each.
GET /v1/scheduled-messages (list_scheduled_messages)Array of your own scheduled messages200. Pending reminders by delivery time first, then recent history.
GET /v1/events (get_history)Object with the subject and an events array, newest firstlimit 1 to 200, default 50. Needs subjectKind and subjectId.
GET /v1/identities/unlinked (list_unlinked_identities){ "identities": [...] }, newest firstlimit 1 to 100, default 50. Admin only.
GET /v1/code-intel/blast-radius (blast_radius)Dependent files, tests and specsAt most 400 impacted files, then truncated is true and the answer is a prefix. Input takes at most 50 paths and symbols combined, depth 1 to 4, default 2.
GET /v1/code-intel/data-model (get_data_model)Tables per repositoryWithout table: 400 tables, then truncated. With table: 20 matching tables. table is at most 200 characters.
GET /documents/{id}/deliveryObject with the spec's card and a pullRequests arraypullRequests holds at most 20, most recently updated first. The 21st is unreachable.
GET /v1/code-intel/searchObject with repositories, droppedRepositories and a results array of file hitsresults: limit 1 to 20, default 8. Symbols per hit are capped at 8.
GET /v1/knowledge-graph/askMarkdown text by default; compact JSON with mode=agentlimit 1 to 20, default 8.
GET /prd/{documentId}/search, GET /prd/{documentId}/criteriaArraysUnbounded. Whatever the document holds comes back in one response.
GET /v1/signals/facets, GET /v1/beacons/facetsOne counts objectNot a list. Over a 500ms budget it returns "mode": "timeout" with total, facets and arrays all null.

Continue reading a document or a signal source

Two operations continue through a large body instead of paging: the document Markdown window and the signal source cursor.

GET /documents/{id}/markdown takes offset and limit over the characters of the serialized document. limit maxes out at 12000 and defaults to 8000 when you send only an offset. Send neither and you get the whole document with no pagination object at all. Send either and the response adds pagination with offset, limit, totalCharacters and nextOffset, where nextOffset is null on the last window.

GET /v1/signals/{id}/source returns pages of about 40,000 bytes with a nextCursor you pass back as cursor. The cursor is bound to one signal, one source and one part; reusing it anywhere else returns a 400 telling you to restart without it. If the source document changes while you are paging, the next call returns a 400 rather than a spliced document, and you start over.