Pagination
List transactions page by page with an opaque cursor.
Lists that can grow without bound are paginated with a cursor. Today that's
GET /transactions. Other lists (households, members, invites, a month's groups and items) are
small and come back whole.
Parameters
| Parameter | Meaning |
|---|---|
limit | Page size, 1 to 200. Default 100. |
cursor | Where to continue. Omit it for the first page; then pass the previous response's next_cursor. |
The response has the page's items plus next_cursor. When next_cursor is null (or missing),
you've reached the end. Treat the cursor as an opaque string: don't parse or build it, and don't
reuse it with different filters.
Example
# first page
curl -s "$SR_API/transactions?view=tracked&month=2026-12&limit=50" \
-H "Authorization: Bearer $SR_TOKEN"{
"transactions": [ … 50 transactions, newest first … ],
"next_cursor": "eyJkIjoiMjAyNi0xMi0wMyIsImkiOiIwMUpDWEs2UThXNk01VDdWM04yQjlSNFkxWiJ9",
"untracked_count": 2
}# next page: same filters, plus the cursor
curl -s "$SR_API/transactions?view=tracked&month=2026-12&limit=50&cursor=eyJkIjoi…" \
-H "Authorization: Bearer $SR_TOKEN"A loop that reads everything:
let cursor;
const all = [];
do {
const url = new URL(`${SR_API}/transactions`);
url.search = new URLSearchParams({ view: 'tracked', month: '2026-12', limit: '200' });
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, { headers: { Authorization: `Bearer ${SR_TOKEN}` } });
const page = await res.json();
all.push(...page.transactions);
cursor = page.next_cursor;
} while (cursor);Filters
GET /transactions needs a view: untracked, tracked, or deleted (the three tabs of the
app's Transactions panel). month limits tracked to one budget month, and q searches by
merchant (a case-insensitive substring) or by an exact amount such as 54.20. Results are newest
first by date.