Skip to content

API Pagination Standard

Version: 1.0 Status: Draft Last Updated: 2026-04-03

Purpose

Standardise pagination across all Simpaisa /v3/ APIs. Cursor-based pagination replaces offset-based pagination for better performance on large, frequently changing datasets (270M+ transactions and growing).

Scope

Applies to all /v3/ API endpoints returning collections: transaction lists, bank lists, merchant lists, audit logs, and any future list endpoints across Pay-Ins, Pay-Outs, Remittances, and Cards.

Legacy endpoints (pre-v3) retain offset-based pagination for backward compatibility.

Query Parameters

Parameter Type Default Description
limit int 20 Items per page. Min 1, max 100.
cursor string Opaque cursor from previous response. Omit for first page.
  • First page: omit cursor entirely.
  • Next page: pass the cursor value from the previous response's pagination object.
  • Clients MUST treat cursor values as opaque strings. Internal encoding may change without notice.

Response Envelope

All collection endpoints return this structure:

{
  "data": [ ... ],
  "pagination": {
    "cursor": "eyJjIjoiMjAyNi0wMy0xNVQxMDozMDowMFoiLCJpIjoiYWJjMTIzIn0=",
    "hasMore": true,
    "total": 1234
  }
}

Fields

Field Type Required Description
data array Yes Array of resource objects.
pagination.cursor string Yes Cursor for the next page. null when no more results.
pagination.hasMore boolean Yes true if more results exist beyond this page.
pagination.total integer No Total matching count. Omit if result set exceeds 10,000 — the query cost is not justified.

Empty Results

When no results match the query:

{
  "data": [],
  "pagination": {
    "cursor": null,
    "hasMore": false
  }
}

Ordering

All paginated endpoints use a stable, deterministic sort order:

  1. Primary: created_at DESC (newest first).
  2. Tiebreaker: id ASC — guarantees deterministic ordering when timestamps collide.

This combination ensures no items are skipped or duplicated across pages, even during concurrent writes.

Optional. Services SHOULD include a Link header with rel="next" for discoverability:

Link: </v3/transactions?cursor=eyJ...&limit=20>; rel="next"

Omit when hasMore is false.

Implementation Notes

  • Cursor encoding: base64-encode a JSON object containing the last record's created_at and id. This keeps cursors opaque to clients while remaining debuggable internally.
  • Expired cursors: if a cursor references a deleted or archived record, return 400 Bad Request with error code INVALID_CURSOR.
  • Limit validation: values outside 1–100 return 400 Bad Request. Do not silently clamp.
  • Filtering: cursors are scoped to the original query filters. Changing filters with an existing cursor returns 400 Bad Request.

Legacy Endpoint Compatibility

Pre-v3 endpoints continue to use offset-based pagination (page, per_page) unchanged. No migration is required for existing integrations. New endpoints MUST use cursor-based pagination exclusively.

KrakenD Gateway Configuration

KrakenD proxy endpoints should pass cursor and limit query parameters through to backend services without modification. Do not aggregate or manipulate pagination at the gateway layer.

References