Pular para o conteúdo principal

Rate Limits

A API aplica limites nas rotas de integração /v1/* do originador, para proteger contra abuso. São dois limites independentes e ambos valem ao mesmo tempo: quem estourar primeiro devolve 429.

LimiteRecortePadrãoVale para
Por tokencredencial de integração autenticada120 req/minrequisições autenticadas por credencial de API
Por origemIP de origem da requisição120 req/mintodas as requisições, autenticadas ou não

Ambos usam janela deslizante de 1 minuto.

Limite por token​

Cada credencial de integração tem o seu próprio orçamento: dois tokens que saem do mesmo IP não competem entre si, e trocar de IP de origem não renova o orçamento de um token.

O limite é aplicado depois da autenticação — requisição sem credencial válida é rejeitada antes (401), sem consumir orçamento de token.

Limite por origem​

Aplicado antes da autenticação, por IP de origem. É o que protege as rotas públicas:

  • POST /v1/auth/login — 10 req/min
  • POST /v1/auth/token — 30 req/min
  • demais rotas de integração /v1/* — 120 req/min

Onde os limites não se aplicam​

  • /v1/health, /v1/openapi.json, /v1/.well-known/jwks.json
  • rotas internas (Backoffice, Portal, Starkbank, migração)

Headers de rate limit​

Toda resposta das rotas limitadas inclui:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 95

Em resposta de sucesso, os valores são os do limite por origem. Em um 429, os valores são sempre os do bucket que negou: X-RateLimit-Limit traz o teto violado (o do token, se foi ele) e X-RateLimit-Remaining vem 0. Um 429 nunca reporta saldo restante.

Resposta 429​

Ao exceder o limite, a API responde 429 com o header Retry-After (segundos até liberar).

Os dois limites usam o mesmo corpo: detail é sempre um objeto com code, scope, limit_rpm, retry_after_seconds e message. Só o scope muda — é ele que diz qual bucket negou.

Limite por token (scope: "token"):

{
"detail": {
"code": "rate_limit_exceeded",
"scope": "token",
"limit_rpm": 120,
"retry_after_seconds": 60,
"message": "Token excedeu o limite de 120 requisicoes por minuto. Aguarde 60s antes de retentar."
}
}

Limite por origem (scope: "ip"):

{
"detail": {
"code": "rate_limit_exceeded",
"scope": "ip",
"limit_rpm": 120,
"retry_after_seconds": 60,
"message": "Origem excedeu o limite de 120 requisicoes por minuto. Aguarde 60s antes de retentar."
}
}

Um único parse serve os dois: leia detail.code para identificar o erro e detail.scope para saber qual limite foi atingido (veja Códigos de Erro).

Mudança de contrato

Até a versão anterior o limite por origem respondia detail como a string "rate_limit_exceeded", com retry_after_seconds ao lado do detail. Esse formato não existe mais: quem tratava detail como string nesse caso precisa passar a ler detail.code. Os headers Retry-After e X-RateLimit-* não mudaram.

Boas práticas

Respeite o Retry-After antes de retentar e use backoff exponencial em falhas consecutivas. Precisa de um limite maior? Fale com o Backoffice Zemo.