Skip to main content

Pagination

Reviewed public list endpoints in the Zentra API return paginated results to keep response times predictable and payloads bounded, but the query style can vary by resource.

How It Works

Transfers and virtual accounts currently document page plus limit:
GET /api/v1/transfers?page=2&limit=50
Cards currently document limit plus offset:
GET /api/v1/cards?limit=50&offset=50

Parameters

ParameterTypeDefaultMaxDescription
pageinteger1-Page number for resources that expose page-based pagination
limitinteger20100Results per page
offsetinteger0-Record offset for resources that expose offset-based pagination

Response Format

Paginated responses include a pagination object with metadata:
{
  "success": true,
  "data": [
    {
      "id": "trn_123",
      "amount_minor": 10000,
      "status": "completed"
    }
  ],
  "pagination": {
    "page": 2,
    "limit": 50,
    "total": 243,
    "pages": 5,
    "has_more": true
  }
}

Pagination Object

FieldTypeDescription
pageintegerCurrent page number
limitintegerResults per page
totalintegerTotal number of items
pagesintegerTotal number of pages
has_morebooleanWhether there are more pages

Examples

Fetching First Transfer Page

const transfers = await client.transfers.list({
  page: 1,
  limit: 20
});

console.log(`Page ${transfers.pagination.page} of ${transfers.pagination.pages}`);
console.log(`Total: ${transfers.pagination.total} transfers`);

Fetching All Transfer Pages

async function fetchAllTransfers() {
  const allTransfers = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await client.transfers.list({
      page,
      limit: 100
    });

    allTransfers.push(...response.data);
    hasMore = response.pagination.has_more;
    page++;
  }

  return allTransfers;
}

const all = await fetchAllTransfers();
console.log(`Fetched ${all.length} transfers`);

Filtering with Pagination

Combine pagination with filters for more specific queries:
GET /api/v1/transfers?status=successful&page=2&limit=50
GET /api/v1/transfers?start_date=2024-01-01&end_date=2024-01-31&page=1&limit=100
const completedTransfers = await client.transfers.list({
  status: 'completed',
  start_date: '2024-01-01',
  end_date: '2024-01-31',
  page: 1,
  limit: 50
});

Sorting

Sorting support varies by resource. For example, reviewed virtual account listing supports:
GET /api/v1/virtual-accounts?sort=-created_at
GET /api/v1/virtual-accounts?sort=account_number
GET /api/v1/virtual-accounts?sort=-total_received_minor
const accounts = await client.virtualAccounts.list({
  sort: '-created_at',
  limit: 50
});

const highestInbound = await client.virtualAccounts.list({
  sort: '-total_received_minor',
  limit: 50
});

Performance Tips

When fetching multiple pages, use limit=100 to minimize the number of requests.
Apply filters to reduce the dataset before paginating.
Cache paginated results when possible to avoid repeated API calls.
Instead of polling for new data, use webhooks for real-time updates.
Fetching very high page numbers can be slow. Prefer date-range filters or incremental sync patterns where supported.

Cursor-Based Pagination

Cursor-based pagination is not currently part of the reviewed public gateway contract. If Zentra introduces it later, it will be documented as a separate reviewed route shape instead of being implied by the current page-and-limit contract.

Reviewed List Endpoints

The following reviewed public endpoints currently document paginated list behavior:
EndpointResourceQuery Style
GET /api/v1/transfersTransferspage, limit
GET /api/v1/virtual-accountsVirtual Accountspage, limit, optional offset
GET /api/v1/cardsCardslimit, offset
GET /api/v1/identity/customersIdentity Customerstenant-specific contract details may narrow the exact filter set

Common Patterns

Infinite Scroll

let page = 1;
let isLoading = false;

async function loadMore() {
  if (isLoading) return;

  isLoading = true;
  const response = await client.transfers.list({ page, limit: 20 });

  appendToList(response.data);

  if (response.pagination.has_more) {
    page++;
  } else {
    hideLoadMoreButton();
  }

  isLoading = false;
}

window.addEventListener('scroll', () => {
  if (isNearBottom()) {
    loadMore();
  }
});

Pagination Controls

function PaginationControls({ pagination, onPageChange }) {
  const { page, pages, has_more } = pagination;

  return (
    <div>
      <button
        disabled={page === 1}
        onClick={() => onPageChange(page - 1)}
      >
        Previous
      </button>

      <span>Page {page} of {pages}</span>

      <button
        disabled={!has_more}
        onClick={() => onPageChange(page + 1)}
      >
        Next
      </button>
    </div>
  );
}