Appearance
Pagination
Tables paginate with offset and limit. There are no cursors.
bash
curl "https://api.app.tightly.io/api/v1/inventory/table?offset=0&limit=100" \
-H "Authorization: Bearer $TIGHTLY_API_KEY"What comes back
json
{
"data": {
"rows": [],
"size": 100,
"offset": 0,
"filtered_max_size": 4120
}
}| Field | What it is |
|---|---|
rows | The page. |
size | How many rows this page carries. |
offset | The offset this page was read at, echoed back. |
filtered_max_size | How many rows the whole filtered set holds. This is the total to walk to. |
Some tables also return filtered_max_unique_size, which is the count after a distinct. Where both are present, walk against the one that matches the grain you asked for.
Walking a whole table
js
async function* everyRow(url, key, pageSize = 100) {
let offset = 0
let total = Infinity
while (offset < total) {
const response = await fetch(`${url}?offset=${offset}&limit=${pageSize}`, {
headers: { Authorization: `Bearer ${key}` },
})
const { data } = await response.json()
total = data.filtered_max_size
yield * data.rows
if (data.rows.length === 0) break
offset += data.rows.length
}
}Two details that matter:
- Advance by
rows.length, not bypageSize. An operation may return fewer rows than asked for, and advancing by the request would skip whatever it held back. - Stop on an empty page as well as on the total. The table is live: rows can be deleted between two pages, and a walk anchored only on the first total can run past the end.
A walk over a table that is changing under you will see the change. If you need a consistent view of a large table, read it in one page where the size allows, or take the export.
The caps
| Cap | Value | What happens at it |
|---|---|---|
limit | 10,000 across the API | A larger limit is refused. |
offset | Below 2,147,483,647 | A larger offset is refused. |
| Export rows | 100,000 | An export that would exceed it is refused, never truncated. |
Per-operation defaults and maxima are stated on each operation in the reference, and they are what a walk should be sized against. Nine reads take the global cap of 10,000 in one page - products, variants, suppliers, supplier contacts, both stocktake tables, the stock ledger, invoices and EDI documents - so a catalogue or a count can be read whole rather than walked. Three cap at 500: the sales order list, the product catalogue's PIM read and the product filters. Everything else that states a maximum caps at 100, and most of those default to a page of 8.
The export cap refuses rather than truncates on purpose: somebody handed 100,000 of 150,000 rows with no warning has wrong data and no way to know it.
Filtering before you paginate
Most tables take filter_args, a JSON array of {key, operation, value}:
bash
curl -G "https://api.app.tightly.io/api/v1/inventory/table" \
-H "Authorization: Bearer $TIGHTLY_API_KEY" \
--data-urlencode 'filter_args=[{"key":"tags","operation":"in","value":["stocked_out","understocked"]}]' \
--data-urlencode 'limit=100'Each table publishes its own allowlist of keys and operations on the operation's schema in the reference, and refuses any key that is not on it. Filtering server-side is nearly always cheaper than walking the table and filtering in your own process, and it spends far less of your rate limit.
Several tables also serve a .../filters operation that returns the values and ranges the table can be filtered on, which is the honest way to build a filter without hard-coding a list that changes.
Sorting
Where an operation supports sorting it takes sort_args: a comma-separated list of columns, each prefixed with - for descending or + (or nothing) for ascending. The columns an operation sorts on are named on the operation.
bash
curl -G "https://api.app.tightly.io/api/v1/organizations/$ORG/purchase-orders" \
-H "Authorization: Bearer $TIGHTLY_API_KEY" \
--data-urlencode 'sort_args=+name,-expected_delivery_date'There is no default order guarantee across pages for an operation that does not name one, so a walk that must be stable should ask for a sort.