Foreshock

API

Pagination

An opaque cursor over a change feed. The cap is deliberate, and a null cursor means caught up, not clear.

Updated
2026-08-24

GET /v1/exposure is the only paged endpoint.

The three parts

PartWhereNotes
sincequery parameterOpaque cursor from the previous response. Omit to start from the beginning of your currently retained window. Max 512 characters.
limitquery parameterPage size. Minimum 1, maximum 200, default 100.
next_cursorresponse bodyPass as since on the next call. null when you are caught up.

The loop

cursor := load_persisted_cursor()          # null on first run
loop:
    response := GET /v1/exposure?since=cursor&limit=100
    if response.status != 200:
        alert(); back off; retry           # do not advance the cursor
    process(response.matches)
    inspect(response.coverage_health)
    if response.next_cursor == null:
        persist(cursor); sleep; continue
    cursor := response.next_cursor
    persist(cursor)

Persist the cursor only after the page has been processed durably. If you persist first and then crash, you have skipped a page and nothing will tell you.

The cursor is opaque

Cursor contents are not structured. Do not parse them, do not construct them, and do not order by them. They are not timestamps, they are not offsets, and their format is not part of the contract.

The cap is deliberate

limit is capped at 200 because this endpoint is a change feed for your own address book, not an export interface. There is no page size that turns it into one, and there is no other endpoint, parameter, or format that dumps enforcement data in a non-terminal stage.

If you find yourself wanting a full dump, what you probably want is the cursor loop running continuously, plus GET /v1/coverage to know whether the quiet is real.

next_cursor: null is not an all-clear

It means you have consumed everything currently available. It says nothing about whether we can currently see. Read coverage_health in the same response before you conclude that nothing is wrong.