Pagination

Every list endpoint returns the same envelope and uses opaque, cursor-based paging. You request a page size with limit, and follow next_cursor until the API tells you there's nothing more. Cursors are encrypted tokens tied to the exact query that produced them.

The list envelope

  • Name
    object
    Type
    string
    Description

    Always list.

  • Name
    data
    Type
    array
    Description

    The page of resources, newest-first unless the endpoint documents otherwise.

  • Name
    has_more
    Type
    boolean
    Description

    Whether more pages exist. Stop walking when this is false.

  • Name
    next_cursor
    Type
    string
    Description

    Pass this back as cursor to fetch the next page. null on the last page.

  • Name
    url
    Type
    string
    Description

    The path this list was fetched from.

Response

{
  "object": "list",
  "data": [
    { "object": "template", "id": "template_00a1b2c3d4e5f607" }
    // ...
  ],
  "has_more": true,
  "next_cursor": "hf8a2c1b9d0e...",
  "url": "/v1/templates"
}

Request parameters

  • Name
    limit
    Type
    integer
    Description

    Page size, 1–100 (default 10). A value outside that range is rejected with 400 parameter_invalid (on limit) rather than silently clamped. A few endpoints fix their own size — see below.

  • Name
    cursor
    Type
    string
    Description

    The previous response's next_cursor, passed back verbatim.

Walking a collection

Fetch the first page, then keep passing next_cursor back as cursor until has_more is false.

GET
/v1/templates
# First page
curl -s -G https://api.synexcloud.com/v1/templates \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -d limit=50

# Next page
curl -s -G https://api.synexcloud.com/v1/templates \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -d limit=50 \
  -d cursor=hf8a2c1b9d0e...

Cursor rules

  • Never construct or modify a cursor. A malformed or tampered cursor returns 400 invalid_cursor.
  • Don't persist cursors long-term. To resume later, restart from the first page.
  • Cursors are tied to their query. Every filter joins the cursor's scope, so a cursor minted under one set of filters and replayed under another is 400 invalid_cursor. Keep the filters identical for the whole walk and vary only limit.

Creation-time filters

Four list endpoints accept a creation window:

EndpointFilter runs as
GET /v1/templateskey condition
GET /v1/ontologieskey condition
GET /v1/importsSQL WHERE
GET /v1/eventsbounds on the evt_ id
  • Name
    created[gte]
    Type
    string
    Description

    Only objects created at or after this time.

  • Name
    created[lte]
    Type
    string
    Description

    Only objects created at or before this time.

Both bounds are inclusive of the whole second they name and accept unix seconds or an ISO string. An inverted range — a created[gte] later than created[lte] — is rejected with 400 parameter_invalid rather than returning an empty page.

None of them scans, so a narrow window is exact and cheap. Only templates can return a short page, and only because its category filter is applied after the page is read.

The bounds join the cursor's scope like every other filter: a cursor minted under one window is invalid_cursor under a different one, or under none.

Endpoints that page differently

A few lists differ from the standard shape — in page size, order, or where a filter runs. Assume the standard shape and these will surprise you:

EndpointDifference
GET /v1/ontologies/:ontology/passportssearch= is applied after a page is read, so a page can come back short or empty while has_more is true
GET /v1/templatesThe category filter is applied after the page is read, so short pages are normal
GET /v1/imports/:id/rowsNo limit; reads a fixed 100 rows per page. The status filter is applied after the page is read, so a filtered page can come back short
GET /v1/data_request_sets/:id/childrenPages in row order rather than newest first
GET /v1/data_request_sets/:id/fulfil/sourcesUnpaginated — a fixed list in one response. has_more is always false
GET /v1/ontologies/:id/versionsUnpaginated — every version in one response. has_more is always false
GET /v1/team/membersUnpaginated — every member in one response. has_more is always false
POST /v1/lab/queryNot a list endpoint: pages by a keyset cursor carried inside the AST (ast.cursor, page size ast.limit), and a bad one is 422 bad_cursor

Where a filter is applied after the page is read, page until has_more is false and never terminate on a short page. An empty page is not an answer about existence: for search= in particular, no rows on a page means nothing matched inside the window that page scanned, not that no such passport exists.

Was this page helpful?