top of page

Paginate customers and applications

Use cursor traversal for REST customers and page traversal for Merchant Sign-Up applications.

Choose the endpoint contract first

The generic pagination guide describes page/per_page and a data array. Current endpoint schemas differ: REST customer listing returns customers and next_cursor, while Merchant Sign-Up listing returns applications and page metadata. Do not create one universal parser based on the overview. Endpoint family Request Response collection Next page REST Customers GET sandbox.nmi.com/api/v5/customers customers Opaque next_cursor passed back as cursor Merchant Sign-Up GET sandbox.signup.nmi.com/api/v1/applications applications Increment page until current_page reaches last_page

Traverse REST customers with a cursor

Use the merchant private API key as the Authorization value, without Bearer. Omit cursor on the first request. per_page defaults to 10 and permits 1–100; cursor has a 512-character maximum. The schema permits date_from/date_to in YYYY-MM-DD format and filters including first_name, last_name, email and account_number (exactly four digits). Preserve filters as you follow the cursor. GET /api/v5/customers?per_page=50 HTTP/1.1 Host: sandbox.nmi.com Authorization: <sandbox-merchant-private-key> Accept: application/json Stop when next_cursor is omitted or null. Treat the returned cursor as opaque; do not decode it, increment it or substitute a page number.

A bounded customer iterator

This server-only example demonstrates the exact response collection and cursor. Supply a limit suitable for your job; it fails rather than silently returning an incomplete export when that bound is reached. Store only the customer information your application needs. export async function* customerPages(privateKey, maxPages = 100) { let cursor; const seen = new Set(); for (let page = 0; page < maxPages; page++) { const url = new URL('https://sandbox.nmi.com/api/v5/customers'); url.searchParams.set('per_page', '50'); if (cursor) url.searchParams.set('cursor', cursor); const res = await fetch(url, { headers: { Authorization: privateKey, Accept: 'application/json' } }); if (!res.ok) throw new Error(`Customer listing HTTP ${res.status}`); const data = await res.json(); if (!Array.isArray(data.customers)) throw new Error('Unexpected customer response'); yield data.customers; if (data.next_cursor == null) return; cursor = data.next_cursor; if (typeof cursor !== 'string' || !cursor || seen.has(cursor)) { throw new Error('Invalid or repeated cursor'); } seen.add(cursor); } throw new Error('Customer page limit reached; export is incomplete'); }

Traverse Merchant Sign-Up applications by page

This is a separate partner API requiring an OAuth access token with applications:read. It is not authenticated with the checkout merchant key. page starts at 1; per_page permits only 10, 20, 50 or 100 and defaults to 10. Optional updated_from/updated_to filters use YYYY-MM-DD; sort_dir is asc or desc on updated_at, default desc. GET /api/v1/applications?page=1&per_page=50&sort_dir=asc HTTP/1.1 Host: sandbox.signup.nmi.com Authorization: Bearer <sandbox-signup-access-token> Accept: application/json Response field Use applications Process the application records current_page / last_page Determine whether another page exists per_page Actual page size total Reported matching item count

Keep exports stable and authorized

Derive the merchant or partner scope from trusted server configuration. Do not let the browser select an arbitrary account credential. Preserve filter and sort settings across a traversal, deduplicate records by identifier when building a sync, and record incomplete runs. Page traversal over changing data may skip or repeat records; use a deliberate synchronization window rather than assuming the entire dataset is frozen. Merchant onboarding Rate limits Customer Vault guide

Join the community and get the latest biz news & tips

Thanks for submitting!

© 2025 Betterpay. All Rights Reserved.

bottom of page