Pular para o conteúdo
Diogo FC

Rate Limiting: como proteger APIs e controlar o consumo de recursos

À medida que uma aplicação cresce, aumenta também a quantidade de requisições recebidas por seus serviços. Nem todas essas requisições, entretanto, são legítimas ou precisam ser processadas imediatamente.

13 min de leitura

Um único usuário pode realizar centenas de chamadas em poucos segundos, um bot pode tentar descobrir credenciais por força bruta ou uma integração pode consumir recursos muito acima do esperado. Em aplicações distribuídas, esse comportamento pode comprometer a disponibilidade de toda a infraestrutura.

É nesse cenário que entra o Rate Limiting, uma técnica utilizada para controlar a frequência com que clientes podem acessar determinados recursos.

Neste artigo vamos entender como funciona o Rate Limiting, quais são os principais algoritmos, onde implementar essa estratégia e quais decisões arquiteturais devem ser consideradas em aplicações modernas.

O que é Rate Limiting?

Rate Limiting é uma técnica que estabelece limites para a quantidade de requisições ou operações que um cliente pode executar dentro de um determinado intervalo de tempo.

Na prática, funciona como uma política de consumo: cada cliente possui uma determinada quantidade de requisições permitidas e, ao ultrapassar esse limite, novas chamadas são temporariamente bloqueadas.

Alguns exemplos:

  • 100 requisições por minuto para uma API pública;
  • 5 tentativas de login a cada 10 minutos;
  • 3 mensagens a cada 30 segundos;
  • 10 uploads por hora;
  • 1.000 consultas por dia para uma API de terceiros.

O limite pode variar conforme o usuário, plano contratado, endpoint ou até mesmo o tipo de operação realizada.

Quando o cliente ultrapassa o limite estabelecido, a aplicação normalmente retorna o status HTTP 429 Too Many Requests, informando que novas requisições devem aguardar.

Por que utilizar Rate Limiting?

Além de controlar o volume de acessos, essa estratégia resolve problemas importantes relacionados à segurança, disponibilidade e custos operacionais.

Proteção contra abusos

Sem limites de consumo, um usuário ou bot pode realizar requisições continuamente, explorando recursos da aplicação.

Alguns cenários comuns:

  • Brute Force: tentativas repetidas de descobrir senhas ou credenciais.
  • Spam: envio excessivo de mensagens, comentários ou formulários.
  • Scraping: coleta automatizada de grandes volumes de informações.
  • Abuso de APIs: consumo excessivo de endpoints por integrações ou clientes mal configurados.

O Rate Limiting dificulta esses comportamentos, estabelecendo uma frequência máxima de utilização.

É importante destacar que ele não substitui mecanismos como autenticação, autorização e WAF. Trata-se de uma camada adicional de proteção.

Garantir disponibilidade

Imagine uma API utilizada por milhares de usuários.

Se um único cliente realizar milhares de requisições simultâneas, poderá consumir grande parte dos recursos disponíveis, comprometendo o atendimento dos demais usuários.

Ao estabelecer limites individuais, é possível evitar que determinados clientes monopolizem recursos como:

  • CPU e memória;
  • conexões com o banco de dados;
  • processamento de arquivos;
  • chamadas para serviços externos.

Isso contribui para uma distribuição mais equilibrada dos recursos e para a disponibilidade da aplicação.

Controlar custos operacionais

Nem toda requisição possui o mesmo custo.

Uma chamada simples que consulta um cache é diferente de uma operação que executa consultas complexas no banco, processa arquivos ou utiliza modelos de inteligência artificial.

Considere uma aplicação que utiliza LLMs para gerar relatórios. Cada requisição pode representar um custo financeiro associado ao processamento do modelo.

Sem limites, um único usuário poderia gerar um consumo muito acima do esperado.

O Rate Limiting permite estabelecer políticas de utilização e proteger o orçamento operacional da aplicação.

Diferenciar planos de utilização

Outra aplicação bastante comum é controlar o consumo conforme o plano contratado.

Plano | Limite de exemplo

Gratuito | 100 requisições por dia

Pro | 10.000 requisições por dia

Enterprise | Limites personalizados

Nesse cenário, o Rate Limiting também se torna parte das regras comerciais do produto.

Principais algoritmos de Rate Limiting

Existem diferentes algoritmos para controlar a frequência das requisições. Cada abordagem possui características próprias, principalmente em relação à precisão, consumo de memória e capacidade de absorver picos de tráfego.

1. Fixed Window (Janela Fixa)

É uma das estratégias mais simples de implementar.

O algoritmo divide o tempo em intervalos fixos e mantém um contador de requisições para cada janela.

Imagine uma API que permite 100 requisições por minuto.

O funcionamento seria:

  1. No início de cada minuto, o contador começa em zero.
  2. Cada requisição incrementa o contador.
  3. Ao atingir 100 requisições, novas chamadas são bloqueadas.
  4. No início do próximo minuto, o contador é reiniciado.

Exemplo:

12:00:00
Contador = 0

↓

100 requisições

↓

Limite atingido
Novas requisições bloqueadas

↓

12:01:00
Contador = 0

Vantagens:

  • Implementação simples;
  • Baixo consumo de memória;
  • Fácil integração com Redis;
  • Boa opção para limites básicos.

Desvantagens:

O principal problema é o chamado Boundary Burst (pico na fronteira da janela).

Imagine que um usuário realize 100 requisições às 12:00:59. Como o contador será reiniciado às 12:01:00, ele poderá realizar outras 100 requisições imediatamente.

Na prática, serão 200 requisições em poucos segundos, mesmo com um limite de 100 por minuto.

Essa característica pode ser problemática em APIs que precisam de um controle mais rigoroso do tráfego.

2. Sliding Window (Janela Deslizante)

Diferentemente da janela fixa, a janela deslizante considera sempre um intervalo de tempo anterior à requisição atual.

Imagine uma regra que permite até 3 mensagens a cada 30 segundos.

A cada nova mensagem, o algoritmo verifica quais requisições ainda estão dentro dos últimos 30 segundos e remove aquelas que já expiraram.

Exemplo:

Regra: 3 mensagens a cada 30 segundos

12:00:00 → Mensagem 1
12:00:10 → Mensagem 2
12:00:20 → Mensagem 3

12:00:25 → Nova tentativa
↓
Bloqueada

12:00:30 → Mensagem 1 expira
↓
Nova tentativa permitida

Uma implementação pode armazenar os timestamps das requisições em estruturas como:

  • Queue;
  • Linked List;
  • Sorted Set do Redis.

Existem também variações como Sliding Window Counter, que utiliza contadores de janelas adjacentes para estimar o volume de requisições, reduzindo o custo de armazenamento.

Vantagens:

  • Controle mais preciso da frequência;
  • Reduz os picos causados pelas fronteiras de janelas fixas;
  • Permite políticas baseadas em intervalos realmente móveis.

Desvantagens:

  • Pode exigir armazenamento de timestamps;
  • Possui maior complexidade de implementação;
  • O Sliding Window Log pode consumir mais memória em cenários de alto volume.

É uma boa alternativa quando a aplicação precisa de limites mais precisos, especialmente para operações sensíveis, como autenticação e envio de mensagens.

3. Token Bucket

O Token Bucket utiliza uma abordagem diferente: em vez de contar requisições realizadas, controla a quantidade de tokens disponíveis para consumo.

Imagine um balde com capacidade máxima de 5 tokens.

Cada requisição consome um token, enquanto novos tokens são adicionados continuamente, respeitando uma taxa de reposição.

Exemplo:

Capacidade: 5 tokens

┌─────────────────┐
│ ● ● ● ● ● │
└─────────────────┘

Cada requisição consome 1 token.

A cada segundo:
+1 token, até atingir a capacidade máxima.

Se não houver tokens disponíveis, a requisição será bloqueada ou precisará aguardar, dependendo da implementação.

O principal diferencial dessa estratégia é permitir pequenos picos de tráfego (bursts), desde que existam tokens acumulados.

Por exemplo, uma API pode permitir uma capacidade inicial de 10 requisições e repor 1 token por segundo.

Isso significa que o cliente pode realizar 10 requisições rapidamente e, depois, continuará consumindo tokens conforme eles forem repostos.

Vantagens:

  • Permite picos controlados de tráfego;
  • Não precisa armazenar todas as requisições anteriores;
  • Possui boa flexibilidade;
  • Funciona bem em APIs públicas e microsserviços.

Desvantagens:

  • Exige gerenciamento da quantidade de tokens;
  • Precisa considerar concorrência ao atualizar o estado;
  • A implementação distribuída exige cuidado com operações atômicas.

É uma das estratégias mais interessantes para APIs que precisam equilibrar flexibilidade e controle de consumo.

4. Leaky Bucket

O Leaky Bucket utiliza a ideia de um balde com um pequeno furo.

As requisições podem chegar rapidamente, mas o processamento acontece em uma velocidade controlada.

Imagine que 100 requisições cheguem praticamente ao mesmo tempo. Em vez de encaminhar todas imediatamente para o processamento, elas são colocadas em uma fila e liberadas gradualmente.

       Requisições
↓ ↓ ↓ ↓ ↓ ↓

┌─────────────┐
│ │
│ Bucket │
│ │
└──────┬──────┘
│
│ Saída controlada
▼
Processamento

Diferentemente do Token Bucket, que permite consumir tokens acumulados e absorver picos, o Leaky Bucket procura manter uma taxa de saída constante.

Vantagens:

  • Suaviza picos de requisições;
  • Torna o fluxo de processamento mais previsível;
  • Ajuda a proteger serviços com capacidade limitada;
  • É útil para controlar a taxa de execução de operações.

Desvantagens:

  • Pode aumentar a latência;
  • Exige gerenciamento de filas;
  • Requisições podem ser descartadas quando a fila atinge sua capacidade máxima.

Essa abordagem é interessante quando o objetivo não é apenas limitar o volume, mas também controlar a velocidade com que as operações chegam ao processamento.

Comparando os algoritmos

Cada estratégia resolve um problema diferente. A escolha depende principalmente do comportamento de tráfego que a aplicação precisa suportar.

Algoritmo | Principal característica | Melhor aplicação

Fixed Window | Contador por intervalo fixo | Limites simples

Sliding Window | Considera o intervalo anterior | Controle preciso

Token Bucket | Permite bursts controlados | APIs e microsserviços

Leaky Bucket | Mantém uma taxa de saída constante | Processamento controlado

Não existe um algoritmo universalmente melhor. Em muitos cenários, Token Bucket oferece um bom equilíbrio entre simplicidade, desempenho e flexibilidade.

Onde implementar Rate Limiting?

Além de escolher o algoritmo, outra decisão importante é definir em qual camada da arquitetura o controle será aplicado.

1. Cliente (Frontend)

O frontend pode implementar mecanismos para evitar requisições desnecessárias.

Alguns exemplos:

  • Desabilitar um botão após o primeiro clique;
  • Impedir múltiplos envios de formulários;
  • Utilizar debounce em campos de pesquisa;
  • Utilizar throttle em eventos de scroll.

Essas técnicas ajudam a melhorar a experiência do usuário e reduzem chamadas desnecessárias.

Entretanto, existe uma limitação importante: o cliente não é confiável.

Um usuário pode utilizar Postman, scripts ou qualquer outra ferramenta para realizar chamadas diretamente à API, ignorando completamente as restrições implementadas no navegador.

Portanto, o controle no frontend deve ser considerado apenas uma otimização de experiência, nunca uma camada de segurança.

2. Backend (Aplicação)

Outra possibilidade é implementar o Rate Limiting diretamente no backend.

Nesse cenário, cada requisição passa por um middleware, interceptor ou componente responsável por verificar se o cliente ainda possui chamadas disponíveis.

Fluxo simplificado:

Cliente
↓
API
↓
Middleware de Rate Limiting
↓
Validação do limite
↓
Controller
↓
Processamento

Frameworks como NestJS, Express e Fastify possuem recursos ou integrações que facilitam essa implementação.

Vantagens:

  • Controle próximo das regras de negócio;
  • Fácil integração com autenticação;
  • Possibilidade de criar limites específicos por endpoint;
  • Maior flexibilidade para políticas personalizadas.

Desvantagens:

Em aplicações com múltiplas instâncias, existe um problema importante: cada servidor pode manter seu próprio contador.

Imagine uma aplicação com três instâncias:

          Load Balancer
|
┌────────┼────────┐
↓ ↓ ↓
API 1 API 2 API 3
100 100 100

Se cada instância permitir 100 requisições por minuto, um mesmo usuário poderá realizar até 300 requisições, dependendo de como o balanceamento distribuir as chamadas.

Isso acontece porque o estado do controle está isolado em cada servidor.

Para resolver esse problema, precisamos compartilhar os contadores.

3. Redis (Estado Compartilhado)

Uma solução bastante utilizada em arquiteturas distribuídas é armazenar o estado do Rate Limiting em um Redis.

Em vez de cada instância controlar seus próprios contadores, todas consultam e atualizam o mesmo armazenamento.

             Redis
▲
|
┌───────┼───────┐
| | |
API 1 API 2 API 3

Dessa forma, independentemente de qual instância receba a requisição, o limite será compartilhado.

Vantagens:

  • Estado compartilhado entre instâncias;
  • Baixa latência de acesso;
  • Suporte a diferentes estruturas de dados;
  • Integração com algoritmos como Fixed Window, Sliding Window e Token Bucket;
  • Possibilidade de expiração automática dos contadores utilizando TTL.

Entretanto, é importante considerar a concorrência.

Se duas requisições consultarem o contador simultaneamente, ambas podem identificar que ainda existe espaço disponível e ultrapassar o limite.

Para evitar esse comportamento, operações de leitura e atualização precisam ser atômicas. No Redis, isso pode ser implementado utilizando Lua Scripts ou comandos adequados para cada estratégia.

Outro ponto importante é avaliar o comportamento da aplicação quando o Redis estiver indisponível: bloquear todas as requisições (fail-closed) ou permitir temporariamente o acesso (fail-open).

Essa decisão depende do nível de criticidade do serviço e do risco associado ao consumo não controlado.

4. API Gateway / Reverse Proxy

Também é possível aplicar Rate Limiting antes mesmo de a requisição chegar ao backend.

Nesse modelo, a própria infraestrutura controla o volume de chamadas recebido pela aplicação.

Exemplo:

Cliente
↓
CDN / WAF
↓
API Gateway
↓
Load Balancer
↓
Backend

Algumas soluções que oferecem esse tipo de recurso:

  • NGINX;
  • Kong;
  • Traefik;
  • AWS API Gateway;
  • Cloudflare;
  • Azure API Management.

Vantagens:

  • Bloqueia requisições antes de consumir recursos da aplicação;
  • Centraliza políticas de acesso;
  • Reduz processamento desnecessário no backend;
  • Facilita a aplicação de limites globais;
  • Pode proteger múltiplos serviços simultaneamente.

Desvantagens:

  • Menor flexibilidade para regras que dependem de informações internas do negócio;
  • Dependência da configuração e disponibilidade da infraestrutura;
  • Necessidade de integração com mecanismos de autenticação para identificar corretamente os usuários.

O API Gateway é uma excelente opção para aplicar políticas de proteção na entrada da arquitetura. Entretanto, regras específicas de negócio podem continuar sendo responsabilidade do backend.

Como identificar quem está realizando as requisições?

Para aplicar limites corretamente, precisamos definir como identificar cada cliente.

Os identificadores mais comuns são:

  • IP: útil para usuários anônimos e proteção inicial;
  • User ID: ideal para controlar o consumo individual de usuários autenticados;
  • API Key: comum em APIs públicas e integrações externas;
  • JWT: permite identificar o usuário ou as permissões associadas ao token;
  • Session ID: utilizado em aplicações que trabalham com sessões;
  • Tenant ID: importante em arquiteturas multi-tenant, nas quais diferentes empresas compartilham a mesma infraestrutura.

A escolha depende do contexto.

Por exemplo, limitar apenas pelo IP pode causar problemas em empresas, universidades e redes compartilhadas, nas quais centenas de usuários podem utilizar o mesmo endereço público.

Da mesma forma, utilizar apenas o User ID não protege endpoints públicos que ainda não exigem autenticação.

Uma abordagem interessante é combinar diferentes identificadores conforme o tipo de operação.

Boas práticas

Ao implementar Rate Limiting em uma aplicação, alguns cuidados fazem diferença:

1. Diferencie usuários anônimos e autenticados.

Usuários não autenticados podem receber limites mais restritivos, enquanto clientes identificados podem possuir políticas específicas.

2. Utilize HTTP 429.

Quando o limite for ultrapassado, retorne 429 Too Many Requests, deixando claro que o cliente excedeu a frequência permitida.

3. Informe quando tentar novamente.

Utilize o header Retry-After para indicar quanto tempo o cliente deve aguardar antes de realizar uma nova tentativa.

4. Considere limites diferentes por endpoint.

Uma consulta simples não possui o mesmo custo de uma operação que gera relatórios, processa arquivos ou utiliza inteligência artificial.

5. Utilize armazenamento compartilhado em arquiteturas distribuídas.

Redis ou o próprio mecanismo de Rate Limiting do API Gateway podem garantir que diferentes instâncias respeitem a mesma política.

6. Monitore os bloqueios.

Acompanhe métricas como quantidade de requisições bloqueadas, usuários mais limitados, endpoints com maior consumo e frequência de erros HTTP 429.

7. Considere a concorrência.

Garanta que a atualização dos contadores seja atômica, principalmente quando múltiplas instâncias recebem requisições simultaneamente.

8. Não confie apenas no frontend.

Toda política de limitação que tenha impacto em segurança, custos ou disponibilidade precisa ser validada no servidor ou em uma camada confiável da infraestrutura.

Conclusão

Rate Limiting é muito mais do que simplesmente limitar a quantidade de requisições de uma API.

É um mecanismo importante para proteger recursos, controlar custos, evitar abusos e manter a disponibilidade de aplicações, principalmente em arquiteturas distribuídas.

A escolha do algoritmo e do local de implementação depende dos requisitos de cada sistema.

Em aplicações menores, um middleware no backend pode ser suficiente. Conforme a arquitetura cresce, utilizar um armazenamento compartilhado como Redis ou centralizar políticas no API Gateway pode oferecer maior consistência e controle.

Uma arquitetura também pode combinar essas estratégias: o Gateway protege a entrada, o backend aplica regras específicas de negócio e o Redis mantém o estado compartilhado entre as instâncias.

No final, o objetivo não é simplesmente impedir requisições, mas garantir que os recursos da aplicação sejam utilizados de maneira equilibrada, previsível e sustentável.

Saiba mais