Aparência
Limites de uso
Os limites protegem a API para todo mundo e dependem do plano da empresa. Cada credencial tem a sua própria cota: uma integração que consome muito não atrapalha as outras credenciais da empresa.
Limites por plano
| Plano | Por minuto | Rajada | Por dia |
|---|---|---|---|
| Premium | 120 chamadas | até 60 chamadas de uma vez | 100.000 chamadas |
| Enterprise | 600 chamadas | até 300 chamadas de uma vez | 1.000.000 de chamadas |
Cada chamada conta como uma, seja uma página de 100 itens ou um único recurso.
Melhor esforço
Os limites são aplicados pelo gateway da API em regime de melhor esforço: números muito próximos do teto podem passar ou ser barrados com pequena diferença. Planeje com folga em vez de rodar colado no limite.
Existe ainda uma proteção por endereço IP: acima de 6.000 requisições em 5 minutos a partir do mesmo IP, o gateway bloqueia as chamadas, independentemente do plano. Essa resposta de bloqueio vem do firewall e não é uma das respostas de erro documentadas em Erros.
Cabeçalhos de limite
Toda resposta autenticada traz os limites do seu plano:
| Cabeçalho | Exemplo | Significado |
|---|---|---|
X-RateLimit-Limit | 120 | Chamadas permitidas por minuto. |
X-RateLimit-Policy | 120;w=60, 100000;w=86400 | Limite por minuto (janela de 60 s) e por dia (janela de 86400 s). |
Os valores são fixos do plano. Não existe cabeçalho com "chamadas restantes" nem com a hora do reinício: se precisar saber quanto já gastou, conte no seu lado.
Quando o limite estoura: 429
Passou do limite por minuto ou da cota diária? A API responde 429 com o código rate_limited e o cabeçalho Retry-After: 60:
http
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 60json
{
"type": "https://developers.festero.com.br/errors#rate_limited",
"title": "Too Many Requests",
"status": 429,
"detail": "Rate limit per minute exceeded. Retry after the interval in Retry-After.",
"code": "rate_limited",
"requestId": "6f1c2a9e-0b7d-4c1e-9a52-3e8f7d1c0a44"
}O texto de detail informa qual limite estourou: Rate limit per minute exceeded... para o limite por minuto, ou Daily quota exceeded... para a cota do dia.
- Limite por minuto: espere o tempo do
Retry-After(60 segundos) e continue num ritmo menor. - Cota diária: o
Retry-Afterde 60 segundos é um valor fixo do gateway. Como a cota é diária, esperar um minuto não resolve; reduza o volume diário de chamadas.
Boas práticas
- Use
sales-summaryem vez de paginar tudo. Para saber quanto foi vendido e recebido num período, uma chamada aGET /v1/finance/sales-summarysubstitui centenas de páginas de eventos e recebíveis. - Sincronize só o que mudou. Guarde o instante da última sincronização e use
updatedSince. Veja Paginação e datas. - Peça páginas grandes. Com
limit=100você faz um quarto das chamadas que faria com o padrão de 25. - Prefira webhooks a consultas repetidas. Em vez de perguntar a cada minuto se algo mudou, deixe o Festero avisar: Webhooks.
- Reaproveite o token. Ele vale 60 minutos. Veja Autenticação.
- Use
ETag. Ao reler um recurso individual, envie oETagrecebido emIf-None-Match; se nada mudou, a resposta é304, sem corpo. - Respeite o
Retry-Aftere use espera crescente. Em429,500,502e504, tente de novo esperando mais a cada tentativa. - Evite rajadas paralelas. Enviar centenas de chamadas ao mesmo tempo esgota a rajada mesmo com a média baixa.
Exemplo: repetir com espera crescente
js
async function chamar(url, token, tentativas = 5) {
for (let tentativa = 1; ; tentativa++) {
const resposta = await fetch(url, { headers: { Authorization: `Bearer ${token}` } })
const repetivel = [429, 500, 502, 504].includes(resposta.status)
if (!repetivel || tentativa === tentativas) return resposta
// Em 429, o gateway diz quanto esperar. Nos demais casos, espera crescente: 1 s, 2 s, 4 s...
const retryAfter = Number(resposta.headers.get('Retry-After'))
const esperaMs = retryAfter > 0 ? retryAfter * 1000 : 2 ** (tentativa - 1) * 1000
await new Promise((resolver) => setTimeout(resolver, esperaMs))
}
}