DevelopersDocsConcepts

Pagination

How lists work. The list object, limit up to 100, the cursor parameter, next_cursor, and a loop that reads every page in TypeScript and Python.

Every endpoint that returns many things returns the same shape.

{
  "object": "list",
  "data": [ { "object": "order", "id": "ord_4821" } ],
  "has_more": true,
  "next_cursor": "bzoyMA"
}
  • data is the page.
  • has_more says whether there is another page.
  • next_cursor is what to send to get it. It is null on the last page.

#Ask for a page

Parameter Does
limit How many items. 1 to 100. Default 20.
cursor The next_cursor of the previous page. Leave it out for the first page.
curl "https://adv.heliosynctech.com/api/v1/cases/SBXX010000032026/orders?limit=50" -H "Authorization: Bearer $AOS_KEY"
curl "https://adv.heliosynctech.com/api/v1/cases/SBXX010000032026/orders?limit=50&cursor=bzoyMA" -H "Authorization: Bearer $AOS_KEY"

A cursor is an opaque string: send back exactly the next_cursor you were given. Do not build or edit one, and do not keep one for days: it points at a place in the list, and the list can change. A cursor that is not from that list gives 400 invalid_cursor. Cursors look different from list to list (an offset, a watch id like wch_41, a report id, an event id, a request id); that is why they are not for you to make.

The event log, GET /events, also takes after, an event id, so a reader can resume from the last event it handled; its next_cursor is the id of the last event on the page and works as after too. It lists oldest first; every other list is newest first.

#Read every page

async function* all(path: string, params: Record<string, string> = {}) {
  let cursor: string | null = null;
  do {
    const qs = new URLSearchParams({ limit: "100", ...params, ...(cursor ? { cursor } : {}) });
    const res = await fetch(`${process.env.AOS_API_BASE}${path}?${qs}`, {
      headers: { Authorization: `Bearer ${process.env.AOS_KEY}` },
    });
    const page = await res.json();
    if (!res.ok) throw new Error(`${page.error.code}: ${page.error.message}`);
    yield* page.data;
    cursor = page.has_more ? page.next_cursor : null;
  } while (cursor);
}

for await (const order of all("/cases/SBXX010000032026/orders")) console.log(order.id);
import os, requests

def all_items(path, **params):
    cursor = None
    while True:
        q = {"limit": 100, **params, **({"cursor": cursor} if cursor else {})}
        res = requests.get(
            os.environ["AOS_API_BASE"] + path,
            params=q,
            headers={"Authorization": f"Bearer {os.environ['AOS_KEY']}"},
            timeout=30,
        )
        page = res.json()
        if not res.ok:
            raise RuntimeError(page["error"]["message"])
        yield from page["data"]
        if not page["has_more"]:
            return
        cursor = page["next_cursor"]

for order in all_items("/cases/SBXX010000032026/orders"):
    print(order["id"])

#Cost

A page is billed as the endpoint says. A free list (GET /cases/{cnr}/orders, GET /watches, GET /events) is free on every page. A priced search (GET /cases, GET /judgments) is billed per request, not per row, so limit=100 costs the same as limit=20 and is cheaper per row.

#Which endpoints are lists

GET /cases, GET /cases/{cnr}/orders, GET /cause-lists, POST /cause-lists/match, GET /tribunal-orders, GET /courts, GET /case-types, GET /holidays, GET /judgments, the citing list of a citator, GET /limitation/rules, GET /reports, GET /watches, GET /webhook-endpoints, GET /events, GET /usage and the GET /workspace/* lists. All take limit (1 to 100, default 20) and cursor.