Pagination
Docs / API reference

Pagination

How to walk a long list. Message list uses keyset pagination — you ask for the next page from the last id you saw, so new mail arriving mid-walk cannot skip or repeat a row.

Keyset cursor

Pass limit (1–100, default 25). When more rows exist, the response includes next_cursor — the id of the last row on the page. Request the next page with ?cursor=<that id>. When next_cursor is null, you are done.

The listing is ordered by queued_at descending and the cursor resumes from that exact position, so paging to the end sees every message once. If the row the cursor points at was erased or has aged out of retention meanwhile, the request answers 422 with type: "cursor_expired" rather than silently restarting at page one — start the listing again without a cursor.

ListCursor
GET /v1/messagesA message UUID. The list is ordered by queued_at, then created_at, then id, and next_cursor is the last message id on the page. A cursor that is not a uuid is a validation 422. A cursor whose message is gone is 422 cursor_expired.
GET /v1/applications, /v1/domains, /v1/templates, /v1/transports, /v1/suppressionsAn integer id. next_cursor is the last row's id, and the next page is rows with a smaller id. These lists are ordered by id descending. None of them answer cursor_expired.
GET /v1/dmarc-reportsAn opaque string. The list is ordered by range_end, then id, both descending, and next_cursor names both for the last row on the page. Pass it back unchanged. A bare integer — the previous cursor — is 422 invalid_cursor. Start again without a cursor.

Worked example

200 · limit=1 · next page exists
{
  "data": [
    {
      "id": "2ee35675-6bae-42be-aa5e-6d4618d0c61d",
      "status": "queued",
      "status_label": "Queued",
      "evidence": "none",
      "supports_feedback": false,
      "application_id": 1,
      "template_id": 1,
      "subject": "Reset password",
      "subject_redacted": false,
      "from_address": "[email protected]",
      "queued_at": "2026-08-06T06:55:48.000000Z",
      "completed_at": null,
      "content_expires_at": null,
      "tags": { "env": "docs" }
    }
  ],
  "next_cursor": "2ee35675-6bae-42be-aa5e-6d4618d0c61d"
}

Next request: GET /api/v1/messages?limit=1&cursor=2ee35675-6bae-42be-aa5e-6d4618d0c61d.