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

# Limites de requisição

> A cota de 100 requisições por 60 segundos por API Key da NyxPag, como a janela funciona, os cabeçalhos RateLimit e como respeitar o limite sem perder operações.

Cada API Key tem uma cota de **100 requisições por janela de 60 segundos**. Passou disso, a API responde `429` até a janela reiniciar.

## Como a cota funciona

| Regra | Detalhe |
| - | - |
| Tamanho | 100 requisições por 60 segundos |
| Por chave | Cada API Key tem a **sua** cota, mesmo dentro da mesma conta |
| Compartilhada | Soma **todas** as rotas autenticadas e também o [MCP](/guides/mcp) |
| Início da janela | Na primeira requisição autenticada, e não em horário fixo |
| Não dá para burlar | Trocar rota, método, cabeçalho ou IP **não** reinicia a janela |
| O que conta | Toda requisição que passou na validação da chave e do IP, **inclusive as que falham depois** |
| O que não conta | Os endpoints públicos: `GET /transparency`, `GET /v1`, `/v1/openapi.json`, `/v1/llms.txt` |

<Warning>
  Requisições com erro também gastam a cota. Um loop que recebe `403` ou `400` e tenta de novo sem parar consome as 100 mais rápido do que um fluxo saudável.
</Warning>

<Note>
  Além da cota por chave existem proteções por **IP**, aplicadas antes da autenticação. Elas também podem devolver `429`. Os cabeçalhos `RateLimit-*` ajudam a distinguir: `RateLimit-Limit: 100` é a cota da chave.
</Note>

## Cabeçalhos

Toda resposta autenticada informa o estado da cota.

| Cabeçalho | Unidade | Descrição |
| - | - | - |
| `RateLimit-Limit` | requisições | Limite da regra aplicada (`100` na cota por chave) |
| `RateLimit-Remaining` | requisições | Quantas restam na janela atual |
| `RateLimit-Reset` | segundos | Tempo até a janela reiniciar |
| `Retry-After` | segundos | Em um `429`: quanto esperar antes de tentar de novo |

## A resposta 429

```json theme={"dark"}
{
  "success": false,
  "code": "rate_limit_exceeded",
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Muitas solicitações. Aguarde antes de tentar novamente."
  },
  "retryAfter": 60,
  "requestId": "req_01H..."
}
```

Leia o cabeçalho `Retry-After` (ou o campo `retryAfter`, em segundos) para saber quando voltar.

## Respeitando o limite

<Steps>
  <Step title="Prefira webhooks a consultas">
    Quase toda a cota de uma integração mal feita vai para polling de status. Receber `transaction.approved` não gasta nenhuma requisição.
  </Step>

  <Step title="Leia RateLimit-Remaining">
    Quando chegar perto de zero, reduza o ritmo antes de tomar o `429`.
  </Step>

  <Step title="Espere o Retry-After">
    No `429`, aguarde o tempo informado, mais um jitter aleatório. Nunca tente de novo a cada segundo.
  </Step>

  <Step title="Limite as tentativas">
    Defina um máximo, como 5. Operações financeiras repetem com a **mesma** chave de [idempotência](/guides/idempotencia).
  </Step>

  <Step title="Separe serviços em chaves diferentes">
    Cada chave tem a sua cota. Dar uma chave ao checkout e outra à conciliação evita que um job de relatório esgote a cota do pagamento. É para isolar carga, não para ultrapassar o limite de um mesmo serviço.
  </Step>
</Steps>

## Exemplo: respeitar o Retry-After

<CodeGroup>
  ```javascript JavaScript theme={"dark"}
  async function fetchRespectingLimit(url, options, maxAttempts = 5) {
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      const res = await fetch(url, options);

      // ritmo proativo: desacelera antes de estourar
      const remaining = Number(res.headers.get("RateLimit-Remaining") ?? 100);
      if (remaining < 10) await new Promise((r) => setTimeout(r, 1000));

      if (res.status !== 429) return res;

      const retryAfter = Number(res.headers.get("Retry-After") ?? 1);
      const jitter = Math.random() * 500;
      await new Promise((r) => setTimeout(r, retryAfter * 1000 + jitter));
    }
    throw new Error("Limite de requisições: tentativas esgotadas");
  }
  ```

  ```python Python theme={"dark"}
  import os
  import random
  import time
  import requests

  def fetch_respecting_limit(url, max_attempts=5, **kwargs):
      for _ in range(max_attempts):
          res = requests.get(url, timeout=15, **kwargs)

          # ritmo proativo: desacelera antes de estourar
          if int(res.headers.get("RateLimit-Remaining", "100")) < 10:
              time.sleep(1)

          if res.status_code != 429:
              return res

          retry_after = int(res.headers.get("Retry-After", "1"))
          time.sleep(retry_after + random.random() * 0.5)

      raise RuntimeError("Limite de requisições: tentativas esgotadas")
  ```

  ```php PHP theme={"dark"}
  <?php
  function fetchRespectingLimit(string $url, int $maxAttempts = 5): array
  {
      for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
          $headers = [];
          $ch = curl_init($url);
          curl_setopt_array($ch, [
              CURLOPT_RETURNTRANSFER => true,
              CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NYXPAG_API_KEY')],
              CURLOPT_HEADERFUNCTION => function ($c, $line) use (&$headers) {
                  $parts = explode(':', $line, 2);
                  if (count($parts) === 2) $headers[strtolower(trim($parts[0]))] = trim($parts[1]);
                  return strlen($line);
              },
          ]);
          $body = curl_exec($ch);
          $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

          if ($status !== 429) return [$status, json_decode($body, true)];

          sleep((int) ($headers['retry-after'] ?? 1));
      }
      throw new RuntimeException('Limite de requisições: tentativas esgotadas');
  }
  ```

  ```bash cURL theme={"dark"}
  # espera o Retry-After no 429
  for i in 1 2 3 4 5; do
    CODE=$(curl -s -D /tmp/h -o /tmp/b -w "%{http_code}" \
      -H "Authorization: Bearer $NYXPAG_API_KEY" \
      https://api.nyxpag.com.br/v1/balance)
    [ "$CODE" != "429" ] && break
    sleep "$(grep -i '^retry-after:' /tmp/h | tr -d '\r' | awk '{print $2}')"
  done
  cat /tmp/b
  ```
</CodeGroup>

## Quanto uma integração costuma gastar

| Fluxo | Requisições |
| - | - |
| Criar uma cobrança | 1 |
| Confirmar por webhook | 0 |
| Confirmar por consulta (`refresh=true`) a cada 5 segundos, por 15 minutos | cerca de 180 por cobrança |
| Conciliação de 1.000 transações com `limit=100` | 10 |

O terceiro caso mostra por que polling não escala: **uma única cobrança** consultada de 5 em 5 segundos já passa da cota.

<CardGroup cols={2}>
  <Card title="Erros" icon="triangle-alert" href="/guides/erros">
    Todos os códigos e o que repetir.
  </Card>

  <Card title="Antes de ir para produção" icon="list-checks" href="/guides/confiabilidade/checklist-producao">
    O que revisar antes de escalar.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.