Rate Limits, Paginação e Idempotência na API de Assinatura
Uma integração que funciona no ambiente de testes nem sempre sobrevive ao volume de produção. Picos de tráfego esbarram em rate limits, retentativas mal feitas geram cobranças e assinaturas duplicadas, e a listagem de milhares de transações se torna lenta e inconsistente. Este guia técnico reúne os três pilares que separam um protótipo de uma integração robusta com uma API de assinatura digital: rate limits, idempotência e paginação.
Se você já domina os fundamentos descritos no nosso guia da API de assinatura digital e quer levar sua integração para produção, este artigo entra no detalhe operacional que faz a diferença em escala. Vamos cobrir o tratamento de erros 429, o header Retry-After, backoff exponencial com jitter, idempotency keys para reenviar POSTs com segurança e paginação por cursor para varrer grandes coleções.
O conteúdo é agnóstico de linguagem — os exemplos usam cURL e pseudocódigo em JavaScript/Python —, mas se aplica diretamente a quem usa nossos SDKs oficiais ou chamadas REST puras. Para um panorama do tratamento de falhas em geral, vale combinar esta leitura com o artigo sobre códigos de erro e tratamento de falhas na API.
O que são rate limits e por que eles existem
Rate limit é o teto de operações que um cliente pode realizar dentro de uma janela de tempo. Esses limites protegem a plataforma contra abuso, garantem qualidade de serviço para todos os clientes (evitando que um único integrador monopolize a capacidade) e funcionam como uma camada de defesa contra ataques de negação de serviço.
No SignDocs, o mecanismo principal não é um teto de requisições por segundo, e sim cotas de negócio por tenant, com janelas diária e mensal, contadas por tipo de operação — criação de transações e sessões de assinatura têm sua cota; a verificação de PDFs tem cota própria. Os valores são dimensionados no plano sob medida contratado com o time comercial, com possibilidade de franquia adicional (overage) na janela mensal.
Camadas típicas de limite
| Camada | Janela | Como é dimensionada | Observação |
|---|---|---|---|
| Cota de transações/sessões (criação) | Diária e mensal, por tenant | No plano sob medida, conforme o volume contratado | Operações de escrita; proteja retentativas com idempotência |
Cota de verificação de PDF (POST /v1/verify/document) |
Diária e mensal, por tenant | Cota própria (padrão: 500/dia, 5.000/mês) | Cabeçalhos RateLimit-* acompanham as respostas |
| Franquia adicional (overage) | Mensal | Negociada no plano | Amortece picos sazonais sem bloquear a operação |
| Token endpoint (OAuth2) | — | — | Faça cache do bearer token (expira em 15 min); não peça um novo a cada chamada |
Headers de rate limit
APIs bem projetadas expõem o estado da sua cota em headers de resposta, permitindo que você se adapte antes de bater no teto. Os mais comuns são:
RateLimit-Limit: o total permitido na janela atual.RateLimit-Remaining: quanto ainda resta na janela.RateLimit-Reset: segundos até a janela reiniciar.Retry-After: enviado junto com o429, indica quantos segundos esperar antes de tentar de novo.
O SignDocs usa a forma padronizada sem o prefixo X- (draft do IETF adotado pelas APIs modernas). Um cliente bem comportado monitora RateLimit-Remaining e, ao perceber que está chegando perto de zero, reduz o ritmo de envio proativamente em vez de esperar pelo erro.
Tratando o erro 429 com Retry-After e backoff
Quando você ultrapassa o limite, a API responde com 429 Too Many Requests. A resposta correta nunca é repetir a chamada imediatamente — isso só agrava a sobrecarga. O fluxo recomendado é: ler o Retry-After, aguardar pelo menos esse tempo e então retomar com backoff exponencial e jitter.
Anatomia de uma resposta 429
Backoff exponencial com jitter
O backoff exponencial aumenta o intervalo entre tentativas a cada falha consecutiva (1s, 2s, 4s, 8s...). O jitter — um componente aleatório somado ao intervalo — evita que vários clientes que falharam ao mesmo tempo tentem novamente em sincronia, fenômeno conhecido como thundering herd. Quando o 429 traz Retry-After, esse valor é o piso mínimo de espera.
429 e 5xx (502, 503, 504). Erros 4xx como 400 (requisição malformada), 401 (token inválido) e 422 (validação) não devem ser repetidos: o problema está na própria requisição e retentar só desperdiça cota. Esse mapeamento detalhado está no guia de códigos de erro e tratamento de falhas.
Idempotência: reenviando POSTs com segurança
Aqui mora o problema mais sutil de toda integração de pagamento ou assinatura: o que fazer quando um POST dá timeout e você não sabe se ele foi processado? Se você simplesmente reenviar, corre o risco de criar duas transações, dois envelopes ou duas cobranças para o mesmo evento de negócio. Se não reenviar, pode perder a operação. A solução é a idempotência.
Uma requisição é idempotente quando executá-la várias vezes produz o mesmo efeito de executá-la uma única vez. GET e DELETE já são naturalmente idempotentes; POST não é — e é exatamente por isso que precisamos de um mecanismo explícito.
Como funciona a idempotency key
- Seu cliente gera um identificador único — tipicamente um UUID v4 — antes de enviar o
POST. - Esse valor vai no header
X-Idempotency-Keyda requisição. - A API armazena a chave junto com o resultado da primeira execução bem-sucedida.
- Se a mesma chave chegar de novo (uma retentativa), a API não cria um novo recurso: ela devolve a resposta original memorizada.
O ponto crucial é gerar a chave uma vez por operação de negócio e reutilizá-la em todas as retentativas daquela operação. Gerar uma chave nova a cada tentativa anula completamente a proteção.
Exemplo: criando uma signing session de forma idempotente
O endpoint POST /v1/signing-sessions da Assinatura Expressa cria uma sessão de assinatura em uma única chamada. Veja como protegê-lo com uma idempotency key via cURL:
Repare que usamos o host de homologação api-hml.signdocs.com.br (forma com hífen). É o ambiente correto para testar fluxos de retry sem afetar produção — lembrando que as entidades de homologação têm TTL de 7 dias. Para um passo a passo completo em cURL, consulte o guia integrar assinatura digital com cURL/REST.
Respostas possíveis ao reusar uma chave
| Cenário | Resposta da API | O que fazer |
|---|---|---|
| Primeira requisição, bem-sucedida | 201 Created com o novo recurso |
Persistir o ID retornado; pronto |
| Retentativa com a mesma chave e mesmo corpo | 200/201 com o recurso original (memorizado) |
Tratar como sucesso; nenhum recurso novo foi criado |
| Mesma chave, corpo diferente | 409 Conflict — "Idempotency key already used with a different request body" |
Erro de lógica: você reusou uma chave para outra operação. Gere uma chave nova |
Paginação: listando transações em escala
Endpoints de listagem — como GET /v1/transactions ou a consulta de eventos — nunca devolvem todos os registros de uma vez. Eles paginam o resultado. Entender o modelo de paginação correto evita varreduras lentas, inconsistentes e custosas em cota.
Offset vs. cursor
Existem dois modelos predominantes:
- Offset/limit: você pede a página N com
?limit=50&offset=100. Simples, mas degrada em grandes volumes e é instável: se registros forem inseridos durante a varredura, itens podem ser pulados ou repetidos. - Cursor (keyset): cada resposta devolve um ponteiro opaco (no SignDocs,
nextToken) que aponta para onde a próxima página começa. É estável mesmo com inserções concorrentes e tem desempenho constante — é o modelo da API.
| Critério | Offset/limit | Cursor |
|---|---|---|
| Desempenho em grandes volumes | Degrada conforme o offset cresce | Constante, independente da profundidade |
| Consistência com inserções | Baixa: pula ou duplica itens | Alta: o cursor é estável |
| Pular para uma página arbitrária | Possível (ir direto à página 10) | Não: navegação sequencial |
| Recomendação | Apenas listas pequenas/estáveis | Padrão para transações e eventos |
Anatomia de uma resposta paginada por cursor
Para buscar a próxima página, repita a chamada passando o token: GET /v1/transactions?limit=50&nextToken=eyJQSyI6.... Quando a resposta vier sem nextToken, você chegou ao fim da coleção. Os filtros aceitos incluem status, userExternalId, documentGroupId, startDate e endDate.
Loop de paginação completo
O padrão correto para varrer uma coleção inteira é um laço que segue o cursor até esgotá-lo. Veja em Python, já combinando paginação com respeito ao rate limit:
yield/streaming para não carregar a coleção inteira na memória; aplique filtros no servidor (?status=COMPLETED, ?startDate=...) em vez de baixar tudo e filtrar localmente; e trate o nextToken como opaco — não o decodifique nem o construa manualmente, apenas repasse o valor recebido. Os SDKs oficiais oferecem auto-paginação (listAutoPaginate) que encapsula esse laço.
Consistência: o que esperar dos dados retornados
APIs distribuídas frequentemente operam com consistência eventual em algumas leituras. Isso significa que, logo após criar uma transação, uma consulta de listagem pode levar alguns instantes para refleti-la. Entender esse comportamento evita bugs sutis.
- Leitura após escrita: consultar um recurso diretamente pelo seu ID (
GET /v1/transactions/{id}) logo após criá-lo tende a ser consistente. Já varrer a listagem esperando vê-lo imediatamente pode falhar por replicação em andamento. - Ordenação estável: ao paginar por cursor, a ordem é garantida pela chave de ordenação (normalmente data de criação). Não dependa de ordenação implícita em paginação por offset.
- Fonte da verdade para eventos: para acompanhar mudanças de estado em tempo real, prefira webhooks de eventos a polling de listagem. Use a listagem paginada como reconciliação periódica, não como mecanismo primário.
Para entender como cada estado de uma transação se conecta ao próximo, e em que ponto cada leitura se torna confiável, vale revisar o fluxo transacional completo da API, que detalha o ciclo de vida de ponta a ponta.
RateLimit-*, suporte a X-Idempotency-Key em operações de escrita e paginação por nextToken nos endpoints de listagem. Tudo testável no ambiente de homologação gratuito antes de ir para produção. Fale com nossa equipe para dimensionar as cotas do seu caso.
Checklist de integração resiliente
Antes de promover sua integração para produção, valide que ela atende a estes pontos. Se você está começando agora, o quickstart de 5 minutos cobre o caminho inicial; este checklist é o que torna a integração à prova de escala:
- Cache de token: o bearer OAuth2 é reaproveitado e só renovado perto da expiração.
- Retry seletivo: retentativas apenas para
429e5xx, nunca para4xxde validação. - Backoff com jitter: intervalo exponencial com componente aleatório, respeitando
Retry-After. - Idempotency keys: toda escrita (
POST) crítica carrega uma chave única por operação de negócio, reutilizada nas retentativas. - Limite de tentativas: um número máximo de retentativas, com falha controlada (dead-letter/alerta) ao esgotá-las.
- Paginação por cursor: varreduras seguem o
nextTokenaté ele deixar de vir na resposta, com streaming. - Filtros no servidor: use parâmetros de filtro em vez de baixar tudo e filtrar localmente.
- Monitoramento de cota: logue
RateLimit-Remaininge alerte quando ele se aproximar de zero recorrentemente. - Eventos via webhook: status em tempo real por webhook; listagem como reconciliação.
Perguntas Frequentes
O que significa o erro HTTP 429 na API de assinatura?
O código 429 (Too Many Requests) indica que sua aplicação ultrapassou o rate limit permitido em uma janela de tempo. A resposta acompanha o header Retry-After, que informa em quantos segundos você pode tentar novamente, além dos cabeçalhos padronizados RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset. O comportamento correto é parar de enviar requisições, aguardar o intervalo indicado em Retry-After e então retomar com backoff exponencial e jitter, em vez de repetir a chamada imediatamente.
Como uma idempotency key evita assinaturas ou cobranças duplicadas?
Uma idempotency key é um identificador único, gerado pelo cliente (tipicamente um UUID v4), enviado no header X-Idempotency-Key de uma requisição POST. O servidor armazena a chave junto com o resultado da primeira execução. Se a mesma chave chegar novamente — por exemplo, após um timeout em que você não sabe se a transação foi criada — a API retorna a resposta original sem criar um novo recurso. Isso permite reenviar POSTs com segurança, sem o risco de gerar transações, envelopes ou cobranças em duplicidade.
Qual a diferença entre paginação por offset e por cursor?
Na paginação por offset, você pede a página N usando parâmetros como limit e offset (ou page). É simples, mas fica lento e inconsistente em grandes volumes: se novos registros forem inseridos durante a varredura, itens podem ser pulados ou repetidos. Na paginação por cursor, cada resposta devolve um ponteiro opaco (na API SignDocs, o nextToken) que aponta para o ponto exato onde a próxima página começa. O cursor é estável mesmo com inserções concorrentes e tem desempenho constante, sendo a abordagem recomendada para listar transações e eventos em produção.
Por quanto tempo a API guarda uma idempotency key?
No SignDocs, a chave é retida por 24 horas. Durante esse intervalo, repetir uma requisição com a mesma chave e o mesmo corpo retorna o resultado memorizado (e a mesma chave com corpo diferente retorna 409 Conflict). Após a expiração, a chave é descartada e uma nova requisição com aquele mesmo valor seria tratada como inédita. Por isso, idempotency keys são ideais para proteger retentativas de curto prazo (timeouts, falhas de rede), não para deduplicação de longo prazo, que deve ser feita pela lógica de negócio da sua aplicação.
Devo usar backoff fixo ou exponencial ao receber 429 e 5xx?
Use backoff exponencial com jitter. Em backoff fixo, todos os clientes que falharam tendem a tentar novamente no mesmo instante, criando picos sincronizados que sobrecarregam o servidor em recuperação (efeito thundering herd). No backoff exponencial, o intervalo dobra a cada tentativa (1s, 2s, 4s, 8s...) e o jitter — um componente aleatório somado ao intervalo — espalha as retentativas no tempo. Quando a resposta 429 traz Retry-After, respeite esse valor como piso mínimo de espera.
O endpoint público de verificação tem o mesmo rate limit das chamadas autenticadas?
Não. A consulta pública por evidenceId (GET /v1/verify/{evidenceId}) é aberta e protegida contra abuso na borda, enquanto a verificação de PDFs (POST /v1/verify/document) é autenticada e tem cota própria por tenant — por padrão, 500 verificações por dia e 5.000 por mês, com cabeçalhos RateLimit-* na resposta. Para validação programática em alto volume, use o fluxo autenticado e dimensione a cota no plano sob medida.
Construa integrações que aguentam produção
A API SignDocs entrega cotas por plano sob medida, idempotency keys, paginação por cursor e ambiente de homologação gratuito — tudo o que sua integração precisa para escalar com segurança. ICP-Brasil nativo, LGPD-first e suporte em português.
Fale com o time comercial Conheça a plataforma grátis