Enterprise: the API, webhooks and MCP server are live for schools on the Enterprise plan. The owner allows every key and AI connection. Ask about Enterprise

On this page

Guides

Pagination

Lists return data and next_cursor. Continue until next_cursor is null, even when a filtered page is empty.

Cursors

limit defaults to 50 and accepts 1–200. Cursors are opaque and bound to the connection, route, filters and location access; preserve the same filters on later calls. There is no has_more field on REST pages.

Modern source rows are ordered by updatedAt then document id, ascending. A bounded legacy suffix scans records without a canonical timestamp in document-id order, so that suffix may contain older or newer effective times. Each request scans at most 500 raw source rows; filtering can produce short or empty data with a non-null next_cursor.

Swimmer pages expand source families into child records, with children sorted by id; the cursor can resume inside a family. Waiver pages similarly expand templates into per-swimmer status. These are not frozen snapshots: concurrent school changes can affect later pages.

Walk every page

Node
const records = [];
let cursor = null;
do {
  const query = new URLSearchParams({limit: '200'});
  if (cursor) query.set('cursor', cursor);
  const response = await fetch('https://api.swum.ai/v1/families?' + query, {
    headers: {Authorization: `Bearer ${process.env.SWUM_API_KEY}`},
  });
  if (!response.ok) throw new Error(await response.text());
  const page = await response.json();
  records.push(...page.data);
  cursor = page.next_cursor;
} while (cursor !== null);

Deduplicate by record id and use overlapping update windows if your sync must tolerate concurrent writes. Do not stop because a page contains fewer than limit records.

Keep a copy in sync

updated_since requires an ISO 8601 timestamp with an explicit time zone. It applies to list results using effective update time: a valid stored updatedAt when present; otherwise the latest suitable progress, response, decline, check-in, created or Firestore snapshot update/create time. Family effective times also include resolved location-affinity metadata changes. Expanded swimmer/waiver rows filter their own effective time so a newer child is not hidden by an older parent/template.

Single-object family, swimmer, class and progress reads return the current object; updated_since is validated there but does not suppress that object. Use webhooks plus periodic reconciliation; neither a page nor a single webhook is a complete historical export.

TipPreserve the same updated_since and other filters while walking a cursor. Start a new walk when changing filters or after access changes.