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

# Limites de requisição

> O rate limit da API do SULTS: 2 requisições por segundo por IP, a resposta 429 e como aplicar backoff exponencial nas varreduras

A API aplica um limite de **2 requisições por segundo, por IP**. Ao exceder, a resposta é `429` e a chamada não é processada. O corpo desse 429 é uma página HTML padrão do servidor, sem JSON e sem o header `Retry-After`: trate pelo status.

O limite vale por IP, não por token. Integrações diferentes que saem do mesmo servidor dividem a mesma cota, e é comum descobrir isso só quando a segunda integração entra no ar.

## Quantas chamadas por segundo a API aceita?

Uma pausa de 500 ms entre chamadas mantém você dentro do limite. Parece lento, mas o custo é menor do que soa: uma varredura de 100 páginas termina em menos de um minuto.

O ponto de atenção é a sincronização inicial, quando você carrega a base inteira pela primeira vez. Depois dela, as cargas incrementais são pequenas.

## Trate o 429 com backoff exponencial

Ao receber `429`, espere e tente de novo, dobrando o intervalo a cada falha: 1 s, 2 s, 4 s, 8 s. Como não há `Retry-After`, esse intervalo é seu, começando em 1 segundo. Nunca repita a chamada imediatamente, porque isso só consome a cota de novo.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-dark","dark":"github-dark"}}
  # --retry repete em 429 e 5xx: espera 1 s e dobra a cada tentativa
  curl --retry 5 "https://api.sults.com.br/api/v1/chamado/ticket?start=0&limit=100" \
    -H "Authorization: SEU_TOKEN"
  ```

  ```python Python theme={"theme":{"light":"github-dark","dark":"github-dark"}}
  import time
  import requests

  BASE = "https://api.sults.com.br"
  HEADERS = {"Authorization": "SEU_TOKEN"}

  def buscar(caminho, params=None, tentativas=5):
      espera = 1
      for tentativa in range(tentativas):
          r = requests.get(f"{BASE}{caminho}", headers=HEADERS, params=params)
          if r.status_code != 429:
              r.raise_for_status()
              return r.json()
          time.sleep(espera)
          espera *= 2  # dobra a espera a cada 429 seguido
      raise RuntimeError(f"limite de requisicoes persistiu apos {tentativas} tentativas")

  chamados = buscar("/api/v1/chamado/ticket", {"start": 0, "limit": 100})
  print(chamados["size"], "chamados nesta pagina")
  ```

  ```javascript Node theme={"theme":{"light":"github-dark","dark":"github-dark"}}
  const BASE = "https://api.sults.com.br";
  const HEADERS = { Authorization: "SEU_TOKEN" };

  const dormir = (ms) => new Promise((r) => setTimeout(r, ms));

  async function buscar(caminho, params = {}, tentativas = 5) {
    const url = new URL(BASE + caminho);
    Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));

    let espera = 1000;
    for (let i = 0; i < tentativas; i++) {
      const r = await fetch(url, { headers: HEADERS });
      if (r.status !== 429) {
        if (!r.ok) throw new Error(`HTTP ${r.status}`);
        return r.json();
      }
      await dormir(espera);
      espera *= 2; // dobra a espera a cada 429 seguido
    }
    throw new Error(`limite de requisicoes persistiu apos ${tentativas} tentativas`);
  }

  const chamados = await buscar("/api/v1/chamado/ticket", { start: 0, limit: 100 });
  console.log(chamados.size, "chamados nesta pagina");
  ```

  ```php PHP theme={"theme":{"light":"github-dark","dark":"github-dark"}}
  <?php
  const BASE = "https://api.sults.com.br";

  function buscar(string $caminho, array $params = [], int $tentativas = 5): array
  {
      $espera = 1;
      for ($i = 0; $i < $tentativas; $i++) {
          $ch = curl_init(BASE . $caminho . "?" . http_build_query($params));
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
          curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: SEU_TOKEN"]);
          $corpo = curl_exec($ch);
          $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
          curl_close($ch);

          if ($status !== 429) {
              if ($status >= 400) {
                  throw new RuntimeException("HTTP $status");
              }
              return json_decode($corpo, true);
          }
          sleep($espera);
          $espera *= 2; // dobra a espera a cada 429 seguido
      }
      throw new RuntimeException("limite de requisicoes persistiu apos $tentativas tentativas");
  }

  $chamados = buscar("/api/v1/chamado/ticket", ["start" => 0, "limit" => 100]);
  echo $chamados["size"] . " chamados nesta pagina" . PHP_EOL;
  ```

  ```csharp C# theme={"theme":{"light":"github-dark","dark":"github-dark"}}
  using System.Net;
  using System.Net.Http;
  using System.Text.Json;

  var http = new HttpClient { BaseAddress = new Uri("https://api.sults.com.br") };
  http.DefaultRequestHeaders.Add("Authorization", "SEU_TOKEN");

  async Task<JsonDocument> Buscar(string caminho, int tentativas = 5)
  {
      var espera = TimeSpan.FromSeconds(1);
      for (var i = 0; i < tentativas; i++)
      {
          var resposta = await http.GetAsync(caminho);
          if (resposta.StatusCode != HttpStatusCode.TooManyRequests)
          {
              resposta.EnsureSuccessStatusCode();
              return JsonDocument.Parse(await resposta.Content.ReadAsStringAsync());
          }
          await Task.Delay(espera);
          espera *= 2; // dobra a espera a cada 429 seguido
      }
      throw new HttpRequestException($"limite de requisicoes persistiu apos {tentativas} tentativas");
  }

  using var chamados = await Buscar("/api/v1/chamado/ticket?start=0&limit=100");
  Console.WriteLine($"{chamados.RootElement.GetProperty("size")} chamados nesta pagina");
  ```
</CodeGroup>

## Ritme as varreduras

Em listagens paginadas, insira a pausa entre as páginas em vez de esperar pelo `429`. Prevenir custa menos do que reagir.

```python theme={"theme":{"light":"github-dark","dark":"github-dark"}}
import time

pagina = 0
while True:
    lote = buscar("/api/v1/chamado/ticket", {"start": pagina, "limit": 100})
    if not lote["data"]:
        break
    processar(lote["data"])
    pagina += 1
    time.sleep(0.5)  # respeita o limite de 2 req/s
```

## Quais erros não devem ser repetidos?

Nem toda falha pede espera. Erros `400` e `404` não se resolvem sozinhos: reenviar o mesmo payload devolve o mesmo erro, então corrija antes de tentar de novo. O `401` é configuração, e repetir a chamada não vai autenticá-la.

Só `429` e os erros `5xx` merecem retry com espera crescente. O catálogo completo está em [erros na v1](/v1/erros) e [erros na v2](/v2/erros).

## Próximos passos

<CardGroup cols={2}>
  <Card title="Como a API funciona" icon="compass" href="/geral/como-a-api-funciona">
    Os conceitos que valem para as duas versões.
  </Card>

  <Card title="Autenticação" icon="key" href="/geral/autenticacao">
    Como o token funciona e onde gerar o seu.
  </Card>
</CardGroup>


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