Rate limiting é um controle que limita quantas requisições um cliente pode fazer por janela de tempo; throttling desacelera o excesso em vez de rejeitá-lo. Some o terceiro irmão — a cota, uma franquia por período de cobrança — e você tem o vocabulário completo que a maioria das explicações embaralha numa palavra só. Juntos, eles são o que mantém as APIs justas, solventes e no ar.
Principais pontos
| Pergunta | Resposta |
|---|---|
| Rate limit | Teto rígido por janela curta — o excesso recebe 429 |
| Throttle | O excesso é desacelerado ou enfileirado, não recusado |
| Cota | A franquia de longo prazo — por dia/mês, por plano |
| A voz do servidor | 429 + Retry-After + headers de rate limit |
| Os modos do cliente | Respeite o Retry-After; backoff exponencial com jitter |
O que significam a resposta 429 e os headers de rate limit
HTTP/1.1 429 Too Many Requests ← RFC 6585
Retry-After: 30 ← espere esse tempo (segundos ou uma data)
X-RateLimit-Limit: 1000 ← seu orçamento nesta janela
X-RateLimit-Remaining: 0 ← o que resta
X-RateLimit-Reset: 1767024000 ← quando ele renova
RateLimit: "default";r=0;t=30 ← a forma padrão emergente do IETF
A metade do contrato que cabe ao cliente — o código de que todo consumidor de SDK acaba precisando:
// JavaScript / Node.js — Back4app JS SDK
// The client half of rate limiting: back off, with jitter, then retry
async function withBackoff(fn, attempt = 0) {
try {
return await fn();
} catch (e) {
if (e.code !== 155 || attempt >= 4) throw e; // 155: request limit hit
const wait = 2 ** attempt * 500 + Math.random() * 200;
await new Promise((r) => setTimeout(r, wait)); // exponential + jitter
return withBackoff(fn, attempt + 1);
}
}
const results = await withBackoff(() => query.find()); // Flutter / Dart — Back4app Flutter SDK
// The client half of rate limiting: back off, with jitter, then retry
Future<ParseResponse> withBackoff(Future<ParseResponse> Function() fn,
[int attempt = 0]) async {
final response = await fn();
if (response.success || attempt >= 4) return response;
final wait = (1 << attempt) * 500 + Random().nextInt(200);
await Future.delayed(Duration(milliseconds: wait)); // exponential + jitter
return withBackoff(fn, attempt + 1);
}
final response = await withBackoff(() => query.query()); // iOS / Swift — Back4app Swift SDK
// The client half of rate limiting: back off, with jitter, then retry
func withBackoff<T>(_ attempt: Int = 0,
_ fn: @escaping () async throws -> T) async throws -> T {
do { return try await fn() }
catch {
guard attempt < 4 else { throw error }
let wait = pow(2.0, Double(attempt)) * 0.5 + .random(in: 0...0.2)
try await Task.sleep(for: .seconds(wait)) // exponential + jitter
return try await withBackoff(attempt + 1, fn)
}
}
let results = try await withBackoff { try await query.find() } // Android / Kotlin — Back4app Android SDK
// The client half of rate limiting: back off, with jitter, then retry
suspend fun <T> withBackoff(attempt: Int = 0, fn: suspend () -> T): T {
return try {
fn()
} catch (e: ParseException) {
if (attempt >= 4) throw e
val wait = (1L shl attempt) * 500 + Random.nextLong(200)
delay(wait) // exponential + jitter
withBackoff(attempt + 1, fn)
}
}
val results = withBackoff { query.find() } Token bucket vs. leaky bucket vs. janela fixa vs. janela deslizante
| Algoritmo | Bursts | Precisão | Memória | Veredicto |
|---|---|---|---|---|
| Token bucket | Permitidos, até o tamanho do balde | Boa | Mínima | O padrão para APIs — humanos em bursts, média constante |
| Leaky bucket | Suavizados num gotejo constante | Boa | Pequena | Shaping de tráfego — saída constante, latência adicionada |
| Janela fixa | Burst de fronteira: 2× o limite nas bordas | Fraca | Mínima | Simples, e notoriamente burlável |
| Janela deslizante | Controlados | A melhor | Modesta | O compromisso de escala em que a maioria das plataformas aterrissa |
Uma nota de rodapé distribuída que os diagramas pulam: com muitos servidores de API, o balde precisa viver em algum lugar compartilhado — tipicamente um store em memória fazendo incrementos atômicos — e você escolhe entre precisão estrita (toda checagem consulta o store) e velocidade (contadores locais, sincronização frouxa). A maioria das plataformas aceita limites levemente frouxos como o preço da latência; cotas contratuais recebem o tratamento estrito.
Escopo: quem exatamente é limitado?
Por IP barra enxurradas anônimas e vazamento de absorção de DDoS, mas um NAT de escritório transforma centenas de usuários num único endereço. Por chave mapeia limites a aplicações e planos de preço — o escopo que carrega o piano. Por usuário impede que uma conta monopolize a chave de um app compartilhado. Por endpoint precifica operações caras com honestidade — endpoints de busca e export merecem orçamentos mais apertados que health checks. Sistemas de produção os combinam em camadas; a pergunta composta é sempre “qual orçamento esta requisição gastou?”
Casos de uso comuns
- Proteção de API pública — o caso canônico: orçamentos justos por chave, publicados em headers, aplicados na borda.
- Aplicação de tiers — planos gratuitos vs. pagos que diferem precisamente em cota e folga de burst.
- Defesa contra abuso e scraping — limites apertados para anônimos, generosos para autenticados.
- Controle de custo em caminhos caros — chamadas de LLM, exports, buscas: orçamentos por endpoint que refletem o custo real.
- Autodefesa no lado do cliente — backoff e coalescência de requisições contra os limites dos outros, que suas integrações precisam respeitar para não serem banidas.
Rejeitar ou fazer throttling? Matriz de decisão
| Rate limit (rejeitar) quando… | Throttle (desacelerar/enfileirar) quando… |
|---|---|
| Os clientes conseguem fazer retry com inteligência | O trabalho precisa acontecer em algum momento |
| Você protege capacidade interativa | Você suaviza carga de batch e background |
| O contrato é requisições por janela | O contrato é justiça na prestação do serviço |
| Feedback rápido vale mais que sucesso atrasado | Sucesso atrasado vale mais que um erro |
| O tráfego é anônimo ou não confiável | Os produtores são seus, internos |
E a meta-regra: seja qual for a escolha, publique-a — limites na documentação e nos headers transformam um muro frustrante num contrato de engenharia contra o qual os clientes conseguem construir.
Limitações e trade-offs
- Limites são instrumentos rombudos. Uma requisição não é uma unidade de custo — uma leitura barata e um export monstruoso gastam “1” cada. Limitação sensível a custo (pontos por operação) é o refinamento, pelo preço da complexidade.
- Precisão distribuída custa latência. Contadores globais estritos serializam num store compartilhado; contadores locais frouxos admitem demais nas margens. Escolha por garantia, não por moda.
- 429s punem o loop de retry inocente. Clientes sem jitter se sincronizam em thundering herds; o design do seu limiter precisa assumir o pior cliente, porque vai encontrá-lo.
- Throttling esconde a sobrecarga. Requisições enfileiradas suavizam os gráficos enquanto acumulam backlog invisível; filas precisam de limites e políticas de descarte, ou viram atrasos de outage.
- Limites são decisões de produto vestidas de ops. Orçamentos, tiers e folgas de burst moldam experiência do usuário e receita — defina-os com a página de preços aberta, não só com o dashboard.
Rate limiting no Back4app
O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. Proteção é um padrão da plataforma, não um projeto seu: limites de requisição se aplicam na borda por app e por plano, operações caras podem ficar atrás de funções Cloud Code com checagens próprias, e os clientes recebem uma semântica 429 limpa — que o padrão de backoff no lado do SDK, nas abas de código acima, transforma em comportamento resiliente em vez de telas quebradas. Você ajusta políticas; não constrói o limiter.
Perguntas frequentes
O que é rate limiting de API?
É um controle que limita quantas requisições um cliente pode fazer dentro de uma janela de tempo — digamos, mil por hora por chave. Requisições acima do limite são rejeitadas, classicamente com HTTP 429 Too Many Requests. Ele protege o serviço contra sobrecarga e abuso, impede que um cliente pesado degrade a experiência dos demais e torna o planejamento de capacidade possível.
Qual a diferença entre rate limiting, throttling e cota?
Três ferramentas que costumam virar uma palavra só. Rate limiting rejeita o excesso de requisições na hora. Throttling desacelera ou enfileira em vez de rejeitar — a requisição completa, só que mais tarde. Cota é a franquia de longo prazo: requisições por dia ou mês, atrelada a um plano ou fatura. Janelas curtas protegem a infraestrutura; cotas definem o acordo comercial; throttling suaviza as bordas.
O que significa o erro 429 e como resolver?
Você excedeu as requisições permitidas na janela atual. A correção é disciplina no cliente: leia o header Retry-After, se presente, e espere esse tempo; caso contrário, faça retry com backoff exponencial mais jitter. A correção errada — retries cegos e imediatos — piora a situação para você e para todo mundo, que é exatamente o que o backoff existe para evitar.
Como funciona o algoritmo token bucket?
Um balde guarda tokens que são recarregados a uma taxa constante; cada requisição gasta um. Um balde cheio deixa um burst passar — o tamanho do balde é a folga de burst — enquanto a taxa de recarga impõe a média sustentada. Essa forma amigável a bursts é o motivo de o token bucket ser o algoritmo padrão para APIs voltadas a usuários.
Por que um rate limiter de janela fixa é problemático?
Pelo burst de fronteira: com limite de 100 por minuto, um cliente pode enviar 100 requisições às 11:59:59 e mais 100 às 12:00:01 — 200 em dois segundos, tudo dentro da regra. Algoritmos de janela deslizante ponderam a janela anterior para fechar a brecha, ao custo de um pouco mais de contabilidade. É o motivo clássico pelo qual "requisições por minuto" precisa de uma definição de minuto.
Quais são os headers de rate limit?
Primeiro a convenção: X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset informam ao cliente o orçamento, o que resta e quando ele renova — popularizados pelos grandes provedores de API. A padronização está chegando via os headers RateLimit e RateLimit-Policy do IETF. De um jeito ou de outro, publicar limites em headers é o que transforma rate limiting de um muro em um contrato.
O que é backoff exponencial com jitter?
O retry educado: espere 1s, depois 2s, 4s, 8s após falhas sucessivas — e some uma fatia aleatória (jitter) a cada espera. O jitter importa mais do que parece: sem ele, todos os clientes que falharam juntos tentam de novo juntos, martelando o serviço em recuperação em ondas sincronizadas. A aleatoriedade quebra o thundering herd.
Os limites devem ser por IP, por chave de API ou por usuário?
Em camadas, porque cada escopo falha sozinho: por IP barra enxurradas anônimas, mas pune escritórios atrás de um único endereço; por chave mapeia limites a aplicações e planos; por usuário impede que uma conta monopolize um app compartilhado; por endpoint protege operações caras especificamente. Sistemas de produção costumam combinar ao menos limites por chave e por endpoint.