Pagination and live view
List and search operations return pages. Page size defaults to 25 and is at most 100 (limit).
{ "taxonomy_version": "taxonomy_1", "snapshot_at": "2026-10-22T16:00:00Z", "items": [], "next_cursor": "eyJ2IjoxLC..."}Pass next_cursor back as cursor. The final page has next_cursor: null. Keep paging until
next_cursor is null, not until a page comes back empty: a search with effective date bounds can
return an empty page that still carries a cursor. The cursor is an opaque,
signed token. Do not parse or build one.
- A cursor is bound to your account, to the operation, and to the exact query that produced it. Reusing
it with a different query fails with
400 CURSOR_QUERY_MISMATCH. - A cursor lives for 30 minutes from the moment it was issued. After that you get
410 CURSOR_EXPIRED. Restart from the first page. - A cursor that is not valid (altered, truncated, from another account) answers
400 INVALID_REQUEST. - The taxonomy carrier list binds the cursor to
taxonomy_version. If the dictionaries change while you page, restart. - Watcher event history uses
after_sequenceinstead of a cursor. See Watchers.
Search is a live view
Section titled “Search is a live view”VelociFile does not freeze the catalog for your search. Each page is read live, ordered by updated_at
descending and then tracking number ascending, and snapshot_at records when the search began.
This means a filing that changes while you page can move. It can appear on two pages, or on none, if it was updated between page requests. Treat a full paging run as approximate, and:
- Deduplicate by
filing_id. - Compare
latest_version_idwith what you already hold. A new version ID means the filing changed. - Use a watcher to be told about changes instead of repeating full scans. Watchers report exactly these moves.
Keep a paging run short. If your job is slow, expect to restart when a cursor expires.
import os
import httpx
BASE = "https://api.dev.velocifile.ai/v1"headers = {"Authorization": f"Bearer {os.environ['VELOCIFILE_API_KEY']}"}query = {"states": ["TX"], "product_lines": ["commercial_auto"]}
seen: dict[str, str] = {}cursor = Nonepages = 0while pages < 3: # bounded here so the sample stays short body = {"query": query, "limit": 100} if cursor: body["cursor"] = cursor response = httpx.post(f"{BASE}/filings/search", headers=headers, json=body, timeout=30) response.raise_for_status() page = response.json() for filing in page["items"]: seen[filing["filing_id"]] = filing["latest_version_id"] pages += 1 cursor = page["next_cursor"] if cursor is None: breakprint(len(seen), "distinct filings after", pages, "pages")