Pagination
limit, offset and total — and how to walk a list without missing rows.
Every list endpoint paginates the same way, with limit and offset in the query string.
curl "https://api.trama.so/v1/customers?limit=50&offset=100" \
-H "Authorization: Bearer $TRAMA_API_KEY"| Parameter | Default | Maximum |
|---|---|---|
limit | 25 | 100 |
offset | 0 | — |
Asking for more than 100 isn't silently clamped — it's a 400 with VALIDATION_FAILED. That's deliberate: a request that silently returns fewer rows than you asked for is how integrations end up dropping data without noticing.
The response shape
{
"data": [ /* … */ ],
"pagination": { "limit": 50, "offset": 100, "total": 1284 }
}total is the count for the filters you sent, not for the whole organization. It's what lets you know whether there's another page to ask for, without having to request one and get an empty array.
async function fetchAll(path) {
const items = [];
let offset = 0;
for (;;) {
const res = await fetch(`https://api.trama.so${path}?limit=100&offset=${offset}`, {
headers: { Authorization: `Bearer ${process.env.TRAMA_API_KEY}` },
});
const { data, pagination } = await res.json();
items.push(...data);
offset += pagination.limit;
if (offset >= pagination.total) return items;
}
}Offsets shift under you. A list is a live query, not a snapshot: if rows are created or reordered while you're walking it — and in a catalogue, editing a product reorders it — a row can move between pages and you'll see it twice or not at all.
If you're syncing rather than browsing, don't rely on having walked every page cleanly. Key on the resource id, and re-run the walk periodically instead of assuming one pass was complete.
There's no cursor pagination, and that's a decision rather than an omission — a stable cursor needs an order we can't promise on every list today. If your catalogue or customer book grows to where this hurts, tell us: it's the kind of thing that gets versioned, not patched.