API
Pagination
An opaque cursor over a change feed. The cap is deliberate, and a null cursor means caught up, not clear.
GET /v1/exposure is the only paged endpoint.
The three parts
| Part | Where | Notes |
|---|---|---|
since | query parameter | Opaque cursor from the previous response. Omit to start from the beginning of your currently retained window. Max 512 characters. |
limit | query parameter | Page size. Minimum 1, maximum 200, default 100. |
next_cursor | response body | Pass 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.