Skip to main content
Signa uses cursor-based pagination for all list and search endpoints. Cursors provide stable, consistent iteration over datasets that may change between requests — no skipped records, no duplicates, no performance degradation at depth.

Basic Usage

Your first request specifies a limit (items per page). The response includes a cursor and has_more flag:
curl -H "Authorization: Bearer sig_YOUR_KEY" \
  "https://api.signa.so/v1/trademarks?limit=50"
Response:
{
  "object": "list",
  "data": [
    { "id": "tm_7d4e1f2a", "object": "trademark", "mark_text": "SIGNA", ... },
    { "id": "tm_3b8c9d0e", "object": "trademark", "mark_text": "AURORA", ... }
  ],
  "has_more": true,
  "pagination": {
    "cursor": "eyJpZCI6IjNiOGM5ZDAw..."
  },
  "request_id": "req_abc123"
}
To fetch the next page, pass the cursor from the previous response:
curl -H "Authorization: Bearer sig_YOUR_KEY" \
  "https://api.signa.so/v1/trademarks?limit=50&cursor=eyJpZCI6IjNiOGM5ZDAw..."
When has_more is false, you have reached the end of the result set. The cursor field will be null.

Parameters

ParameterTypeDefaultDescription
limitinteger20Items per page. Min 1, max 100.
cursorstringOpaque cursor from a previous response.
sortstringvariesSort field with optional - prefix for descending (e.g., -filing_date).
include_totalbooleanfalseInclude total_count in the pagination object.

Response Shape

All list endpoints return the same structure. Default response (without include_total):
{
  "object": "list",
  "data": [ ... ],
  "has_more": true,
  "pagination": {
    "cursor": "eyJpZCI6..."
  },
  "request_id": "req_abc123"
}
With ?include_total=true:
{
  "object": "list",
  "data": [ ... ],
  "has_more": true,
  "pagination": {
    "cursor": "eyJpZCI6...",
    "total_count": 12847,
    "total_count_approximate": false
  },
  "request_id": "req_abc123"
}
Key details:
  • has_more is always at the top level (not inside pagination).
  • pagination.cursor is null when has_more is false.
  • pagination.total_count is only present when include_total=true is passed as a query parameter. It is omitted by default.
  • pagination.total_count_approximate is emitted alongside total_count whenever the total is present. It is false when the count is exact and true when the count was capped (for example, OpenSearch caps total hits at 10,000 for deep searches). Treat any exact count as a hard number and any approximate count as “at least this many”.

Cursor Stability

Cursors encode a position in the result set, not an offset. This means: Records inserted after your cursor was created will not appear in your current pagination session. You will not see duplicates or gaps. Records deleted after your cursor was created are silently skipped. Your page may contain fewer items than the requested limit in rare cases. Concurrent updates do not affect cursor validity. Even if the underlying data changes, your cursor continues from where it left off.
Cursors are scoped to a specific query, sort order, and filter combination. Changing any of these parameters invalidates the cursor — start a fresh pagination session instead.

Cursor Expiration

Cursors expire after 24 hours. If you pass an expired cursor, you receive a cursor_expired error:
{
  "error": {
    "type": "cursor_expired",
    "title": "Cursor expired",
    "status": 400,
    "detail": "The pagination cursor has expired. Start a new query.",
    "retryable": false,
    "retry_after": null
  },
  "request_id": "req_abc123"
}
For long-running syncs, checkpoint the cursor and resume within the 24-hour window. If the cursor expires, restart from the beginning.
Cursors are opaque strings. Do not parse, decode, or construct them. Their internal format may change without notice.

Sorting

You can sort results using the sort parameter. The cursor encodes the sort position, so you must use the same sort value for every page in a session. Supported sort fields vary by endpoint:
EndpointSupported Sort FieldsDefault
GET and POST /v1/trademarksfiling_date, registration_date, expiry_date, renewal_due_date, updated_at, publication_date, termination_date, office_code, jurisdiction_codeRelevance when q is present; unordered (fast) when no q
GET /v1/ownersname, updated_atname
Sort direction is specified with a - prefix for descending (no prefix = ascending):
GET /v1/trademarks?sort=-filing_date&limit=50
GET /v1/trademarks?sort=filing_date&limit=50    # ascending
When a text query (q) is provided, the default sort is by relevance. When only filters are used (no q), results are returned in index order (fast, unordered). Providing an explicit sort overrides the default in both cases.

Total Count

By default, the total_count is not included in the response. To include it on any list endpoint, pass include_total=true:
GET /v1/trademarks?q=ACME&limit=50&include_total=true
GET /v1/owners?q=acme&limit=50&include_total=true
GET /v1/attorneys?q=smith&limit=50&include_total=true
GET /v1/firms?q=acme&limit=50&include_total=true
GET /v1/jurisdictions?include_total=true
{
  "object": "list",
  "data": [ ... ],
  "has_more": true,
  "pagination": {
    "cursor": "eyJpZCI6...",
    "total_count": 12847,
    "total_count_approximate": false
  }
}
The shape is the same for every list endpoint:
  • pagination.total_count — the count itself (integer, only present when include_total=true).
  • pagination.total_count_approximatefalse when the count is exact; true when the underlying engine capped the count (OpenSearch caps at 10,000 for deep search queries).
GET /v1/trademarks and POST /v1/trademarks currently also echo total_results, total_count_exact, and total_count_approximate under search_meta for backward compatibility with earlier SDK releases. These fields are deprecated and will be removed in v1.1 — read pagination.total_count and pagination.total_count_approximate going forward.
Computing the total count requires a separate database or OpenSearch query and can be expensive on large datasets. Only request it when you need to display a progress indicator or total count in your UI. Avoid including it on every page request.

SDK Support

The TypeScript SDK wraps pagination with async iterators, toArray(), and manual page control. See SDK Pagination for usage patterns.