Pagination¶
List endpoints in the User API return results one page at a time using a consistent limit/offset convention and a shared response envelope. Once you know the pattern, it applies to every collection endpoint (meetings, action items, agenda items, calendar events, and more).
Request parameters¶
Control paging with two query parameters:
| Parameter | Type | Default | Maximum | Description |
|---|---|---|---|---|
limit | integer | 20 | 100 | Maximum number of items to return in one page. |
offset | integer | 0 | — | Number of items to skip before collecting the page. |
limitvalues outside1–100are clamped: values above100become100, and non-positive or missing values fall back to the default of20.offsetis zero-based. Anoffsetbeyond the total returns an emptyitemsarray (not an error).
# Second page of 50 meetings (items 51–100)
curl "https://api.contio.ai/v1/meetings?limit=50&offset=50" \
-H "Authorization: Bearer $CONTIO_TOKEN"
Response envelope¶
Every list endpoint wraps results in the same envelope:
{
"items": [
{ "id": "123e4567-e89b-12d3-a456-426614174000", "title": "Weekly Sync" }
],
"total": 100,
"limit": 20,
"offset": 0
}
| Field | Description |
|---|---|
items | The array of resources for this page. |
total | Total number of items available across all pages. |
limit | The page size actually applied (after clamping). |
offset | The offset actually applied for this page. |
Use total together with your limit to know when you have reached the end: you have fetched everything once offset + len(items) >= total.
Iterating through all pages¶
def iterate_all(session, url, headers, page_size=100):
offset = 0
while True:
resp = session.get(
url, headers=headers,
params={"limit": page_size, "offset": offset},
)
resp.raise_for_status()
body = resp.json()
yield from body["items"]
offset += len(body["items"])
if offset >= body["total"] or not body["items"]:
break
async function* iterateAll(url: string, headers: HeadersInit, pageSize = 100) {
let offset = 0;
while (true) {
const resp = await fetch(`${url}?limit=${pageSize}&offset=${offset}`, { headers });
if (!resp.ok) throw new Error(`Request failed: ${resp.status}`);
const body = await resp.json();
yield* body.items;
offset += body.items.length;
if (offset >= body.total || body.items.length === 0) break;
}
}
Use larger pages to conserve rate limit
Requesting the maximum limit of 100 minimizes the number of round trips and helps you stay within your rate limit. Prefer fewer large pages over many small ones.
Ordering and stability
New items can be created while you paginate. If exact, gap-free consistency matters, capture a snapshot of total at the start and be prepared for items shifting between pages. For always-current data, consider User Events (SSE) or Workspace Webhooks instead of repeated polling.
Next steps¶
- Rate Limits — budgets and backoff
- Errors — the standard error envelope
- User Events (SSE) — real-time updates without polling