All list endpoints return at most limit rows and a meta.next_cursor
value. Pass that cursor on the next request to get the next page.
Why cursors instead of offset
Many APIs paginate with offset and limit — "skip 200 rows, give me
the next 25." That model has two problems for the workloads this API
serves:
- Rows shift under you. If a field crew submits three records between your page-2 and page-3 requests, every row moves down by three — an offset walk sees duplicates or silently skips rows. For a sync job whose purpose is "give me every record," skipped rows are data loss.
- Deep pages get slow. To serve
offset=5000the database fetches and discards 5,000 rows first. The cost grows with depth.
A cursor avoids both. Think of next_cursor as a bookmark: it
marks the exact last row you received, and the next request resumes
strictly after that row — not at a position that other writes can
shift. Concurrent submissions cannot make you skip or repeat rows, and
page 200 costs the database the same as page 1.
The trade: there is no "jump to page 7" and no total page count. For exports, syncs, and reports that walk the list front to back, neither is missed.
Your only job as a caller: take meta.next_cursor from each response
and pass it back as cursor on the next request, until it comes back
null.
Request
GET /api/v1/organizations/{org}/forms/{form}/records?limit=25&cursor=<opaque>
| Query param | Default | Max | Notes |
|---|---|---|---|
limit | 25 | 100 | Positive integer |
cursor | (none) | — | Opaque; copy verbatim from the previous response |
sort | endpoint default | — | created_at, -created_at, name, -name, etc. The minus prefix sorts descending |
Response
{
"data": [ /* up to `limit` rows */ ],
"meta": {
"next_cursor": "eyJpZCI6Ii4uLiJ9",
"limit": 25
},
"request_id": "req_..."
}When next_cursor is null, you've reached the end.
Cursor opacity
The cursor is base64url-encoded internal state. Treat it as a black box. We may change the encoding without warning. Do not parse, log, or persist cursors beyond the immediate paging session.
Filters
Filters are resource-specific query params and combine with pagination. For example:
GET /api/v1/organizations/{org}/forms/{form}/records
?created_after=2026-04-01T00:00:00Z
&created_before=2026-05-01T00:00:00Z
&submitted_by=...
&limit=100
See each resource's reference page for the full filter list.
Walking the whole list
URL="https://fieldscroll.app/api/v1/organizations/$ORG/forms/$FORM/records?limit=100"
while [ -n "$URL" ]; do
RESP=$(curl -s -H "Authorization: Bearer $FS_KEY" "$URL")
echo "$RESP" | jq '.data[]'
CURSOR=$(echo "$RESP" | jq -r '.meta.next_cursor // empty')
if [ -z "$CURSOR" ]; then break; fi
URL="https://fieldscroll.app/api/v1/organizations/$ORG/forms/$FORM/records?limit=100&cursor=$CURSOR"
done