> ## Documentation Index
> Fetch the complete documentation index at: https://docs.engineapi.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate limit

> Limites de uso da engineAPI: headers, limites por plano, tratamento de 429 e retry com backoff.

A engineAPI aplica limites de taxa para garantir estabilidade e performance para todos os parceiros. Os limites são aplicados por partner: todas as suas chaves compartilham o mesmo orçamento.

## Limites por plano

| Plano      | Requests/segundo |
| ---------- | ---------------- |
| Dev        | 5                |
| Starter    | 20               |
| Growth     | 60               |
| Scale      | 200              |
| Enterprise | Dedicado         |

<Info>
  A janela do rate limit é sempre de **1  segundo**. Não existe limite por minuto, hora ou
  dia. O limite é aplicado por `partnerId` (não por IP) e vale para toda requisição
  autenticada, emissão, consulta ou download.
</Info>

## Headers de rate limit

Toda resposta da API inclui headers que indicam seu consumo atual:

```http theme={null}
HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1714200000
```

| Header                  | Descrição                                  |
| ----------------------- | ------------------------------------------ |
| `X-RateLimit-Limit`     | Total de requests permitidos no período    |
| `X-RateLimit-Remaining` | Requests restantes no período atual        |
| `X-RateLimit-Reset`     | Timestamp Unix de quando o contador reseta |

## Resposta 429 (Too Many Requests)

Quando o limite é atingido, a API retorna o mesmo envelope RFC 7807 usado em todos os
erros (ver [Erros e Rejeições](/guides/errors)):

```json theme={null}
{
  "error": {
    "type": "https://engineapi.com.br/errors/RATE_LIMIT_EXCEEDED",
    "title": "Limite de Requisições Excedido",
    "status": 429,
    "detail": "Rate limit excedido. Seu plano permite 20 requests/segundo. Upgrade em https://engineapi.com.br/#pricing",
    "instance": "/v1/nfe",
    "requestId": "req_uofirusmuamw",
    "timestamp": "2026-07-05T11:06:14.453Z"
  }
}
```

Com o header adicional:

```http theme={null}
Retry-After: 1 {/* fact:rateLimit.retryAfterSeconds */}
```

<Info>
  `Retry-After` é sempre `1 {/* fact:rateLimit.retryAfterSeconds */}` (a janela do rate limit é de 1  segundo). Não é um valor
  dinâmico calculado a partir do consumo.
</Info>

## Retry com backoff exponencial

Implemente retry automático para lidar com rate limits de forma elegante:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { EngineApiClient, EngineApiError } from '@engineapi/sdk';

  async function emitirComRetry(
    client: EngineApiClient,
    params: any,
    maxRetries = 3
  ) {
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        return await client.nfe.emitir(params);
      } catch (error) {
        if (error instanceof EngineApiError && error.isRateLimited) {
          if (attempt === maxRetries) throw error;

          const waitMs = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
          console.log(`Rate limited. Retry em ${waitMs}ms...`);
          await new Promise(r => setTimeout(r, waitMs));
        } else {
          throw error; // Não é rate limit, propaga o erro
        }
      }
    }
  }
  ```

  ```python Python theme={null}
  import time
  import httpx

  def emitir_com_retry(engine, dados, max_retries=3):
      for attempt in range(max_retries + 1):
          try:
              return engine.emitir_nfe(dados)
          except httpx.HTTPStatusError as e:
              if e.response.status_code == 429:
                  if attempt == max_retries:
                      raise
                  wait = 2 ** attempt
                  print(f"Rate limited. Retry em {wait}s...")
                  time.sleep(wait)
              else:
                  raise
  ```

  ```php PHP theme={null}
  function emitirComRetry(EngineAPI $engine, array $dados, int $maxRetries = 3): array
  {
      for ($attempt = 0; $attempt <= $maxRetries; $attempt++) {
          try {
              return $engine->emitirNfe($dados);
          } catch (ClientException $e) {
              if ($e->getResponse()->getStatusCode() === 429) {
                  if ($attempt === $maxRetries) throw $e;
                  $wait = pow(2, $attempt);
                  echo "Rate limited. Retry em {$wait}s...\n";
                  sleep($wait);
              } else {
                  throw $e;
              }
          }
      }
  }
  ```
</CodeGroup>

## Boas práticas

<AccordionGroup>
  <Accordion title="Monitore os headers">
    Verifique `X-RateLimit-Remaining` em cada resposta. Se estiver abaixo de 10%, reduza a velocidade das chamadas proativamente.
  </Accordion>

  <Accordion title="Use filas para emissão em lote">
    Se precisa emitir muitas notas de uma vez, use uma fila (Bull, RabbitMQ, SQS) com intervalo entre emissões ao invés de chamadas paralelas.
  </Accordion>

  <Accordion title="Separe consultas de emissões">
    Consultas (GET) e downloads (PDF/XML) são mais leves que emissões (POST). Se possível, faça consultas em horários de menor movimento.
  </Accordion>

  <Accordion title="Cache respostas de consulta">
    O status de uma NF-e autorizada não muda. Cache o resultado de `GET /v1/nfe/:id` para evitar chamadas repetidas.
  </Accordion>
</AccordionGroup>

## Endpoints sem autenticação

Nenhum endpoint é isento de rate limit. O que muda é a régua aplicada:

| Requisição                                                           | Limite aplicado                             |
| -------------------------------------------------------------------- | ------------------------------------------- |
| Autenticada (JWT ou `x-api-key`)                                     | Limite do plano, por partner (tabela acima) |
| Sem autenticação (`GET /health`, `GET /health/live`, `GET /version`) | Limite global por IP, separado do plano     |

<Info>
  Os endpoints públicos de monitoramento vivem fora do prefixo `/v1` (`/health`,
  `/health/live`, `/version`) e não consomem o limite do seu plano: usam um limite
  global por IP. Use `GET /health` à vontade para health check de infraestrutura.
</Info>

## Precisa de mais?

Se o seu volume ultrapassa os limites do plano Scale, entre em contato para um plano Enterprise personalizado:

* Email: [suporte@engineapi.com.br](mailto:suporte@engineapi.com.br)
* Dashboard: [app.engineapi.com.br](https://app.engineapi.com.br)
