Skip to main content

CursorProof: linked pages with a verification shield

CursorProof

PyPI Python CI License

CursorProof is a command-line tool for checking cursor pagination in REST APIs. It traverses an endpoint at several page sizes, checks duplicates, ordering and termination, and saves a trace that can be replayed without the API.

An API can return 200 OK on every page while skipping records or repeating a page. An independent oracle, such as an unpaginated endpoint or database query, lets CursorProof compare the returned stream with the expected records.

Install

Requires Python 3.10 or newer.

pipx install cursorproof
# or
python -m pip install cursorproof

Check your API

Save a configuration as cursorproof.yml. This example expects results and next_cursor in each response and records identified by id:

url: http://localhost:8000/api/orders
response:
  items: $.results
  next_cursor: $.next_cursor
  id: $.id
ordering:
  - field: id
    type: number
limits: [10, 50, 100]

Validate the configuration, run the checks, and replay the saved trace:

cursorproof check cursorproof.yml
cursorproof run cursorproof.yml
cursorproof replay cursorproof-repro.json

Token pagination is the default: the next request sends the returned token in the cursor query parameter and the selected page size in limit. For APIs that return a continuation URL, including Django REST framework, add:

pagination:
  mode: url
  cursor_param: cursor
  limit_param: page_size
response:
  items: $.results
  next_cursor: $.next
  id: $.id

URL mode requires CursorProof 0.2.0 or newer. To install the version from a checkout, run uv tool install . from the repository root.

URL mode follows server-provided links after checking the origin, endpoint, scope parameters and page size. It does not follow redirects. The user guide explains authentication, response paths, ordering, oracles and the continuation URL contract.

Try the local demo

Clone the repository and run these commands from its root directory.

Install from the checkout with uv:

uv sync --locked

To install the checkout as an isolated CLI tool instead, run uv tool install ..

Start the demo API in one terminal:

uv run python examples/demo_api.py

In another terminal, run the broken example:

uv run cursorproof run examples/broken.yml

Replay its saved trace offline with:

uv run cursorproof replay cursorproof-repro.json

The demo has 40 records with repeated sort values. /broken continues using only the timestamp and skips records at page boundaries despite successful responses. CursorProof reports CP003 MISSING_ITEMS and differences between limits. The /correct endpoint also uses the unique ID; try it with:

uv run cursorproof run examples/correct.yml --format json --repro correct-repro.json

Understand the result

  • Exit 0: all executed checks passed.
  • Exit 1: CursorProof found a pagination invariant violation.
  • Exit 2: configuration or execution failed, or a traversal could not finish.

Use --format json for a machine-readable report and --repro PATH to choose the trace file. A passing run describes the tested data and configuration. Without an oracle, it cannot establish completeness: every tested page size could omit the same records. See the guide for oracle configuration, finding codes and trace privacy.

Django REST framework example

The DRF configuration uses standard CursorPagination links. The Django REST framework example shows how a large group of equal sort values can repeat a page. It includes a local HTTP/SQLite setup, a unique-ordering control and traces you can replay without Django.

EmDash plugin storage example

The EmDash example is a historical check against a TypeScript target. It runs EmDash's real PluginStorageRepository before and after the fix for issue #2691 behind a thin test wrapper, compares the stream with an independent database oracle and saves traces you can replay without Node.js. It is not a claim about current EmDash.

Additional checks

CursorProof also supports snapshot consistency, live keyset checks, mutation hooks, cardinality boundary tests and cursor binding probes. Mutation hooks and boundary tests execute commands supplied in your configuration and can change data. Use controlled test environments and read the consistency and mutation documentation before enabling them. URL mode does not support boundary tests or cursor binding probes.

Run cursorproof --help or cursorproof COMMAND --help for command options.

Development

uv sync --locked
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv build

The analyzer checks recorded observations without network I/O. The runner makes requests, and the CLI displays reports. Tests use HTTPX transports, Hypothesis and the local demo API.

The optional locked integration group adds Django/DRF for tests against a real HTTP endpoint. Follow the integration instructions to use a separate environment and the built wheel. CI runs the core checks on Python 3.10 through 3.14. The Django/DRF integration tests require Python 3.12 or newer and run on Python 3.12 through 3.14.

Contributing and support

See Contributing for setup, checks and pull request guidance. Use issues for bugs and feature requests, and follow the code of conduct. Report vulnerabilities privately using the security policy.

Release history is in the changelog. CursorProof is licensed under Apache-2.0.

Metadata

Release files for cursorproof 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cursorproof 0.2.1
File Size Uploaded Provenance Actions
cursorproof-0.2.1.tar.gz 119.0 kB With provenance Details for cursorproof-0.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cursorproof 0.2.1
File Interpreter ABI Platform Actions
cursorproof-0.2.1-py3-none-any.whl Python 3 none any Details for cursorproof-0.2.1-py3-none-any.whl

Total release size: 155.1 kB

Release files / cursorproof-0.2.1.tar.gz

Download URL cursorproof-0.2.1.tar.gz
Size 119.0 kB
Tags Source
SHA-256 checksum
How to use checksums
1e09283e52b4e3b3c4b1715850d96d43fabd47a979eceda0a7c7d674de4cdc25
BLAKE2b-256 checksum
How to use checksums
62b5336f5842cf1686b57bfaa0f590b2b10befe43cec7f9ef7016959102b3e4b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / cursorproof-0.2.1-py3-none-any.whl

Download URL cursorproof-0.2.1-py3-none-any.whl
Size 36.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8b80c7b0e4e2f19c73687a98c444916cb488bf0818b5c4a8ada125757e2ffdf4
BLAKE2b-256 checksum
How to use checksums
d9452791b98d6c95a2ebf721af728424e0aba2b987464ab95f9ddc79afda358d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Security for cursorproof 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for cursorproof-0.2.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page