# Rate Limiting

> Limites de requisições por plano e como lidar com throttling.

URL: https://docs.fiscalapi.com.br/docs/referencia/rate-limiting


Toda conta possui limites de requisições baseados no plano da assinatura. O rate limiting protege a infraestrutura e garante disponibilidade para todos os usuários.

O limite é por **conta**, não por chave: se você usa várias API Keys, as requisições de todas elas somam no mesmo limite por minuto.

## ## Limites por plano

| Plano      | Requisições | Período    |
| ---------- | ----------- | ---------- |
| Lite       | 5           | por minuto |
| Starter    | 20          | por minuto |
| Pro        | 60          | por minuto |
| Enterprise | 120         | por minuto |

## ## Endpoints com rate limiting

Os seguintes endpoints estão sujeitos ao rate limiting:

* `GET /api/consultar`
* `GET /api/consultar-cnpj`
* `GET /api/consultar-simples-nacional`
* `GET /api/consultar-ie-todos`
* `GET /api/perfil-produtor`
* `GET /api/consultar-cnd`
* `GET /api/consultar-cnd-todos`

<Callout type="info">
  Toda requisição conta para o rate limit, inclusive quando a resposta vem do cache. A vantagem do cache é a latência (resposta em milissegundos, sem ida ao portal de origem).
</Callout>

## ## Resposta ao exceder o limite

Quando o limite é excedido, a API retorna HTTP `429 Too Many Requests` com o header `Retry-After` indicando quantos segundos aguardar antes da próxima requisição.

```json
{
  "type": "urn:fiscalapi:error:rate_limit_exceeded",
  "title": "RATE_LIMIT_EXCEEDED",
  "status": 429,
  "detail": "Limite de requisicoes excedido. Tente novamente em 45 segundos."
}
```

## ## Headers de rate limit

As respostas dos endpoints listados acima incluem headers informativos sobre o estado do seu rate limit — tanto quando a requisição passa quanto no 429:

| Header                  | Descrição                                          |
| ----------------------- | -------------------------------------------------- |
| `X-RateLimit-Limit`     | Número máximo de requisições permitidas por minuto |
| `X-RateLimit-Remaining` | Número de requisições restantes na janela atual    |
| `X-RateLimit-Reset`     | Timestamp Unix de quando a janela será reiniciada  |
| `Retry-After`           | Segundos para aguardar (somente em respostas 429)  |

## ## Boas práticas

* **Implemente backoff exponencial** ao receber respostas 429 — comece com o valor do `Retry-After` e dobre o tempo a cada tentativa
* **Respeite o header `Retry-After`** em vez de usar um valor fixo de espera
* **Use cache local** para evitar chamadas duplicadas e reduzir o consumo da API
* **Considere fazer upgrade de plano** se você atingir o limite com frequência
* **Monitore seu uso** pelo [Dashboard](https://app.fiscalapi.com.br?utm_source=docs\&utm_medium=docs\&utm_campaign=rate-limiting) para antecipar necessidades de escala

## ## Exemplo de retry com backoff

<Tabs items="[&#x22;JavaScript&#x22;, &#x22;Python&#x22;]">
  <Tab value="JavaScript">
    ```javascript
    async function fetchWithRetry(url, headers, maxRetries = 3) {
      for (let attempt = 0; attempt < maxRetries; attempt++) {
        const response = await fetch(url, { headers });

        if (response.status !== 429) {
          return response;
        }

        const retryAfter = parseInt(response.headers.get("Retry-After") || "60");
        const backoff = retryAfter * Math.pow(2, attempt);
        console.log(`Rate limited. Aguardando ${backoff}s antes de tentar novamente...`);
        await new Promise((r) => setTimeout(r, backoff * 1000));
      }

      throw new Error("Limite de tentativas excedido");
    }
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import requests
    import time

    def fetch_with_retry(url: str, headers: dict, max_retries: int = 3):
        for attempt in range(max_retries):
            response = requests.get(url, headers=headers)

            if response.status_code != 429:
                return response

            retry_after = int(response.headers.get("Retry-After", 60))
            backoff = retry_after * (2 ** attempt)
            print(f"Rate limited. Aguardando {backoff}s antes de tentar novamente...")
            time.sleep(backoff)

        raise Exception("Limite de tentativas excedido")
    ```
  </Tab>
</Tabs>

<Callout type="warn">
  Não faça polling agressivo após receber um 429. Respeite sempre o tempo indicado no `Retry-After` para evitar bloqueios mais longos.
</Callout>
