Skip to content
Docs
Get API Access
Browse the Documentation

Pagination

Collections page with an opaque cursor. Follow next_cursor until it is null, with the same query.

How It Works

A paginated collection answers with a page of data and a pagination object:

JSON
{
  "status": { "request_id": "req_7Hk2pQ9xLm", "error_code": 0 },
  "data": [ ... ],
  "pagination": {
    "next_cursor": "eyJ2IjoxLCJvIjoyNX0"
  }
}
  1. Make the first request with the parameters you want and, optionally, a limit.
  2. If next_cursor is a string, repeat the same request with cursor=<next_cursor> added. Keep every other parameter the same.
  3. Stop when next_cursor is null. That page is the last.

A page can hold fewer items than limit without being the last one; only a null cursor ends the traversal.

async function* pages(path, params) {
  let cursor = null;
  do {
    const url = new URL(path, "https://api.cantoncap.cc");
    for (const [key, value] of Object.entries(params)) url.searchParams.set(key, String(value));
    if (cursor) url.searchParams.set("cursor", cursor);

    const response = await fetch(url, {
      headers: { "X-API-Key": process.env.CANTONCAP_API_KEY },
    });
    const body = await response.json();
    if (!response.ok) throw new Error(`${body.error.code}: ${body.error.message}`);

    yield* body.data;
    cursor = body.pagination?.next_cursor ?? null;
  } while (cursor);
}

// Every market Temple supplies, 100 at a time.
for await (const market of pages("/v1/venues/temple/markets", { limit: 100 })) {
  console.log(market.market_id);
}

Rules for Cursors

  • Opaque. A cursor is a token, not a format. Do not build, parse or edit one; its contents can change between releases.
  • Bound to its query. Use a cursor only with the query that returned it. A cursor sent with different filters, or a malformed one, answers 400 invalid_request.
  • Pinned in time. On history endpoints, the cursor pins the range and the data retained before the first page: points recorded later never enter that traversal, so pages never duplicate, skip or reorder. Start a new query to see newer data.
  • Re-checked on every page. Each page applies the current publication rules, so a record withdrawn mid-traversal disappears from the next page.
  • Not for keeping. Do not store cursors across releases: cursors from an earlier release of the candle endpoints answer 400 invalid_request. Restart without the cursor.

Page Size

limit sets the page size. Your plan caps it: asking for more than the plan allows answers 403 quota_exceeded, and leaving limit out when an endpoint's default is above your plan's cap gives you the cap instead. On the history endpoints limit counts points across every requested series together. See Plans and limits.

Not Paginated

Fixed batches answer in one response with no pagination object: /v1/simple/price, /v1/assets/quotes/latest, /v1/assets/info and /v1/activity/markets/latest each take up to 100 IDs. Activity history (/v1/activity/{entity_kind}/{entity_id}) pages by time instead: use the oldest returned as_of as the next time_end.

History Depth
History ranges are checked against your plan's history depth on every page, the continuations included: a range that reaches further back answers 403 quota_exceeded. See Candles and history.
Esc
navigateopenEscclose