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

# Лимиты запросов

> Throttling: глобальный и per-site лимиты, поведение 429 и стратегия повторов

Платформа ограничивает частоту запросов на двух уровнях. При превышении возвращается
`429 Too Many Requests` с кодом `RATE_LIMITED` в конверте ошибки.

## Два уровня ограничений

<CardGroup cols={2}>
  <Card title="Глобальный (по IP)" icon="globe">
    Базовый лимит уровня приложения — **120 запросов в минуту** на исходящий IP
    (скользящее окно 60 секунд). Защищает инстанс от всплесков с одного адреса.
  </Card>

  <Card title="Per-site (по API-ключу)" icon="key">
    Индивидуальный счётчик на каждый сайт обменника — «шумный» клиент не расходует лимит
    остальных. Окно — **фиксированные 60 секунд**, лимит по умолчанию — **120 запросов в минуту**.
  </Card>
</CardGroup>

<Info>
  Per-site лимит настраивается оператором: либо персональный override для вашего сайта, либо
  глобальный дефолт инстанса. Значение `0` означает «без per-site лимита» (остаётся только
  глобальный лимит по IP). Уточните действующее значение при подключении.
</Info>

## Ответ при превышении

При срабатывании per-site лимита платформа возвращает `429` и проставляет заголовки:

| Заголовок               | Значение                                        |
| ----------------------- | ----------------------------------------------- |
| `Retry-After`           | Через сколько секунд повторить (до сброса окна) |
| `X-RateLimit-Limit`     | Действующий лимит запросов в минуту             |
| `X-RateLimit-Remaining` | Остаток в текущем окне (при `429` всегда `0`)   |

<ResponseExample>
  ```json 429 Too Many Requests theme={null}
  {
    "ok": false,
    "error": {
      "code": "RATE_LIMITED",
      "message": "Rate limit exceeded for this site (120 req/min). Retry after 37s."
    }
  }
  ```
</ResponseExample>

```http Заголовки ответа theme={null}
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
```

## Стратегия повторов

<Steps>
  <Step title="Уважайте Retry-After">
    Получив `429`, дождитесь количества секунд из `Retry-After`, прежде чем повторить.
  </Step>

  <Step title="Применяйте экспоненциальный backoff с джиттером">
    Если `Retry-After` отсутствует — повторяйте с растущей паузой (например, 1s, 2s, 4s, 8s)
    и небольшим случайным джиттером, чтобы не синхронизировать повторы.
  </Step>

  <Step title="Ограничьте число попыток">
    Задайте максимум попыток (например, 5) и переходите к алертингу, если лимит держится долго.
  </Step>
</Steps>

<CodeGroup>
  ```javascript Node.js theme={null}
  async function withRateLimitRetry(fn, maxAttempts = 5) {
    for (let attempt = 1; ; attempt++) {
      const res = await fn();
      if (res.status !== 429) return res;
      if (attempt >= maxAttempts) throw new Error('Rate limit: исчерпаны попытки');

      const retryAfter = Number(res.headers.get('Retry-After'));
      const waitMs = Number.isFinite(retryAfter) && retryAfter > 0
        ? retryAfter * 1000
        : Math.min(2 ** attempt * 1000, 30_000) + Math.random() * 250; // backoff + джиттер
      await new Promise((r) => setTimeout(r, waitMs));
    }
  }
  ```

  ```bash cURL theme={null}
  # Прочитать заголовок Retry-After из ответа
  curl -sS -D - -o /dev/null \
    https://wallet.your-exchange.com/v1/public/balances \
    -H "X-Api-Id: $API_ID" -H "X-Timestamp: $TS" -H "X-Signature: $SIG" \
    | grep -i '^retry-after\|^x-ratelimit'
  ```
</CodeGroup>

## Как не упираться в лимит

<Tip>
  Не опрашивайте статус депозита или выплаты в тугом цикле. Подпишитесь на
  [webhooks](/webhooks/overview) — они доставляют изменения статуса мгновенно, а polling
  оставьте редким запасным механизмом (раз в несколько секунд, не чаще).
</Tip>

* Кэшируйте справочники (`GET /v1/public/networks`, `GET /v1/public/assets`) — они меняются редко.
* Группируйте проверки балансов вместо запроса на каждую операцию.
* Соблюдайте webhook-first подход: реагируйте на события, а не опрашивайте.

## Частые вопросы

<AccordionGroup>
  <Accordion title="Почему 429, хотя я делаю немного запросов?" icon="circle-question">
    Если на сервере обменника несколько процессов делят один исходящий IP, они складываются
    в глобальный лимит по IP (120 req/min). Также проверьте, не запущен ли где-то цикл polling.
  </Accordion>

  <Accordion title="Какой лимит действует для моего сайта?" icon="gauge">
    По умолчанию 120 req/min на сайт, но оператор может задать персональное значение или
    отключить per-site лимит (`0`). Действующий лимит виден в заголовке `X-RateLimit-Limit`
    ответа `429`. Точное значение уточняйте у оператора инстанса.
  </Accordion>

  <Accordion title="Может ли rate-limit заблокировать критичную выплату?" icon="shield-halved">
    Per-site счётчик работает в режиме fail-open: при сбое инфраструктуры лимитирования
    запрос пропускается, а не отклоняется. Тем не менее закладывайте обработку `429` с повтором —
    это штатное поведение под нагрузкой.
  </Accordion>
</AccordionGroup>

<Note>
  Связанные страницы: [Ошибки](/errors), [Webhooks](/webhooks/overview),
  [Частые вопросы](/faq).
</Note>
