HTTP 429 Too Many Requests: Causas, Correções e Rate Limiting
Você está fazendo deploy numa sexta-feira à tarde (eu sei, eu sei), tudo parece estar bem, e aí seu sistema de monitoramento acende como uma árvore de Natal. Os usuários estão recebendo erros 429. Sua API está rejeitando requisições. Ou talvez seja o contrário — você está chamando uma API de terceiros e eles estão rejeitando você. De qualquer forma, o código de status HTTP 429 Too Many Requests acabou de se tornar a coisa mais importante do seu dia.
Eu já estive dos dois lados disso. Já fui o desenvolvedor que acidentalmente DDoS-eou uma API de CMS por causa de um build process mal configurado, e já fui aquele implementando rate limiting para proteger nossos próprios servidores de clientes fora de controle. Ambas as experiências me ensinaram coisas que a documentação não cobre. Vamos passar por tudo isso.
Índice
- O que HTTP 429 Realmente Significa?
- Causas Comuns de Erros 429
- O Header Retry-After Explicado
- Como Lidar com Erros 429 como Cliente
- Implementando Rate Limiting em Next.js API Routes
- Estratégias de Rate Limiting Comparadas
- Rate Limiting em Astro e Outros Frameworks
- Monitoramento e Debug de Erros 429 em Produção
- FAQ

O que HTTP 429 Realmente Significa?
HTTP 429 é definido em RFC 6585, publicado em 2012. A especificação é surpreendentemente curta. Aqui está o essencial: o usuário (ou cliente) enviou muitas requisições em um determinado período de tempo.
É só isso. É uma resposta de rate limiting. O servidor está dizendo: "Eu entendi sua requisição, ela provavelmente é válida, mas você precisa diminuir a velocidade."
Isso é diferente de um 403 Forbidden (você não é permitido) ou um 503 Service Unavailable (o servidor inteiro está com problemas). Um 429 é direcionado. É sobre sua taxa de requisição especificamente.
A resposta DEVE incluir um header Retry-After dizendo ao cliente quanto tempo esperar antes de tentar novamente. Eu disse "deve" porque muitas APIs não se incomodam, o que complica a vida de todos.
Onde Você Verá 429s na Prática
- APIs de terceiros: Stripe, OpenAI, GitHub, Contentful, Sanity — todas têm rate limits
- CDNs e plataformas de hosting: Vercel, Cloudflare e AWS retornarão 429s se você atingir seus rate limits de edge
- Suas próprias APIs: Se você implementou rate limiting (e deveria ter)
- Processos de build: Geração estática de sites que chama uma API de CMS para cada página pode facilmente acionar rate limits
- Web scraping: Se você está buscando dados de fontes externas agressivamente
Causas Comuns de Erros 429
Vou detalhar os cenários que realmente encontrei em produção, classificados mais ou menos por frequência.
1. Builds de Sites Estáticos Bombardeando um Headless CMS
Este é aquele que mais afeta times trabalhando com arquiteturas headless. Você tem um site com 2.000 páginas, cada uma precisando de dados do seu CMS. Seu processo de build dispara todas aquelas requisições em paralelo, o CMS vê um pico massivo e começa a retornar 429s. Seu build falha.
Vemos isso regularmente ao trabalhar em projetos de headless CMS. A correção envolve fila de requisições e limites de concorrência, que vou cobrir abaixo.
2. Cache Faltando ou Quebrado
Se cada carregamento de página dispara uma chamada de API fresca porque sua camada de cache não está funcionando, você atingirá rate limits rápido — especialmente com picos de tráfego. Uma vez debuguei um app Next.js onde revalidate foi acidentalmente definido como 0, significando que ISR estava efetivamente desabilitado. Cada visitante disparava uma nova chamada de API para Contentful. Levou cerca de 45 minutos de tráfego real para começar a receber 429s.
3. Loops de Retry Sem Backoff
Seu código recebe um erro, tenta novamente imediatamente, recebe outro erro, tenta novamente imediatamente... parabéns, você construiu uma máquina de disparo de rate-limit. Já vi este padrão em manipuladores de webhook, trabalhos em background e até chamadas fetch do lado do cliente.
4. Múltiplos Serviços Compartilhando uma Chave de API
Seu ambiente de staging, seu ambiente de produção, sua configuração de dev local e seu pipeline CI/CD estão todos usando a mesma chave de API. Cada um parece bom individualmente, mas coletivamente estão queimando seu orçamento de rate limit.
5. Fetch do Lado do Cliente Sem Debouncing
Um recurso de busca-conforme-digita que dispara uma chamada de API a cada tecla pressionada. Um dashboard que faz polling a cada 500ms. Um scroll infinito que dispara buscas mais rápido do que o usuário pode scrollar. Esses padrões podem absolutamente disparar 429s, especialmente quando multiplicados em todos seus usuários.
6. Abuso Ou Ataque Real
Às vezes um 429 está fazendo exatamente o que deveria — protegendo seu servidor de alguém enviando um número irrazoável de requisições. Bots, credential stuffing, scraping — rate limiting é sua primeira linha de defesa.
O Header Retry-After Explicado
O header Retry-After é a maneira do servidor de dizer exatamente quando tentar novamente. Pode vir em dois formatos:
Segundos para esperar:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Data/hora específica:
HTTP/1.1 429 Too Many Requests
Retry-After: Thu, 01 Jan 2026 00:00:00 GMT
O formato de segundos é muito mais comum. O formato de data usa HTTP-date conforme definido em RFC 7231.
Aqui está o que a maioria dos tutoriais não vai te dizer: muitas APIs não enviam Retry-After de forma alguma, ou o enviam inconsistentemente. A API do OpenAI geralmente o inclui. A API do GitHub o inclui junto com X-RateLimit-Reset. Muitas APIs menores apenas enviam um 429 nu e deixam você adivinhar.
Algumas APIs também enviam headers adicionais de rate limit:
| Header | Propósito | Exemplo |
|---|---|---|
X-RateLimit-Limit |
Máximo de requisições permitidas por janela | 100 |
X-RateLimit-Remaining |
Requisições restantes na janela atual | 0 |
X-RateLimit-Reset |
Timestamp Unix quando a janela reseta | 1735689600 |
Retry-After |
Segundos para esperar antes de tentar novamente | 30 |
Sempre verifique esses headers. Eles permitem que você implemente lógica de retry mais inteligente e até mesmo desacelere proativamente antes de atingir o limite.

Como Lidar com Erros 429 como Cliente
Quando você é aquele recebendo erros 429, aqui está como lidar com eles adequadamente.
Exponential Backoff com Jitter
Este é o padrão ouro. Não apenas espere um tempo fixo — aumente o atraso exponencialmente a cada tentativa e adicione um pouco de aleatoriedade (jitter) para evitar problemas de thundering herd.
async function fetchWithRetry(
url: string,
options: RequestInit = {},
maxRetries: number = 5
): Promise<Response> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
if (attempt === maxRetries) {
throw new Error(`Still getting 429 after ${maxRetries} retries`);
}
// Check for Retry-After header first
const retryAfter = response.headers.get('Retry-After');
let delay: number;
if (retryAfter) {
// Could be seconds or a date
const parsed = parseInt(retryAfter, 10);
if (!isNaN(parsed)) {
delay = parsed * 1000;
} else {
delay = new Date(retryAfter).getTime() - Date.now();
}
} else {
// Exponential backoff with jitter
const baseDelay = Math.pow(2, attempt) * 1000;
const jitter = Math.random() * 1000;
delay = baseDelay + jitter;
}
console.log(`Rate limited. Waiting ${Math.round(delay / 1000)}s before retry ${attempt + 1}`);
await new Promise(resolve => setTimeout(resolve, delay));
}
// TypeScript wants this, though we'll never reach it
throw new Error('Unexpected end of retry loop');
}
Request Queuing para Build Processes
Para geração estática de sites onde você precisa fazer centenas ou milhares de chamadas de API, use uma fila com controle de concorrência:
import pLimit from 'p-limit';
// Limit to 5 concurrent requests
const limit = pLimit(5);
const pages = await getAllPageSlugs(); // Returns ['/', '/about', '/blog/post-1', ...]
const results = await Promise.all(
pages.map(slug =>
limit(() => fetchWithRetry(`https://api.cms.com/pages/${slug}`))
)
);
A biblioteca p-limit (2.5M+ downloads semanais no npm em 2025) é minha escolha preferida para isso. Você também pode adicionar um atraso entre requisições:
const limit = pLimit(3);
const delay = (ms: number) => new Promise(r => setTimeout(r, ms));
const results = await Promise.all(
pages.map((slug, i) =>
limit(async () => {
if (i > 0) await delay(200); // 200ms between requests
return fetchWithRetry(`https://api.cms.com/pages/${slug}`);
})
)
);
Implementando Rate Limiting em Next.js API Routes
Agora vamos virar para o outro lado — você está construindo uma API e precisa protegê-la. Se você está construindo com Next.js, aqui está como adicionar rate limiting às suas rotas de API.
Rate Limiter Simples em Memória
Para uma implantação de servidor único ou durante desenvolvimento, isso funciona:
// lib/rate-limit.ts
type RateLimitEntry = {
count: number;
resetTime: number;
};
const rateLimitMap = new Map<string, RateLimitEntry>();
export function rateLimit({
windowMs = 60 * 1000,
maxRequests = 100,
}: {
windowMs?: number;
maxRequests?: number;
} = {}) {
return function check(identifier: string): {
allowed: boolean;
remaining: number;
resetIn: number;
} {
const now = Date.now();
const entry = rateLimitMap.get(identifier);
if (!entry || now > entry.resetTime) {
rateLimitMap.set(identifier, {
count: 1,
resetTime: now + windowMs,
});
return { allowed: true, remaining: maxRequests - 1, resetIn: windowMs };
}
if (entry.count >= maxRequests) {
return {
allowed: false,
remaining: 0,
resetIn: entry.resetTime - now,
};
}
entry.count++;
return {
allowed: true,
remaining: maxRequests - entry.count,
resetIn: entry.resetTime - now,
};
};
}
Usando em uma rota de API do Next.js App Router:
// app/api/data/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { rateLimit } from '@/lib/rate-limit';
const limiter = rateLimit({ windowMs: 60_000, maxRequests: 30 });
export async function GET(request: NextRequest) {
const ip = request.headers.get('x-forwarded-for') ?? 'anonymous';
const { allowed, remaining, resetIn } = limiter(ip);
if (!allowed) {
return NextResponse.json(
{ error: 'Too many requests. Please slow down.' },
{
status: 429,
headers: {
'Retry-After': String(Math.ceil(resetIn / 1000)),
'X-RateLimit-Limit': '30',
'X-RateLimit-Remaining': '0',
},
}
);
}
// Your actual route logic here
return NextResponse.json(
{ data: 'Here you go' },
{
headers: {
'X-RateLimit-Limit': '30',
'X-RateLimit-Remaining': String(remaining),
},
}
);
}
Rate Limiting em Produção com Upstash Redis
A abordagem em memória quebra quando você está rodando em plataformas serverless como Vercel, porque cada invocação de função pode acertar uma instância diferente. Você precisa de um armazenamento compartilhado. Upstash Redis é a escolha mais popular para isso em 2025.
npm install @upstash/ratelimit @upstash/redis
// lib/rate-limit.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
export const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(30, '60 s'),
analytics: true,
prefix: 'api-ratelimit',
});
// app/api/data/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { ratelimit } from '@/lib/rate-limit';
export async function GET(request: NextRequest) {
const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1';
const { success, limit, remaining, reset } = await ratelimit.limit(ip);
if (!success) {
const retryAfter = Math.ceil((reset - Date.now()) / 1000);
return NextResponse.json(
{ error: 'Rate limit exceeded' },
{
status: 429,
headers: {
'Retry-After': String(retryAfter),
'X-RateLimit-Limit': String(limit),
'X-RateLimit-Remaining': '0',
'X-RateLimit-Reset': String(reset),
},
}
);
}
return NextResponse.json({ data: 'Success' }, {
headers: {
'X-RateLimit-Limit': String(limit),
'X-RateLimit-Remaining': String(remaining),
},
});
}
O tier gratuito do Upstash oferece 10.000 requisições/dia, o que é suficiente para pequenos projetos. Seu plano Pro começa em $10/mês por 500K comandos diários a partir do início de 2025.
Rate Limiting em Nível de Middleware
Se você quer rate limiting em todas as suas rotas de API, Next.js middleware é o lugar:
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { ratelimit } from '@/lib/rate-limit';
export async function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/api/')) {
const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1';
const { success, reset } = await ratelimit.limit(ip);
if (!success) {
return NextResponse.json(
{ error: 'Too many requests' },
{
status: 429,
headers: {
'Retry-After': String(Math.ceil((reset - Date.now()) / 1000)),
},
}
);
}
}
return NextResponse.next();
}
export const config = {
matcher: '/api/:path*',
};
Estratégias de Rate Limiting Comparadas
Nem todos os algoritmos de rate limiting são iguais. Aqui está como os principais se comparam:
| Algoritmo | Como Funciona | Vantagens | Desvantagens | Melhor Para |
|---|---|---|---|---|
| Fixed Window | Conta requisições em janelas de tempo fixas (ex: por minuto) | Simples de implementar | Rajada no limite da janela pode permitir 2x o limite | APIs simples, ferramentas internas |
| Sliding Window | Conta requisições sobre um período de tempo rolante | Distribuição mais suave | Ligeiramente mais complexo, mais memória | A maioria das APIs em produção |
| Token Bucket | Tokens são recarregados a taxa constante, cada requisição custa um token | Permite rajadas controladas | Gerenciamento de estado mais complexo | APIs que precisam de tolerância a rajadas |
| Leaky Bucket | Requisições entram em uma fila e são processadas a taxa fixa | Taxa de saída muito suave | Pode adicionar latência, requisições podem ser descartadas | Entrega de webhooks, processamento de trabalhos |
| Sliding Window Log | Armazena timestamp de cada requisição | Mais preciso | Alto uso de memória em escala | Necessidades de baixo volume e alta precisão |
Para a maioria das aplicações web, sliding window é o ponto ideal. É o que o Upstash usa por padrão, e é o que eu recomendaria a menos que você tenha uma razão específica para escolher algo diferente.
Rate Limiting em Astro e Outros Frameworks
Se você está construindo com Astro, rate limiting funciona diferentemente porque Astro é primariamente um framework estático-first. Mas com endpoints do servidor do Astro (disponíveis em modo SSR), os conceitos são os mesmos:
// src/pages/api/data.ts
import type { APIRoute } from 'astro';
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(30, '60 s'),
});
export const GET: APIRoute = async ({ request }) => {
const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1';
const { success, reset } = await ratelimit.limit(ip);
if (!success) {
return new Response(JSON.stringify({ error: 'Rate limit exceeded' }), {
status: 429,
headers: {
'Content-Type': 'application/json',
'Retry-After': String(Math.ceil((reset - Date.now()) / 1000)),
},
});
}
return new Response(JSON.stringify({ data: 'Hello' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
};
Para aplicações implantadas em edge na Cloudflare Workers, você também pode considerar as regras de Rate Limiting built-in do Cloudflare, que operam no nível da infraestrutura e podem lidar com muito mais tráfego do que soluções de nível de aplicação. Seu Advanced Rate Limiting começa em $0.05 por 10.000 requisições boas no plano Business.
Monitoramento e Debug de Erros 429 em Produção
Você não pode corrigir o que não consegue ver. Aqui está minha checklist para lidar com erros 429 em produção:
Quando Você Está Recebendo 429s
- Verifique qual API está retornando 429 — Olhe para a URL da resposta, não apenas o status
- Registre o header
Retry-After— Se consistentemente muito longo, você pode precisar de um plano de tier superior - Audite seus padrões de requisição — Você está fazendo chamadas redundantes? Pode fazer batch de requisições?
- Implemente cache — Use
stale-while-revalidate, cache Redis ou Next.js ISR para reduzir chamadas de API - Verifique se múltiplos ambientes compartilham chaves de API — Esta é a causa mais comum de 429 "mistério"
Quando Você Está Enviando 429s
- Configure dashboards — Rastreie taxa de respostas 429 ao longo do tempo
- Identifique infratores principais — Quais endereços IP ou chaves de API estão atingindo limites com mais frequência?
- Revise seus limites — Eles são muito restritivos? Muito soltos? Verifique sua capacidade de servidor e ajuste
- Sempre envie
Retry-After— Seja um cidadão API bom - Inclua uma mensagem de erro útil — Diga ao cliente qual limite eles atingiram e quando tentar novamente
Uma resposta 429 bem-elaborada se parece com isso:
{
"error": {
"type": "rate_limit_exceeded",
"message": "You've exceeded 30 requests per minute. Please wait before retrying.",
"retryAfter": 42,
"documentation": "https://docs.yourapi.com/rate-limits"
}
}
Isso é infinitamente mais útil do que apenas { "error": "Too many requests" }.
Se você está lidando com problemas persistentes de rate limiting em uma arquitetura headless — seja durante builds, em tempo de execução ou ambos — pode valer a pena entrar em contato para discutir sua arquitetura. Vimos muitos desses problemas em diferentes combinações de CMS e framework e geralmente há uma correção de nível de padrão em vez de apenas colocar um band-aid nos sintomas.
FAQ
O que significa HTTP 429 Too Many Requests?
HTTP 429 é um código de status que significa que você enviou muitas requisições para um servidor dentro de um período de tempo determinado. O servidor está fazendo rate limiting você — está pedindo para você diminuir a velocidade. Não é um erro de autenticação ou um erro do servidor; suas requisições provavelmente são válidas, há apenas muitas delas. O servidor deve incluir um header Retry-After dizendo quando tentar novamente.
Como corrijo um erro 429?
Se você está recebendo erros 429 de uma API, implemente exponential backoff com jitter em sua lógica de retry, reduza sua frequência de requisição, adicione cache para evitar chamadas redundantes e respeite o header Retry-After. Se está atingindo o limite durante builds, use fila de requisição com limites de concorrência. Se está acontecendo consistentemente, você pode precisar fazer upgrade para um plano de API mais generoso com rate limits mais altos.
O que é o header Retry-After?
O header Retry-After é enviado com uma resposta 429 (ou 503) para dizer ao cliente quanto tempo esperar antes de fazer outra requisição. Pode ser especificado como um número de segundos (ex: Retry-After: 60) ou como uma data HTTP (ex: Retry-After: Thu, 01 Jan 2026 00:00:00 GMT). Nem todas as APIs incluem este header, mas as bem projetadas sim.
Como implemento rate limiting em Next.js?
Para desenvolvimento ou implantações em servidor único, você pode usar um Map em memória para rastrear contagens de requisição por endereço IP. Para implantações serverless em produção em plataformas como Vercel, use Upstash Redis com o pacote @upstash/ratelimit. Você pode aplicar rate limiting no nível de rota individual ou em todas as rotas de API usando Next.js middleware.
Qual é a diferença entre erros 429 e 503?
Um 429 Too Many Requests é especificamente sobre rate limiting — seu cliente está enviando muitas requisições. Um 503 Service Unavailable significa que o servidor está sobrecarregado ou em manutenção e não consegue lidar com nenhuma requisição de ninguém. Ambos podem incluir um header Retry-After, mas indicam problemas muito diferentes. Um 429 é direcionado a você; um 503 afeta a todos.
Rate limiting pode prevenir ataques DDoS?
Rate limiting é uma camada de defesa contra ataques DDoS, mas não é suficiente por si só. Rate limiting em nível de aplicação (como o que você implementaria em Next.js) pode lidar com abuso moderado, mas um ataque DDoS sério precisa ser mitigado em nível de infraestrutura — usando serviços como Cloudflare, AWS Shield ou proteções built-in do seu provedor de hosting. Pense em rate limiting de nível de app como um porteiro, e proteção em nível de infraestrutura como os muros da fortaleza.
Que rate limit devo definir para minha API?
Depende completamente do seu caso de uso. Um ponto de partida comum para APIs públicas é 60 requisições por minuto por IP, ou 1.000 requisições por hora por chave de API. Para usuários autenticados, você pode permitir mais. A chave é monitorar padrões de uso real, definir limites que acomodem uso legítimo com alguma margem e ajustar com base em dados reais. Comece mais restritivo e afrouxe — é mais fácil do que apertar limites depois que os usuários dependem de taxas mais altas.
Por que estou recebendo erros 429 durante meu build de site estático?
Geradores de sites estáticos como Next.js e Astro buscam dados para cada página em tempo de build. Se você tiver centenas ou milhares de páginas, isso são centenas ou milhares de chamadas de API em rápida sucessão. A maioria das APIs de CMS tem rate limits entre 5-20 requisições por segundo. Use p-limit ou bibliotecas similares para limitar concorrência a 3-5 requisições simultâneas, adicione pequenos atrasos entre lotes e considere usar builds incrementais (ISR em Next.js ou Astro's incremental content collections) para evitar reconstruir tudo de uma vez.