# Сұрау жиілігінің шектеулері

> Бір API кілт минутына қанша сұрау жібере алады, 429 жауабының үш түрін қалай ажырату керек, Retry-After тақырыбын қалай оқу керек және бэкофф стратегиясын кодта қалай жазу керек.

## Қысқаша

Бір API кілт минутына **600 оқу** (GET) және **200 жазу** (POST, PATCH, DELETE) сұрауын жібере алады. Шектен асса API `429` күйін және `request_rate_limited` кодын қайтарады, `Retry-After` тақырыбында қанша секунд күту керегін айтады. Әр жауапта `X-RateLimit-Remaining` тақырыбы сол минутта қанша сұрау қалғанын көрсетеді.

Бірақ **429 жауабының бәрі жиілікке байланысты емес**. `tariff_daily_burst` пен `tariff_limit_reached` те сол HTTP кодымен келеді, ал олар — тариф лимиті: бірнеше секунд күткеннен өтпейді. Сондықтан кодыңыз 429-ды көргенде бірден қайталамай, алдымен `error` өрісін оқуы керек.

## Нақты шектер

| Не | Шек | Есептегіш |
|---|---|---|
| GET (оқу) | минутына 600 | әр API кілтке бөлек |
| POST, PATCH, DELETE (жазу) | минутына 200 | әр API кілтке бөлек |
| Төлем бетінің ашық сұраулары | минутына 120 | IP мекенжай бойынша |

Терезе — тұрақты бір минут: минут біткенде есептегіш нөлден басталады. Екі API кілт жасасаңыз, әрқайсысының есептегіші бөлек жүреді — бұл әр жобаға бөлек кілт беруге ыңғайлы, бірақ шектен өту үшін кілт көбейтудің қажеті жоқ: қалыпты дүкен бұл шекке ешқашан жетпейді.

`X-RateLimit-Remaining` тақырыбы әр сәтті жауапта келеді. Мониторингіңізде сол санды жазып қойсаңыз, шекке жақындап қалғаныңызды алдын ала көресіз.

## 429-дың үш түрі

| Код | Қайдан келеді | Не болды | Не істеу керек |
|---|---|---|---|
| `request_rate_limited` | API (`/api/v1/*`) | Кілттің минуттық жиілігі асып кетті | `Retry-After` көрсеткен секундты күтіп қайталаңыз |
| `rate_limited` | Кабинетке кіру коды | Кодты 60 секунд ішінде қайта сұрадыңыз немесе тәуліктік код лимиті бітті | Бір минут күтіңіз. Жауапта `retryAfterSeconds` өрісі болады |
| `too_many_attempts` | Кодты енгізу | Қате код тым көп рет енгізілді | Жаңа код сұраңыз, ескісін теруді тоқтатыңыз |

`rate_limited` пен `too_many_attempts` — кабинетке кіру процесіне қатысты, сіздің серверлік интеграцияңызда әдетте кездеспейді. Егер кездессе, демек кодыңыз кіру эндпоинттерін шақырып тұр.

## Жиілік пен лимитті ажырату

Бұл екеуін шатастыру — ең жиі кездесетін қате. Лимит бітсе, қанша күтсеңіз де сол минутта өтпейді.

| Код | Түрі | Қашан өтеді | Қайталауға бола ма |
|---|---|---|---|
| `request_rate_limited` | Жиілік | Бірнеше секундтан кейін | Иә, `Retry-After` бойынша |
| `tariff_daily_burst` | Тәуліктік қорғаныс | Тәулік ауысқанда | **Жоқ.** Алдымен кодыңызды тексеріңіз — бұл әдетте циклге түскен интеграция |
| `tariff_limit_reached` | Айлық тариф лимиті | Жаңа ай басталғанда немесе тарифті көтергенде | **Жоқ.** Кабинет → Тариф |

Соқыр қайталау мына жағдайда қауіпті: `tariff_daily_burst` келгенде цикл тоқтамай, сағаттар бойы сұрау жіберіп тұрады. Толығырақ: [Тарифтер және лимиттер](/kb/tariff-limits).

## Retry-After тақырыбы

`request_rate_limited` жауабында `Retry-After` тақырыбы секундпен келеді — бұл ағымдағы минуттық терезенің аяқталуына қалған уақыт. Оны оқып, дәл сонша күту ең дұрыс тактика: ертерек қайталасаңыз тағы 429 аласыз.

Тақырып жоқ болса (мысалы прокси кесіп тастаса), 1 секундтан бастап екі есе өсіретін бэкоффқа көшіңіз.

## Кодта қалай өңдеу керек

Ережесі қарапайым: **`error` кодын оқыңыз, соған қарай шешім қабылдаңыз.**

```js
const RETRYABLE = new Set(['request_rate_limited', 'rate_limited']);

async function callWithRetry(fn, { maxAttempts = 5 } = {}) {
  const delays = [1000, 2000, 4000, 8000]; // 1, 2, 4, 8 секунд
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (err) {
      // Тариф лимиті — күту көмектеспейді, бірден тоқтаймыз
      if (err.code === 'tariff_limit_reached' || err.code === 'tariff_daily_burst') throw err;
      if (!RETRYABLE.has(err.code) && !(err.status >= 500)) throw err;
      if (attempt === maxAttempts - 1) throw err;

      const retryAfter = Number(err.retryAfterSeconds || 0) * 1000;
      const wait = retryAfter || delays[Math.min(attempt, delays.length - 1)];
      await new Promise((r) => setTimeout(r, wait + Math.random() * 300));
    }
  }
}
```

Соңындағы кездейсоқ қосымша (`Math.random()`) — джиттер. Бірнеше процесс қатар жұмыс істесе, олар дәл бір сәтте оянып, тағы 429 алмау үшін керек.

Счёт жасауды қайталағанда **әрқашан сол `Idempotency-Key` мәнін жіберіңіз** — әйтпесе бірінші сұрау шын мәнінде өтіп кетіп, қайталауыңыз екінші счёт жасауы мүмкін. Толығы: [Идемпоттылық](/kb/idempotency).

## Жиілікке мүлдем жетпеу

| Сценарий | Дұрыс тәсіл |
|---|---|
| Көп счётты бір уақытта шығару | `POST /api/v1/invoices/bulk` — бір сұрауда 100 счёт: [Топтап счёт жасау](/kb/bulk-invoices) |
| Төлемді күту | Webhook негізгі арна болсын, сұрауды тек алғашқы 30 секундта қосыңыз: [Webhook пен күйді сұрау](/kb/polling-vs-webhook) |
| Барлық счётты үнемі тексеру | `GET /invoices` тізімін сүзгімен алыңыз, әрқайсысын жеке сұрамаңыз |
| Есеп жасау | Тәулігіне бір рет экспорт, әр беттеу үшін бөлек сұрау емес |

Ең жиі кездесетін себеп — циклде `GET /invoices/{id}` шақыратын, бірақ тоқтау шарты жоқ код. Әр сұрау циклінде шығу шартын жазып қойыңыз.

## Жиі қойылатын сұрақтар

**429 алдым, бірақ `Retry-After` жоқ.** Демек бұл `request_rate_limited` емес, тариф коды. `error` өрісін қараңыз.

**Шектеу счёт санына әсер ете ме?** Жоқ. Жиілік — бұл минутына сұрау саны, ал тарифтегі сан — айына жасалған счёт саны. Екеуі бөлек есептеледі.

**Шекті көтеруге бола ма?** Стандартты шектер қалыпты жүктемеге жеткілікті. Нақты сценарийіңіз оған сыймаса, [қолдауға](https://qut.kz/app/support) жазыңыз.

**Sandbox-та шектер басқа ма?** Жиілік шектері бірдей. Ал тариф лимиттері sandbox счёттарына қолданылмайды.

**Бірнеше сервер бір кілтпен жұмыс істеп жатыр.** Есептегіш кілт бойынша ортақ, сондықтан шек барлық серверге бірге қолданылады. Жүктемені бөлу керек болса, әр сервиске бөлек кілт беріңіз.
