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.
| Limite | Recorte | Padrão | Vale para |
|---|---|---|---|
| Por token | credencial de integração autenticada | 120 req/min | requisições autenticadas por credencial de API |
| Por origem | IP de origem da requisição | 120 req/min | todas 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/minPOST /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).
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.
Respeite o Retry-After antes de retentar e use backoff exponencial em falhas consecutivas. Precisa de um limite maior? Fale com o Backoffice Zemo.