NodukanNodukan/ Developers
Introduction

Pagination

How to paginate through lists using Nodukan's limit/offset model — response format, practical examples, and performance tips.

Pagination

All list endpoints in the Nodukan API use a limit/offset pagination model. You request a page of results with limit (page size) and offset (how many to skip), and the response includes a pagination object with the total count.

Query parameters#

ParameterTypeDefaultMaxDescription
limitinteger20100Number of records to return
offsetinteger0—Number of records to skip from the start

Response format#

Every paginated response wraps the list in data and includes a pagination block:

{
  "data": [
    { "id": "lm_01j9x...", "title": "Free Notion Template", "isActive": true },
    { "id": "lm_01j9y...", "title": "Instagram Caption Pack", "isActive": true }
  ],
  "pagination": {
    "total": 47,
    "limit": 20,
    "offset": 0,
    "hasMore": true
  },
  "requestId": "req_01j9xabc..."
}

pagination.hasMore is true when there are more records after the current page (i.e., offset + limit < total).

Iterating through all records#

JavaScript — async iterator#

async function* paginate(path, params = {}) {
  const base = 'https://api.nodukan.com/v1';
  const headers = { Authorization: `Bearer ${process.env.NODUKAN_API_KEY}` };
  let offset = 0;
  const limit = params.limit ?? 100;

  while (true) {
    const url = new URL(`${base}${path}`);
    url.searchParams.set('limit', String(limit));
    url.searchParams.set('offset', String(offset));
    for (const [k, v] of Object.entries(params)) {
      if (k !== 'limit') url.searchParams.set(k, String(v));
    }

    const res = await fetch(url, { headers });
    if (!res.ok) throw new Error(`API error: ${res.status}`);
    const body = await res.json();

    yield* body.data;

    if (!body.pagination.hasMore) break;
    offset += limit;
  }
}

// Usage — iterate all lead magnets
for await (const lm of paginate('/lead-magnets')) {
  console.log(lm.id, lm.title);
}

Python#

from typing import Generator, Any
import os, requests

def paginate(path: str, params: dict = {}) -> Generator[Any, None, None]:
    base = "https://api.nodukan.com/v1"
    headers = {"Authorization": f"Bearer {os.environ['NODUKAN_API_KEY']}"}
    limit = params.pop("limit", 100)
    offset = 0

    while True:
        resp = requests.get(
            f"{base}{path}",
            headers=headers,
            params={"limit": limit, "offset": offset, **params},
        )
        resp.raise_for_status()
        body = resp.json()

        yield from body["data"]

        if not body["pagination"]["hasMore"]:
            break
        offset += limit

# Usage — collect all leads from a specific lead magnet
leads = list(paginate(f"/lead-magnets/{lead_magnet_id}/leads", {"from": "2026-01-01"}))
print(f"Total leads: {len(leads)}")

Filtering alongside pagination#

Most list endpoints accept additional filter parameters that work alongside limit and offset:

# Get active digital lead magnets, 50 at a time
curl "https://api.nodukan.com/v1/lead-magnets?limit=50&offset=0&isActive=true&type=digital" \
  -H "Authorization: Bearer NDK_live_..."

# Get leads captured in May 2026
curl "https://api.nodukan.com/v1/lead-magnets/lm_abc123/leads?limit=100&offset=0&from=2026-05-01&to=2026-05-31" \
  -H "Authorization: Bearer NDK_live_..."

Tips#

  • Use limit=100 when fetching large datasets — fewer round trips reduce latency and rate limit consumption.
  • Sort order is createdAt DESC (newest first) unless the endpoint specifies otherwise.
  • pagination.total is the count of all records matching your filters, not just the current page. Use it to calculate total pages: Math.ceil(total / limit).
  • Stable ordering — results are ordered by a stable key (usually id or createdAt). Safe to resume pagination from a known offset.
Note:

The maximum limit is 100. If you need more than 100 records in a single call, use the async iterator pattern above to fetch them across multiple requests automatically.

Was this helpful?
Edit on GitHub

Last updated