Paginação e filtros

Para: Dev — Conferido em 2026-10-18.

Toda listagem da API devolve {"data": [...], "next_cursor": ...}. Esta página mostra, endpoint por endpoint, o nome do parâmetro de cursor, em que ordem os itens saem, quais filtros existem e como percorrer uma lista inteira sem pular nem repetir itens.

O que é

A paginação é por cursor, nunca por número de página. Cada resposta traz até limit itens e um next_cursor:

  • next_cursor com valor: existe pelo menos mais um item; mande esse valor no parâmetro de cursor da próxima chamada;
  • next_cursor igual a null: acabou. A API só devolve cursor quando há, de fato, mais um item, então não existe "última página vazia".
Parâmetro Regra
limit Inteiro de 1 a 200. Padrão 50. Fora da faixa, 400 validation_failed.
cursor Texto devolvido em next_cursor. O nome do parâmetro muda por endpoint (tabela abaixo). Cursor inválido: 400 validation_failed com reason: format.

Quando usar

  • Recuperar um POST perdido: GET /deposits?external_id=… ou GET /withdrawals?external_id=… (uma página basta).
  • Varrer um período: updated_since com limit=200, para conciliação ou para reconstruir um estado perdido.
  • Seguir o que aconteceu: GET /events com o cursor after, que é a ferramenta de recuperação de webhooks (Recuperação por GET /events).
  • Estado de rotina de um objeto: não liste. Use GET /deposits/{id} ou GET /withdrawals/{id}, que têm balde de limite maior (Limites e quotas).

Como funciona

Cursor, ordem e filtros por endpoint

Endpoint Parâmetro do cursor O que é o cursor Ordem dos itens Filtros
GET /deposits cursor o id (dep_…) do último item recebido por id, que acompanha a ordem de criação external_id, player_ref, status, updated_since, limit
GET /withdrawals cursor texto opaco por data de criação e, no empate, por id external_id, player_ref, status, updated_since, limit
GET /events after o id (evt_…) do último evento recebido por criação do evento object_id, limit
GET /reconciliation cursor texto opaco por object_id e, no empate, por kind date e currency (obrigatórios), limit
GET /receipts cursor nunca devolvido sempre vazio status, updated_since, limit

Pontos de atenção:

  • Não ordena por updated_at. GET /deposits sai em ordem de criação, e um depósito atualizado agora continua na posição em que nasceu. Para achar o que mudou, filtre por updated_since.
  • Cursor opaco é opaco. O cursor de /withdrawals e o de /reconciliation são texto que só o Dracma interpreta: não construa, não edite, não ordene e não guarde por mais tempo que a varredura. O formato pode mudar sem aviso. O cursor de /deposits e /events é um id que você já viu; mesmo assim, use sempre o valor de next_cursor.
  • Cursor serve só para o mesmo conjunto de filtros. Mudou um filtro no meio da varredura: recomece sem cursor.
  • GET /events com after de outra operação ou que não existe volta 400 com reason: unknown (a API não confirma se o evento existe em outra operação).
  • GET /receipts devolve sempre {"data": [], "next_cursor": null} e valida os parâmetros; qualquer cursor enviado volta 400 (reason: format), porque a API nunca emite cursor de recebimento (Rotas e campos reservados).
  • GET /reconciliation devolve os itens do dia em páginas; os totais, o digest e a assinatura (cabeçalho Dracma-Signature) vêm na última página, a que tem next_cursor null (Reconciliação).

updated_since

updated_since é um instante RFC 3339 (2026-10-18T12:00:00Z; aceita fração de segundo) e filtra por updated_at >= updated_since: maior ou igual, inclusive. Por isso o item exatamente na borda reaparece. Para uma varredura incremental:

  1. guarde o maior updated_at que você viu (T) e a version de cada objeto;
  2. na próxima rodada, chame com updated_since=T;
  3. descarte o que você já tem com a mesma version (a version sobe a cada transição);
  4. nunca avance T além do maior updated_at realmente lido.

O mais robusto para acompanhar mudanças é o webhook mais o GET /events; updated_since é para conferência.

Percorrer tudo

cursor = vazio
repita:
    resposta = GET /v1/deposits?updated_since=T&limit=200  (+ &cursor=<cursor> se houver)
    processe(resposta.data)
    se resposta.next_cursor é null: pare
    cursor = resposta.next_cursor
    espere pelo menos 200 ms entre páginas        # a listagem tem balde próprio de 5 req/s

Cada página é uma requisição assinada nova (novo timestamp e nonce). O Retry-After do 429 diz quanto esperar; repita a mesma página, com o mesmo cursor.

Exemplos

Primeira página de saques, e a seguinte. O caminho assinado inclui a query exatamente como enviada (Autenticação HMAC):

GET /v1/withdrawals?status=processing&limit=2
→ 200  {"data":[{...},{...}],"next_cursor":"<texto opaco>"}

GET /v1/withdrawals?status=processing&limit=2&cursor=<texto opaco>
→ 200  {"data":[{...}],"next_cursor":null}

Eventos a partir do último que você processou:

GET /v1/events?after=evt_<último id processado>&limit=100
→ 200  {"data":[ ...eventos... ],"next_cursor":"evt_<id do último desta página>"}

Consulta de um depósito pelo seu external_id (recuperação de POST perdido):

GET /v1/deposits?external_id=dep_0192a3b4-5c6d-7e8f-9a0b-1c2d3e4f5a6b
→ 200  {"data":[{...}],"next_cursor":null}

Erros e casos de borda

Situação Resposta
limit=0, limit=201 ou limit=abc 400 validation_failed (field: limit)
cursor que o Dracma nunca emitiu, ou adulterado 400 validation_failed (field: cursor, reason: format)
status desconhecido 400 validation_failed (field: status)
updated_since fora de RFC 3339 400 validation_failed (field: updated_since, reason: format)
after de GET /events que não existe nesta operação 400 validation_failed (field: after, reason: unknown)
Filtro sem resultado 200 com "data": [] e "next_cursor": null
Muitas páginas por segundo 429 rate_limited; a listagem tem balde próprio de 5 req/s por chave
Objeto criado durante a varredura Pode ou não aparecer, conforme a ordem; a próxima rodada com updated_since o pega

Mudar o limit no meio da varredura é permitido: o cursor não depende dele.

Veja também