Cursor-based pagination for navigating large result sets
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.
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.
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:
Endpoint
Supported Sort Fields
Default
GET /v1/trademarks
filing_date, updated_at, mark_text
-filing_date
GET /v1/owners
canonical_name, updated_at
canonical_name
POST /v1/trademarks/search
relevance, filing_date, updated_at
-relevance
Sort direction is specified with a - prefix for descending (no prefix = ascending):
Copy
GET /v1/trademarks?sort=-filing_date&limit=50GET /v1/trademarks?sort=filing_date&limit=50 # ascending
For search endpoints, the default sort is -relevance. Changing to a date-based sort still returns all matching results, just in a different order.
Computing the total count requires a separate database 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.
The SDK provides an async iterator for automatic pagination. This is the recommended way to process all records:
Copy
import { Signa } from '@signa-so/sdk';const signa = new Signa({ api_key: 'sig_live_YOUR_KEY' });// Iterate through all trademarks matching a filterfor await (const trademark of signa.trademarks.list({ 'offices[]': ['uspto'], status_stage: 'registered',})) { console.log(trademark.id, trademark.mark_text);}
Under the hood, the iterator fetches pages of 100 items and yields them one at a time. When a page is exhausted, it transparently fetches the next page using the cursor.You can also collect all results into an array (use with caution on large datasets):
for await (const page of signa.trademarks.list({ limit: 50 }).pages()) { console.log(`Page with ${page.data.length} items, has_more: ${page.has_more}`); for (const trademark of page.data) { await processTrademarkRecord(trademark); }}