Stay in sync
Collect every file that changed since you last looked, instead of polling each file. GET /public/v1/files lists files by their last change, oldest first. A cursor tells Ocost where you stopped. The key needs the scope documents:read.
Ask for the first page
Pass updated_since to start at a time. Leave it out to start from the first file.
curl -sS -G https://api.ocost.io/public/v1/files \
-H "Authorization: Bearer $OCOST_KEY" \
--data-urlencode "updated_since=2026-11-09T08:00:00Z" \
--data-urlencode "limit=100"The answer holds the files, the cursor and whether more follow:
{
"files": [
{
"id": "01a1b08c-6e3a-7562-8f4a-7924722667b1",
"name": "invoice.pdf",
"status": "read",
"external_ref": "account.move,42",
"documents": [
{ "id": "01a1b08c-7aa1-7b52-9d4e-2f1f1c0c4f11", "kind": "invoice", "number": "RE-2026-1187", "pages": [1] }
],
"updated_at": "2026-11-09T08:02:41Z"
}
],
"next_cursor": "MjAyNi0xMS0wOVQwODowMjo0MVo",
"more": false
}A page holds up to limit files, 1 to 100, 50 by default. A file comes once for each change.
Follow the cursor
While more is true, ask again at once with cursor set to next_cursor. When more is false, keep the last next_cursor. Ask with it on your next poll, and you get only what changed since.
def changed_since(session, cursor=None):
"""Every file changed since the cursor kept from the last poll (None: all)."""
files = []
while True:
params = {"limit": 100, **({"cursor": cursor} if cursor else {})}
page = session.get(f"{API}/files", params=params, timeout=15).json()
files += page["files"]
cursor = page["next_cursor"] or cursor
if not page["more"]:
return files, cursor # keep the cursor for the next pollasync function changedSince(cursor) {
const files = [];
for (;;) {
const params = new URLSearchParams({ limit: '100', ...(cursor ? { cursor } : {}) });
const page = await fetch(`${API}/files?${params}`, { headers: auth }).then((r) => r.json());
files.push(...page.files);
cursor = page.next_cursor ?? cursor;
if (!page.more) return { files, cursor }; // keep the cursor for the next poll
}
}Only the finished files
Ask with status to list only files in those statuses. Repeat it for several.
curl -sS -G https://api.ocost.io/public/v1/files \
-H "Authorization: Bearer $OCOST_KEY" \
--data-urlencode "cursor=$CURSOR" \
--data-urlencode "status=read" --data-urlencode "status=not_read" \
--data-urlencode "status=failed"status filters after paging. A page can therefore hold fewer files than limit, or none. Follow more, not the count.
Then handle each file by its status. Statuses and reasons says what each one means.