API Protection

Rate Limiting

O rate limiting protege uma API contra abusos, bugs e clientes descontrolados. Alguns algoritmos e headers claros mantêm seu serviço disponível para todos.

intermediate14 min readUpdated 15 de set. de 2026
limit.js
js
// limit.js
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(100, "1 m"),
});

export async function handler(req, res) {
  const id = req.headers["x-api-key"] ?? req.ip;
  const { success, remaining, reset } = await ratelimit.limit(id);

  res.set("RateLimit-Remaining", String(remaining));
  res.set("RateLimit-Reset", String(Math.ceil(reset / 1000)));

  if (!success) {
    res.set("Retry-After", "60");
    return res.status(429).json({ error: "rate_limited" });
  }
  // ...handle the request
}
Status
429 Too Many Requests
Dica
Header Retry-After
Identidade
Key, usuário ou IP
Algoritmos
Window, bucket
Armazenamento
Redis para múltiplas instâncias
Objetivo
Disponibilidade para todos

Por que importa

Por que usar rate limit

Proteger a disponibilidade

Um único cliente mal comportado ou um bot de scraping pode esgotar sua capacidade. Os limites mantêm o serviço no ar para todos os demais.

Uso justo

Limites por chave e por tier garantem que um único chamador não consuma mais do que a sua parte.

Limitar danos

Os limites também restringem o custo de bugs, como um cliente preso em um loop de retry bombardeando sua API.

O panorama completo

As três ideias por trás do rate limiting

Identifique o cliente, conte suas requisições com um algoritmo e informe o que aconteceu com o status e headers corretos.

Identidade

Contagem

Decida com base em que a requisição será contada — uma API key, um usuário, um IP ou uma combinação.

Algoritmo

Limite

Fixed window, sliding window, token bucket ou leaky bucket decidem como as requisições são permitidas.

Resposta

Sinalização

Retorne 429 com Retry-After e headers de rate limit para que os clientes possam reduzir a frequência de chamadas corretamente.

Rate limiting em resumo

As ideias centrais

Fixed window

Um contador simples resetado a cada intervalo; barato, mas permite picos na fronteira do intervalo.

Sliding window

Suaviza o problema da fronteira com maior precisão.

Token bucket

Permite picos até o tamanho do bucket enquanto impõe uma taxa média.

Leaky bucket

Processa em uma taxa constante e enfileira ou descarta o excesso.

429 e Retry-After

A maneira padrão de dizer "vá mais devagar, tente novamente mais tarde".

Estado distribuído

Compartilhe contadores no Redis para que os limites se apliquem a todas as instâncias.

Uma breve historia

De bloqueios de IP a limitadores distribuídos

  1. 2000s

    Bloqueio baseado em IP

    Defesas iniciais bloqueiam IPs abusivos após o ocorrido.

    2000s
  2. 2010s

    API keys e quotas

    APIs públicas introduzem limites por chave e quotas mensais.

    2010s
  3. 2015

    Limitadores com Redis

    Contadores compartilhados fazem o limite funcionar entre vários servidores.

    15
  4. 2020

    Headers padronizados

    Headers de RateLimit são propostos para tornar os limites descobertos programaticamente.

    20
  5. Today

    Defesas em camadas

    Limites, quotas, WAFs e detecção de bots trabalham juntos.

    Today

O guia completo

Rate Limiting: Tudo que voce precisa saber

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-After e 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.

Rejeitando uma requisição

Retorne 429 com uma dica de Retry-After e a contagem restante. Descartar silenciosamente ou retornar 200 confunde os clientes e esconde o problema.

Preferir
HTTP/1.1 429 Too Many Requests
Retry-After: 30
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 30
Evitar
HTTP/1.1 200 OK
# silently ignored or
# returns an empty body

Armazenando contadores

Com múltiplas instâncias, os contadores devem ser compartilhados. Limites em memória aplicam-se por instância e permitem que clientes obtenham N vezes a taxa pretendida.

Preferir
const { success } = await ratelimit.limit(key);
// shared across every instance
Evitar
const counts = new Map();
// each server has its own map,
// so the real limit is N x

Trade-offs

O rate limiting é a defesa certa?

Os limites protegem a disponibilidade e limitam o abuso, mas acrescentam estado e podem penalizar utilizadores legítimos se a identidade ou os limiares estiverem errados.

Strengths

  • Mantém o serviço de pé

    Um cliente descontrolado ou um scraper não consegue esgotar a capacidade, por isso todos os outros continuam a ser servidos.

  • Partilha justa

    Limites por chave e por escalão impedem que um único chamador consuma mais do que a sua parte de um recurso partilhado.

  • Limita o custo dos bugs

    Um cliente preso num ciclo de retentativas é contido antes de se transformar numa indisponibilidade ou numa fatura elevada.

Trade-offs

  • Precisa de estado partilhado

    Limites corretos entre instâncias exigem Redis ou um gateway, o que acrescenta uma dependência no caminho crítico.

  • Fácil de errar

    Limites demasiado apertados ou a identidade errada rejeitam utilizadores reais, e limites por IP penalizam todos os que estão atrás de um NAT.

  • Não é uma defesa completa

    Limitar sozinho não trava ataques distribuídos, por isso funciona melhor ao lado de quotas, WAFs e deteção de bots.

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender Rate Limiting?

Nosso tutorial interativo te guia por Rate Limiting passo a passo — com quizzes e codigo real que voce pode executar no navegador.