nodestash

Pagination

Navigate results with auto-pagination and async iterators

The SDK provides two ways to paginate: manual pagination with cursors and auto-pagination with async generators.

Manual Pagination

Every list() call returns data and pagination metadata:

interface ListResponse<T> {
  data: T[]
  pagination: {
    next_cursor: string | null
    has_more: boolean
  }
}

Fetch a Single Page

const page = await client.contacts.list({
  limit: 25,
  sort: '-created_at',
})

console.log(page.data)                   // Contact[]
console.log(page.pagination.has_more)    // boolean
console.log(page.pagination.next_cursor) // string | null

Fetch the Next Page

if (page.pagination.has_more && page.pagination.next_cursor) {
  const nextPage = await client.contacts.list({
    limit: 25,
    sort: '-created_at',
    cursor: page.pagination.next_cursor,
  })
}

Loop Through All Pages

const allContacts = []
let cursor: string | undefined

do {
  const result = await client.contacts.list({
    limit: 100,
    cursor,
  })

  allContacts.push(...result.data)
  cursor = result.pagination.next_cursor ?? undefined
} while (cursor)

console.log(`Total: ${allContacts.length} contacts`)

Auto-Pagination

The listAll() method returns an AsyncGenerator that automatically fetches pages as needed:

for await (const contact of client.contacts.listAll()) {
  console.log(contact.email)
}

With Filters

All list parameters (except cursor) work with listAll():

for await (const deal of client.deals.listAll({
  pipeline_id: 'pp_abc123',
  sort: '-value',
  limit: 100,  // page size (not total limit)
})) {
  console.log(`${deal.title}: ${deal.value}`)
}

The limit parameter in listAll() controls the page size (how many items per API call), not the total number of results.

Collect into an Array

const allDeals: Deal[] = []

for await (const deal of client.deals.listAll({
  pipeline_id: 'pp_abc123',
})) {
  allDeals.push(deal)
}

Break Early

You can break out of the loop at any time — no additional API calls will be made:

let count = 0

for await (const contact of client.contacts.listAll({ limit: 100 })) {
  console.log(contact.email)
  count++

  if (count >= 500) {
    break // stops fetching more pages
  }
}

Process in Batches

async function processBatches(batchSize: number) {
  let batch: Contact[] = []

  for await (const contact of client.contacts.listAll({ limit: 100 })) {
    batch.push(contact)

    if (batch.length >= batchSize) {
      await processBatch(batch)
      batch = []
    }
  }

  // Process remaining items
  if (batch.length > 0) {
    await processBatch(batch)
  }
}

Available on All Resources

Auto-pagination works on every resource that supports listing:

// Contacts
for await (const c of client.contacts.listAll()) { ... }

// Companies
for await (const c of client.companies.listAll()) { ... }

// Deals
for await (const d of client.deals.listAll()) { ... }

// Pipelines
for await (const p of client.pipelines.listAll()) { ... }

// Activities
for await (const a of client.activities.listAll()) { ... }

// Custom Field Definitions
for await (const d of client.customFields.definitions.listAll()) { ... }

// API Keys
for await (const k of client.apiKeys.listAll()) { ... }

Performance Tips

  • Use listAll() for simplicity — it handles everything automatically
  • Set limit: 100 for bulk operations to minimize API calls
  • Break early when you only need the first N results
  • Use filters to reduce the total dataset before paginating
  • Pages are fetched lazily — only when the current page is exhausted

On this page