Developers

Pagination

Cursor iteration and updated_since for incremental sync.

Every list endpoint (GET /customers, GET /invoices, …) returns a cursor-paginated envelope:

{
  "object": "list",
  "data": [],
  "has_more": true,
  "next_cursor": "eyJpZCI6…"
}

Reading a full list

Loop until has_more is false:

let cursor = null
const all = []
do {
  const url = new URL('https://invoia.io/api/v1/customers')
  if (cursor) url.searchParams.set('cursor', cursor)
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.INVOIA_API_KEY}` },
  })
  const page = await res.json()
  all.push(...page.data)
  cursor = page.has_more ? page.next_cursor : null
} while (cursor)

Cursors are opaque and stable across mutations — a new item inserted mid-iteration will not throw off your loop or cause duplicates.

Incremental sync with updated_since

Pass updated_since=<ISO timestamp> to fetch only what changed after a given point:

curl "https://invoia.io/api/v1/customers?updated_since=2026-07-01T00:00:00Z" \
  -H "Authorization: Bearer $INVOIA_API_KEY"

Persist the maximum updated_at you observed in each page and pass it back on the next poll. Combine with cursor if a page-size crossing is possible: updated_since narrows the working set, cursor walks it.

Common filters

FilterEndpointsMeaning
external_idmostExact match on your ext id
customerinvoices, recognitionFilter by customer ref
statusinvoices, credit notese.g. issued, draft
updated_sinceall listsISO 8601 timestamp

Filters and cursor may be combined freely.

Page size

Default page size is 25; the maximum is 100. Pass limit=<n>:

curl "https://invoia.io/api/v1/customers?limit=100" \
  -H "Authorization: Bearer $INVOIA_API_KEY"