Get started
Pagination
Page through collections with limit and an opaque cursor.
Four endpoints return collections: ledger entries, invoices, counterparties and milestones. Each returns one page at a time, and you ask for the next page with a cursor.
Parameters
| Parameter | Meaning |
|---|---|
limit | How many items to return: a positive integer. Defaults to 50; a larger value than 200 is capped at 200. 0, a negative number or a non-integer answers 400 invalid_request. |
cursor | The previous response's page.nextCursor, passed back unchanged. |
The page object
A collection's response carries its items in data and describes the page in page. This is the page of the ledger response in the Quickstart:
{"nextCursor":"eyJrIjo4Mn0","hasMore":true,"count":1}countis how many items this response carries.hasMoreistruewhen there is at least one more item after this page. Vestiarion reads one row more thanlimitto know, so it is a fact, not a guess.nextCursoris the cursor for the next page, ornullwhenhasMoreisfalse.
Cursors are opaque
Pass a cursor back exactly as you received it. Do not decode it, build one, or change it: its contents can change without notice. A cursor this endpoint could not have issued, such as a malformed one or a ledger cursor sent to /invoices, answers 400 invalid_request; the API never quietly starts again from the beginning.
A cursor continues the request that produced it. Send it to the same endpoint, with the same filters. To change a filter, start again without a cursor.
Order
- Invoices, counterparties and milestones are newest first, by creation time, with the row id breaking a tie.
- The ledger is oldest first, ascending by
seq, because it is an append-only stream.
The ledger cursor is a watermark
Because the ledger only grows at the end, a ledger cursor marks a position you can come back to:
- Read pages until
hasMoreisfalse. - Store the last non-null
nextCursor, but only after you have processed every entry in the responses you read. - On your next poll, pass the stored cursor. You get every entry after it: the entries of the last page you read, which had no
nextCursorof its own, and then everything appended since. No entry before the cursor is ever replayed. - Skip any entry whose
seqyou have already stored. Keying your copy byseqdoes this for you.
seq rises within a workspace, but it has gaps, so do not use a gap to detect a missing entry. The hash chain proves continuity: each entry's prevHash is the previous entry's hash. The list ledger entries reference has the details.
A paging loop in JavaScript
This reads every payable invoice, one page of 200 at a time. It runs on Node 18 or later, which has fetch:
const BASE = "https://www.vestiarion.xyz/api/v1";
async function listAll(path, params = {}) {
const items = [];
let cursor = null;
do {
const query = new URLSearchParams({ ...params, limit: "200" });
if (cursor) query.set("cursor", cursor);
const response = await fetch(`${BASE}${path}?${query}`, {
headers: { Authorization: `Bearer ${process.env.VESTIARION_API_KEY}` },
});
if (!response.ok) {
// Read the body as text: an error from outside /api/v1 has no JSON error body.
throw new Error(`${response.status}: ${(await response.text()).slice(0, 200)}`);
}
const body = await response.json();
items.push(...body.data);
cursor = body.page.nextCursor;
} while (cursor);
return items;
}
const payables = await listAll("/invoices", { direction: "payable" });
console.log(`${payables.length} payable invoices`);To follow the ledger, keep the last non-null nextCursor after each page instead of collecting the items, and start the next poll from it. Never replace it with the final page's null: a poll without a cursor starts again from the first entry.