Pagination & caching

Cursors

List endpoints use cursor-based pagination. The cursor is opaque — the server may change its encoding at any time. Treat it as a round-trip token.

  • ?limit=<n> — page size. Default 50, maximum 200.
  • ?cursor=<opaque> — start position. Omit on the first call.

Response envelope

{
  "data": [ /* ...entities... */ ],
  "pageInfo": {
    "nextCursor": "eyJrIjoiMjAyNi0wNS0wMVQxMDowMDowMC4wMDBaIiwiaSI6IjY1ZjBhZSJ9",
    "hasMore": true
  }
}

Stop calling when hasMore is false or nextCursor is null.

Incremental sync

For polling, combine cursors with ?updatedSince=<iso8601>:

GET /v1/participants?updatedSince=2026-05-15T00:00:00Z

Only participants updated after that instant are returned. Save the max updatedAt you saw and pass it on the next poll.

Caching with ETag

Every read response carries a weak ETag header and Cache-Control: private, max-age=0, must-revalidate. Cache responses locally; revalidate by sending the ETag back in If-None-Match:

GET /v1/participants/65f0ae12ab34cd5678ef9012
If-None-Match: W/"r42"

# When unchanged:
HTTP/1.1 304 Not Modified
ETag: W/"r42"
# (empty body)

For single-entity reads the ETag is the document's revision counter — extremely cheap to compute. For list pages it's a hash of the filter, cursor, page size, and the maximum updatedAt in the returned rows.

Rate limits

Every authenticated response carries three rate-limit headers so you can pace your client without guessing:

  • X-RateLimit-Limit — your account's max requests per minute.
  • X-RateLimit-Remaining — what's left in the current 60-second window.
  • X-RateLimit-Reset — unix-seconds when the window rolls over.

Default limit is 60 req/min per account, shared across every token issued under that account. Exceeding the limit returns 429 Too Many Requests with a Retry-After header naming the seconds until the next window. Ask your administrator to raise the limit if your integration needs higher throughput.