Como lidar com ratelimits na API do Discord
Como lidar com ratelimits na API do Discord: O Guia Definitivo
Se você desenvolve bots para o Discord, mais cedo ou mais tarde sua aplicação receberá um código de status HTTP 429 (Too Many Requests). O Discord impõe regras rígidas de tráfego para proteger sua infraestrutura contra abusos e ataques de negação de serviço.
Para que seu bot opere com estabilidade máxima, você precisa entender a mecânica interna desses limites e blindar seu código com um sistema de filas e cache.
1. Anatomia dos Ratelimits do Discord
O Discord divide suas restrições em camadas bem definidas. Os limites são contados com base no token do seu bot e no IP de onde as requisições partem.
O Limite Global (Global Rate Limit)
Por padrão, o Discord aplica um limite global de 50 requisições por segundo para cada token de bot. Se o seu bot tentar disparar 51 requisições em um único segundo (mesmo que para endpoints totalmente diferentes), a API bloqueará temporariamente todas as chamadas subsequentes.
Limites por Rota e Buckets (Per-Route Limits)
Além do limite global, cada rota da API possui seu próprio "bucket" (balde) de créditos, geralmente atrelado a um identificador principal (como o ID de um servidor ou de um canal).
- Rotas comuns: Enviar mensagens em um canal específico possui um limite diferente de editar o apelido de um usuário.
- A rota crítica de Criação/Edição de Canais: Modificar ou criar canais em um servidor é uma das operações mais monitoradas pelo Discord para evitar ataques de raid. Modificações no nome ou tópico de um canal, por exemplo, são estritamente limitadas a 2 alterações a cada 10 minutos por canal. Abusar dessa rota gera bloqueios severos instantaneamente.
Os Cabeçalhos HTTP de Controle
Sempre que seu bot faz uma requisição REST, o Discord responde com cabeçalhos que revelam a saúde do seu bucket atual. Seu código deve ler e respeitar esses dados:
*X-RateLimit-Limit: O número máximo de requisições que você pode fazer neste bucket.
*X-RateLimit-Remaining: Quantas requisições você ainda pode fazer antes do bloqueio.
*X-RateLimit-Reset-After: O tempo (em segundos) que você precisa esperar para o bucket resetar.
*X-RateLimit-Bucket: A string identificadora única daquele bucket específico.
2. A Solução Arquitetural: Sistema de Filas
A forma mais eficiente de evitar atingir o limite do Discord é implementar um Sistema de Filas Assíncronas. Em vez de permitir que seus comandos executem requisições REST diretamente para a API do Discord à medida que as interações ocorrem, você joga essas ações em uma fila controlada por um trabalhador (worker).
O worker consome a fila sequencialmente ou em lotes, verificando os cabeçalhos de ratelimit antes de realizar o próximo disparo. Se o indicador X-RateLimit-Remaining chegar a zero, a fila congela voluntariamente durante os segundos informados em X-RateLimit-Reset-After.
Exemplo Conceitual de Fila Otimizada (Python / Asyncio)
Aqui está um exemplo básico de como estruturar um despachante de requisições que respeita os tempos de espera impostos pela API:
import asyncio
import time
class DiscordRequestQueue:
def __init__(self):
self.queue = asyncio.Queue()
self.is_frozen = False
self.resume_time = 0
async def add_request(self, action_coroutine):
"""Adiciona uma ação de API à fila"""
await self.queue.put(action_coroutine)
async def start_worker(self):
"""Worker que processa e monitora os limites continuamente"""
while True:
# Se a fila estiver congelada por ratelimit, aguarda o reset
if self.is_frozen:
now = time.time()
if now < self.resume_time:
await asyncio.sleep(self.resume_time - now)
self.is_frozen = False
# Pega a próxima requisição da fila
action = await self.queue.get()
try:
# Executa a chamada HTTP para o Discord
response = await action()
# Simulação da leitura dos cabeçalhos do Discord
remaining = int(response.headers.get("X-RateLimit-Remaining", 1))
reset_after = float(response.headers.get("X-RateLimit-Reset-After", 0))
if remaining == 0:
# Congela o processador baseado na resposta do Discord
self.is_frozen = True
self.resume_time = time.time() + reset_after
except Exception as e:
# Trata erros 429 inesperados com Recuo Exponencial (Backoff)
print(f"Erro na requisição: {e}")
finally:
self.queue.task_done()
3. Outras Boas Práticas Cruciais
Além de gerenciar suas requisições REST com filas, adote os seguintes hábitos de desenvolvimento para reduzir a carga contra a API:
1. Cache Agressivo de Dados
Nunca consulte a API do Discord para obter informações estáticas ou que mudam pouco.
- Dados de Servidores: Mantenha os nomes dos canais, papéis (roles) e permissões salvos na memória local do seu bot (ou em uma instância externa do Redis).
- Biblioteca Nativa: Se você usa wrappers modernos como
discord.pyoudiscord.js, eles já possuem sistemas de cache interno automáticos. Evite contorná-los usando métodos comofetch_channel()(que faz uma chamada REST direta) quando puder usarget_channel()(que busca do cache local).
2. Dê Preferência ao Gateway e Webhooks
- Gateway (WebSockets): Use eventos do Gateway para receber atualizações em tempo real em vez de fazer "polling" (ficar perguntando para a API se algo mudou).
- Webhooks para Envio Massivo: Se o seu bot precisa disparar logs de ações, anúncios em massa ou feeds de notícias em canais, utilize Webhooks. Os Webhooks possuem limites de requisições isolados da sua aplicação principal (geralmente 30 requisições a cada 5 segundos por webhook), poupando o limite de 50/s global do token do seu bot.
3. Implemente Exponential Backoff (Recuo Exponencial)
Se por algum desalinhamento de concorrência seu bot receber um erro HTTP 429 real, sua rotina de tentativas (retry) deve recuar estrategicamente. Multiplique o tempo de espera a cada falha consecutiva. Se você bombardear a API imediatamente após receber um 429, o Discord estenderá a punição ou banirá temporariamente o IP da sua VPS/Hospedagem por comportamento abusivo.
Atualizado em: 18/05/2026
Obrigado!
