Mainly

Pagination

One cursor scheme for every list endpoint.

How it works

Every endpoint that returns a list paginates the same way. No exceptions.

Request — two optional query parameters:

ParameterTypeDefaultDescription
limitnumberendpoint-specificMax items to return. Each endpoint documents its default and maximum.
cursorstringOpaque token from a previous response. Omit it for the first page.

Response — every list response has the same envelope:

{
  "items": [ ... ],
  "nextCursor": "eyJzaWciOiI1aDZ4QkVhdUozUEs2U1dDWjFQR2pCdmo4dkRkV0czS3B3QVRHeTFBUkFYRiJ9"
}

When nextCursor is null, you've reached the end.

Example: walking a wallet's history

const base = "https://api.sarg.am/v1/solana";
const headers = { Authorization: `Bearer ${process.env.MAINLY_API_KEY}` };

let cursor: string | null = null;
do {
  const url = new URL(`${base}/wallets/9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM/transactions`);
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("cursor", cursor);

  const page = await fetch(url, { headers }).then((r) => r.json());
  process(page.items);
  cursor = page.nextCursor;
} while (cursor);

Rules

  1. Cursors are opaque. Don't parse, build, or modify them — their format can change without notice.
  2. Cursors expire. A cursor is valid for at least 10 minutes. Long-running exports should tolerate a cursor_expired error by restarting from a recent checkpoint.
  3. Ordering is stable and documented per endpoint. Transaction history is always newest-first.

Why this matters

Raw Solana tooling has four different pagination dialects: signature-based before/until on RPC, page/limit and before/after cursors on asset APIs, plain cursor on compression APIs, and before-signature query parameters on parsed-history APIs — with different caps on each. Mainly hides all of them behind the single scheme above.

On this page