OptimoCMSDocs
SDK

Pagination

Cursor-based pagination and async iterators in the OptimoCMS TypeScript SDK.

Pagination

All list endpoints return paginated results with cursor-based pagination. The SDK provides two ways to work with this: manual cursors and async iterators.

How it works

Every paginated response contains a pagination object:

{
  "data": [ ... ],
  "pagination": {
    "total": 47,
    "limit": 20,
    "nextCursor": "eyJpZCI6InBhZ2VfMjAifQ=="
  },
  "meta": { "requestId": "req_abc123", "timestamp": "2026-05-26T12:00:00Z" }
}
FieldDescription
totalTotal number of items
limitItems per page (default 20, max 100)
nextCursorCursor for the next page. null when there is no next page

Method 1 — Manual cursors

Use list() with the cursor parameter to fetch page by page:

import cms from './cms';

// First page
const page1 = await cms.sites.list({ limit: 10 });
console.log(`${page1.data.length} of ${page1.pagination.total} sites`);

// Second page (if available)
if (page1.pagination.nextCursor) {
  const page2 = await cms.sites.list({
    limit: 10,
    cursor: page1.pagination.nextCursor,
  });
  console.log(`Page 2: ${page2.data.length} sites`);
}

Response page 1:

{
  "data": [
    { "id": "site_001", "name": "Site 1", "status": "published" },
    { "id": "site_002", "name": "Site 2", "status": "published" },
    { "id": "site_003", "name": "Site 3", "status": "draft" }
  ],
  "pagination": {
    "total": 47,
    "limit": 10,
    "nextCursor": "eyJpZCI6InNpdGVfMDEwIn0="
  },
  "meta": { "requestId": "req_pg1", "timestamp": "2026-05-26T12:00:00Z" }
}

Response last page:

{
  "data": [
    { "id": "site_045", "name": "Site 45", "status": "published" }
  ],
  "pagination": {
    "total": 47,
    "limit": 10,
    "nextCursor": null
  },
  "meta": { "requestId": "req_pg5", "timestamp": "2026-05-26T12:00:02Z" }
}

Fetch all items with a loop

import type { SiteSummary } from '@optimocms/sdk';

const allSites: SiteSummary[] = [];
let cursor: string | null = null;

do {
  const response = await cms.sites.list({ limit: 100, cursor: cursor ?? undefined });
  allSites.push(...response.data);
  cursor = response.pagination.nextCursor;
} while (cursor);

console.log(`Total: ${allSites.length} sites`);

The SDK provides a listAll() method that automatically iterates through all pages:

// Iterate over ALL sites, regardless of how many pages there are
for await (const site of cms.sites.listAll()) {
  console.log(`${site.name} — ${site.domain}`);
}

The iterator fetches the next page when the current one is exhausted. You don't need to manage cursors yourself.

With filter parameters

// Only published pages
for await (const page of cms.pages.listAll('site_abc123', { status: 'published' })) {
  console.log(`${page.title} (${page.slug})`);
}

Early termination

// Stop after the first 5 results
let count = 0;
for await (const site of cms.sites.listAll()) {
  console.log(site.name);
  if (++count >= 5) break;
}

Collect into an array

// All items in an array
const allPages = [];
for await (const page of cms.pages.listAll('site_abc123')) {
  allPages.push(page);
}
console.log(`${allPages.length} pages found`);

Pagination parameters

ParameterTypeDefaultDescription
limitnumber20Items per page (1–100)
cursorstringCursor from previous response
orderBystringupdatedAtSort field (pages only)
direction'asc' | 'desc''desc'Sort direction (pages only)
statusstringFilter by status

Which endpoints support pagination?

Endpointlist()listAll()
cms.sites.list()YesYes
cms.pages.list(siteId)YesYes
cms.media.list(siteId)YesYes
cms.shop.products.list(siteId)YesYes
cms.shop.orders.list(siteId)YesYes
cms.shop.coupons.list(siteId)YesYes
cms.booking.bookings.list(siteId)YesYes
cms.webhooks.list(siteId)YesYes
cms.webhooks.deliveries(siteId, webhookId)YesYes
cms.forms.submissions(siteId)YesYes
cms.reviews.list(siteId)YesYes
cms.recruitment.jobs.list(siteId)YesYes

Example: bulk update with rate limit respect

import { OptimoCMS, OptimoCMSError } from '@optimocms/sdk';

const cms = new OptimoCMS({
  apiKey: process.env.OPTIMOCMS_API_KEY!,
  maxRetries: 5,
});

async function updateAllPageTitles(siteId: string, prefix: string) {
  let updated = 0;

  for await (const page of cms.pages.listAll(siteId)) {
    try {
      await cms.pages.update(siteId, page.id, {
        title: `${prefix} — ${page.title}`,
      });
      updated++;
      console.log(`[${updated}] ${page.title} updated`);
    } catch (error) {
      if (error instanceof OptimoCMSError && error.status === 429) {
        console.log('Rate limit reached, SDK retry handles this');
        throw error;
      }
      console.error(`Error at ${page.id}: ${error}`);
    }
  }

  console.log(`Done! ${updated} pages updated.`);
}

Try it — curl equivalent:

# First page
curl "https://api.optimocms.com/v1/sites?limit=10" \
  -H "X-Api-Key: your_api_key"

# Next page with cursor
curl "https://api.optimocms.com/v1/sites?limit=10&cursor=eyJpZCI6InNpdGVfMDEwIn0=" \
  -H "X-Api-Key: your_api_key"

Try it — MCP:

Get all pages from site site_abc123.
→ The MCP server automatically iterates through all pages.

Next steps

On this page