Por que usar rate limit
Toda API tem uma capacidade finita, e nem todo cliente se comporta adequadamente. Um scraper, um cliente com bug preso em um loop de tentativas (retry loop) ou um pico de tráfego legítimo podem esgotar as conexões do seu banco de dados e derrubar o serviço para todos. O rate limiting limita a velocidade com que um cliente pode fazer requisições, garantindo que nenhum usuário individual consuma todos os recursos do sistema.
Isso também torna os custos previsíveis, impõe o uso justo entre diferentes tenants e planos, e oferece a você uma ferramenta para conter abusos antes que eles se tornem uma interrupção total do serviço.
O que limitar
Um limite só faz sentido em relação a uma identidade. As escolhas mais comuns são:
- API key — a melhor opção para comunicações server-to-server e clientes de terceiros.
- User id — a escolha natural para apps com autenticação.
- IP address — um fallback para tráfego anônimo, embora muitos usuários possam compartilhar o mesmo IP.
- Combinação — chave mais IP, ou usuário mais endpoint, para maior precisão.
- Endpoint ou tier — limites mais rigorosos para operações custosas, como buscas ou exportações.
Além disso, decida o escopo: um limite global, um limite por endpoint, ou ambos. Um limite global protege o serviço; limites por endpoint protegem tarefas específicas e onerosas.
Algoritmos
Quatro algoritmos cobrem quase todas as necessidades.
Fixed window — conta as requisições em um intervalo fixo e as reseta no limite do período.
limit: 100 per minute
key: client:123:2026-09-15T10:05
É barato e simples, mas um cliente pode enviar 100 requisições no final de uma janela e 100 no início da próxima, efetivamente dobrando a taxa no limite da transição.
Sliding window — conta as requisições nos últimos N segundos em vez de um bloco fixo, geralmente combinando a janela atual e a anterior com uma média ponderada. É mais suave que o fixed window com um custo semelhante.
Token bucket — um bucket é preenchido a uma taxa constante, e cada requisição consome um token. Isso permite picos curtos de tráfego até o limite do bucket enquanto mantém uma taxa média, o que condiz com o comportamento de clientes reais.
// token-bucket.js
const capacity = 20;
const refillPerSecond = 5;
let tokens = capacity;
let last = Date.now();
function allow() {
const now = Date.now();
tokens = Math.min(capacity, tokens + ((now - last) / 1000) * refillPerSecond);
last = now;
if (tokens < 1) return false;
tokens -= 1;
return true;
}
Leaky bucket — as requisições entram em uma fila que é esvaziada a uma taxa fixa. Isso suaviza o tráfego para uma saída constante, sendo útil quando os sistemas downstream precisam de uma carga estável.
Respondendo a limites
Informe aos clientes o que está acontecendo utilizando sinais padronizados.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30
- 429 Too Many Requests é o código de status correto.
- Retry-After informa ao cliente quando tentar novamente, seja em segundos ou em uma data específica.
- Headers RateLimit-* expõem o limite, a contagem restante e o tempo de reset para que os clientes possam controlar a cadência das requisições.
Retornar 200 ou descartar requisições silenciosamente esconde o problema e gera bugs misteriosos no cliente. Clientes bem implementados leem Retry-After e reduzem a frequência de requisições automaticamente.
Limitação distribuída
Se você executar mais de uma instância, os contadores devem ser compartilhados. Limites em memória aplicam-se por instância, portanto, com dez servidores, um cliente efetivamente recebe dez vezes o limite.
// redis.js
const key = `ratelimit:${apiKey}`;
const count = await redis.incr(key);
if (count === 1) await redis.expire(key, 60);
if (count > 100) {
// reject with 429
}
Use operações atômicas ou uma biblioteca que utilize um script Lua, para que os incrementos e expirações sejam livres de race conditions. Redis é o armazenamento usual por ser rápido e suportar scripts atômicos e expiração. API gateways e plataformas de edge podem aplicar limites antes mesmo que o tráfego chegue à sua aplicação, que é o lugar mais eficiente para fazer isso.
Cotas versus rate limits
Eles resolvem problemas diferentes e frequentemente coexistem.
- Rate limit — a velocidade: 100 requisições por minuto.
- Quota — a quantidade: 100.000 requisições por mês, vinculadas a um plano.
Um cliente pode permanecer abaixo do seu rate limit durante todo o mês e ainda assim esgotar sua quota. Monitore as quotas separadamente, geralmente com um contador de maior duração, e retorne um erro distinto quando uma quota for esgotada para que os clientes saibam a diferença.
Evitando prejudicar usuários reais
Os limites devem impedir abusos sem punir o uso normal.
- Defina limites com base no tráfego medido, deixando uma margem para picos.
- Permita picos utilizando um token bucket em vez de um corte abrupto.
- Use limites diferentes por tier e por endpoint.
- Exclua health checks, chamadas internas e assets estáticos.
- Prefira reduções temporárias de velocidade a banimentos permanentes.
- Monitore as taxas de rejeição e faça ajustes; um pico de erros 429 pode significar que um limite está rigoroso demais.
Melhores práticas
- Identifique os clientes com a chave estável mais específica disponível.
- Escolha um algoritmo que corresponda ao perfil do tráfego.
- Retorne 429 com
Retry-Aftere headers de rate limit. - Compartilhe contadores no Redis ou aplique os limites na edge.
- Separe rate limits de quotas e exponha ambos.
- Permita bursts e defina os limites com base em medições reais.
- Registre e monitore rejeições, e configure alertas para picos repentinos.
Erros comuns
- Contar por instância e multiplicar o limite efetivo.
- Utilizar apenas o IP, o que prejudica usuários atrás de um NAT compartilhado.
- Retornar 200 ou descartar requisições silenciosamente.
- Definir limites tão rígidos que clientes normais acabam sendo bloqueados.
- Esquecer de expirar os contadores, causando vazamento de memória.
- Tratar rate limits como substitutos para autenticação e autorização.
Próximos passos
O rate limiting mantém uma API disponível mesmo sob pressão. Aprofunde seus conhecimentos em design REST e no guia HTTP, combine-o com Versionamento de API como parte do ciclo de vida e implemente-o em Node.js. Depois, adicione um limiter a um endpoint e observe como ele se comporta sob um teste de carga.