Limites de uso
A API tem um orçamento de chamadas por minuto. Ele é da empresa, não da chave.
Quanto cada plano tem
Seção intitulada “Quanto cada plano tem”| Plano | API | Chamadas por minuto | Chaves ativas |
|---|---|---|---|
| Grátis | Não | 0 | 0 |
| Start | Sim | 60 | 3 |
| Pleno | Sim | 180 | 10 |
| Supra | Sim | 600 | 25 |
Esta tabela é gerada do código que decide de verdade: é a mesma função que a API chama a cada requisição para saber se a chamada passa.
O teto que vale para a sua empresa agora vem na resposta de GET /public/v1/me, em limits.requests_per_minute. Leia dali em vez de fixar o número no seu código: a empresa pode mudar de plano, e o painel também permite ajustar o valor de uma empresa em particular.
Troca de plano leva até um minuto para valer na API, porque o plano resolvido fica em memória por esse tempo.
Os cabeçalhos
Seção intitulada “Os cabeçalhos”Toda resposta que passa pelo contador traz três cabeçalhos:
| Cabeçalho | O que é | Como usar |
|---|---|---|
RateLimit-Limit | O teto por minuto da empresa | Compare com o que você planeja disparar |
RateLimit-Remaining | Quantas chamadas ainda cabem na janela | Abaixo de uma margem sua, segure o ritmo antes de tomar 429 |
RateLimit-Reset | Em quantos segundos abre a próxima vaga | Use como intervalo mínimo quando RateLimit-Remaining chegar a zero |
No 429 entra um quarto:
| Cabeçalho | O que é |
|---|---|
Retry-After | Quantos segundos esperar antes de tentar de novo |
A janela é deslizante: ela olha os últimos 60 segundos a partir de agora, não o minuto do relógio. Por isso o RateLimit-Reset cai aos poucos em vez de zerar de uma vez.
Empresa sem teto na aplicação não passa pelo contador, e aí esses cabeçalhos não aparecem. Nesse caso limits.requests_per_minute vem null em GET /public/v1/me.
O que fazer no 429
Seção intitulada “O que fazer no 429”{ "error": { "type": "rate_limit_error", "code": "rate_limit_exceeded", "message": "Limite de chamadas por minuto atingido. Tente de novo em instantes.", "request_id": "3d81b6ac-2f45-4a0e-9c7b-1e5f8a2d4c60", "doc_url": "https://docs.fatureihoje.com/errors#rate_limit_exceeded" }}- Espere o que o
Retry-Afterdiz. Ele vem em segundos e é calculado em cima da janela real, não é um palpite. - Recue progressivamente. Se o segundo 429 vier logo depois, dobre a espera a cada tentativa, até um teto seu.
- Espalhe um pouco. Some alguns milissegundos aleatórios à espera. Sem isso, várias filas que tomaram 429 no mesmo instante voltam juntas no mesmo instante.
- Nunca repita em laço apertado. Repetir na hora não adianta e ainda atrasa as chamadas boas da mesma empresa, que dividem o mesmo contador.
Chamada recusada por permissão é diferente: o 403 acontece depois do contador e gasta orçamento, de propósito. Sondar rota que a chave não pode usar não sai de graça.
Ler os cabeçalhos
Seção intitulada “Ler os cabeçalhos”curl -sS -D - -o /dev/null https://api.fatureihoje.com/public/v1/me \ -H "Authorization: Bearer $FH_API_KEY"-D - imprime os cabeçalhos e -o /dev/null joga fora o corpo.
const res = await fetch('https://api.fatureihoje.com/public/v1/me', { headers: { Authorization: `Bearer ${process.env.FH_API_KEY}` },});
console.log('limite', res.headers.get('RateLimit-Limit'));console.log('restam', res.headers.get('RateLimit-Remaining'));console.log('reabre em', res.headers.get('RateLimit-Reset'), 'segundos');
if (res.status === 429) { const espera = Number(res.headers.get('Retry-After') ?? 1); console.log('esperar', espera, 'segundos');}Rode com node limites.mjs.
<?php
$headers = [];$ch = curl_init('https://api.fatureihoje.com/public/v1/me');curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('FH_API_KEY')], CURLOPT_HEADERFUNCTION => function ($ch, $line) use (&$headers) { $parts = explode(':', $line, 2); if (count($parts) === 2) { $headers[strtolower(trim($parts[0]))] = trim($parts[1]); } return strlen($line); },]);
// curl_exec devolve false quando a conexão nem aconteceu, sem lançar nada.if (curl_exec($ch) === false) { fwrite(STDERR, 'falha de rede: ' . curl_error($ch) . PHP_EOL); exit(1);}
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo 'limite ', $headers['ratelimit-limit'] ?? '', PHP_EOL;echo 'restam ', $headers['ratelimit-remaining'] ?? '', PHP_EOL;echo 'reabre em ', $headers['ratelimit-reset'] ?? '', ' segundos', PHP_EOL;
if ($status === 429) { echo 'esperar ', $headers['retry-after'] ?? '1', ' segundos', PHP_EOL;}Rode com php limites.php. Sem curl_close: desde o PHP 8 o recurso é liberado sozinho, e no 8.5 a função ficou obsoleta.
import osimport urllib.errorimport urllib.request
request = urllib.request.Request( "https://api.fatureihoje.com/public/v1/me", headers={"Authorization": f"Bearer {os.environ['FH_API_KEY']}"},)
try: response = urllib.request.urlopen(request) status, headers = response.status, response.headersexcept urllib.error.HTTPError as failure: status, headers = failure.code, failure.headers
print("limite", headers.get("RateLimit-Limit"))print("restam", headers.get("RateLimit-Remaining"))print("reabre em", headers.get("RateLimit-Reset"), "segundos")
if status == 429: print("esperar", headers.get("Retry-After", "1"), "segundos")Rode com python3 limites.py.
Outros limites que também devolvem 429
Seção intitulada “Outros limites que também devolvem 429”O orçamento por minuto não é o único contador. A autenticação tem um teto próprio de falhas por IP: chave errada repetida muitas vezes no mesmo minuto passa a receber 429 mesmo antes de a chave ser conferida. Esse contador só conta o que deu errado, então uso normal não encosta nele. Está descrito em Autenticação.
Os dois usam o mesmo code, rate_limit_exceeded, e os dois trazem Retry-After. A diferença aparece no contexto: se as suas chamadas estão sendo aceitas e de repente vem 429, é o orçamento da empresa; se elas estão sendo recusadas com 401 e depois vira 429, é o teto de falhas.
Limite de chaves
Seção intitulada “Limite de chaves”A tabela acima também traz quantas chaves ativas cada plano permite. Chave revogada e chave expirada não contam; chave em convivência de rotação conta, porque ela ainda autentica.
Passar desse número devolve plan_limit_reached, com status 403. Rotacionar uma chave existente não esbarra nesse limite; criar mais uma, sim.