Skip to content

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_sequence instead of a cursor. See Watchers.

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:

  1. Deduplicate by filing_id.
  2. Compare latest_version_id with what you already hold. A new version ID means the filing changed.
  3. 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 = None
pages = 0
while 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:
break
print(len(seen), "distinct filings after", pages, "pages")