Limites de uso
Os limites são aplicados por credencial, debaixo de um teto da conta, e por categoria de rota, numa janela móvel de 60 segundos.
| Categoria | Rotas | Limite |
|---|---|---|
| Envio de mensagens | POST /messages e POST /v1/.../messages | 500 req/min |
| Upload de mídia | POST /v1/.../media | 60 req/min |
| Consultas | todo o resto (/templates, /events, demais /v1/*) | 60 req/min |
Os valores da tabela são o padrão. O seu plano pode definir limites próprios por categoria, e aí valem os do plano.
Não têm limite: /plans e /health.
Por chave, com um teto da conta
Cada chave de API tem o seu próprio balde, do tamanho que o plano define — e cada pessoa
conectada ao painel também. Uma integração presa num loop esgota só a própria cota: a
chave do plugin do WordPress martelando /v1 não deixa a chave do seu ERP em 429.
Acima dos baldes das chaves, a conta tem um teto por categoria, por padrão 3 vezes o limite do plano. É ele que impede que criar mais chaves multiplique a cota sem fim: com o envio em 500/min, uma chave sozinha chega a 500 envios por minuto, e todas as chaves da conta juntas, a 1.500.
O painel fica fora desse teto: quem está conectado gasta só o próprio balde. Uma chave em loop pode esgotar o teto da conta, mas não tranca você fora do painel — que é onde você encontra e revoga essa chave.
Headers de controle
Toda resposta de rota limitada traz o estado atual do balde:
| Header | Conteúdo |
|---|---|
X-RateLimit-Category | send, media ou default |
X-RateLimit-Scope | O balde descrito: key (chave de API), session (painel) ou account |
X-RateLimit-Limit | Teto desse balde na janela |
X-RateLimit-Remaining | Quanto ainda resta |
Numa resposta bem-sucedida, os headers descrevem o balde da sua credencial. Num 429,
descrevem o balde que recusou — que pode ser o da conta.
HTTP/1.1 201 Created
X-RateLimit-Category: send
X-RateLimit-Scope: key
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 487
Monitore X-RateLimit-Remaining e desacelere antes de bater no teto — é mais barato que
tratar 429.
Quando o limite estoura
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Category: send
X-RateLimit-Scope: key
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 0
{
"error": {
"message": "Rate limit exceeded for send requests with this credential. Try again in 12s.",
"type": "rate_limit",
"scope": "key"
}
}
O campo error.scope repete o X-RateLimit-Scope. Quando ele vem account, a sua chave
ainda tinha cota: foram as outras chaves da conta, somadas, que esgotaram o teto.
Distribuir as chamadas por mais chaves não resolve — só esperar.
O header Retry-After diz exatamente quantos segundos esperar. Respeite esse valor —
insistir antes só consome cota.
Tratando o 429
async function enviar(payload, tentativa = 0) {
const response = await fetch('https://api.joinotify.com/messages', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.JOINOTIFY_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (response.status === 429 && tentativa < 3) {
const espera = Number(response.headers.get('retry-after') ?? 5);
await new Promise((resolve) => setTimeout(resolve, espera * 1000));
return enviar(payload, tentativa + 1);
}
return response.json();
}
Para disparos em massa, prefira uma fila com taxa controlada (por exemplo 8 mensagens por segundo, bem abaixo das 500/min) em vez de disparar tudo e reagir ao erro.
Os limites da Meta também valem
Os números acima são os limites do Joinotify. Por baixo, a Meta impõe os dela — o limite de mensagens iniciadas pela empresa em 24 horas, que varia conforme a qualidade e o nível de verificação do seu número.
Quando é a Meta que recusa, a resposta vem como meta_error com o corpo original dela em
error.meta, e não como rate_limit.
Precisa de mais?
Os limites são configuráveis por conta. Se o seu volume exige mais, fale com o suporte com uma estimativa de pico por minuto.