AACsearch
Эксплуатация

Лимиты и квоты

Как AACsearch ограничивает запросы на ключ, что значит 429 и как восстановиться.

Лимиты и квоты

AACsearch ограничивает две сущности независимо:

  • Rate limit — запросов в минуту, на API-ключ. Бакет на ключ, сбрасывается каждую минуту.
  • Квоты — суммарные search-units в месяц, на организацию. Потолок биллинг-тарифа.

429 на публичном API — всегда rate limit. Превышение квоты — другой код, см. Ответы по квоте.

Модель rate limit

У каждого постоянного ключа поле rateLimitPerMinute. Дефолт — 600 (10 rps постоянно). На каждый запрос auth-layer инкрементирует счётчик ключа в PostgreSQL; превысил — отказ:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{
  "error": "rate_limited",
  "limit": 600
}

Счётчик сбрасывается на старте каждой минуты по часам. Leaky bucket-сглаживания нет — выстрелили 600 в первую секунду, остаток минуты отказывает; в начале следующей — снова свежие 600.

Да, первая секунда минуты съест внезапный burst. И ещё: стабильные 10 rps лимит не задевают, а burst в 600 запросов, попавший на границу двух минут, частично отказывает. Размазывайте retry.

Задание лимита

В дашборде: API keys → Edit → Rate limit per minute. Ориентиры:

Роль ключаЛимит / минута
Браузерный поиск (виджет)600 – 1 500
Серверный ingest-воркер300 – 600
Admin / дашборд (низкий объём)60
Connector key (CMS плагин)1 000
Синтетический probe30

Это старт. Понаблюдайте за Usage неделю — если 99-я перцентиль ниже половины лимита, можно опустить. Постоянно 80 %+ — либо повышайте, либо делите трафик на два ключа.

Восстановление после 429

Если клиент получил 429:

  1. Не ретраите сразу. Retry в той же минуте снова упадёт.
  2. Ждите следующую минуту. Можно по Retry-After, если есть.
  3. Джиттер. Все клиенты, ретраящие ровно в начале следующей минуты, создают синхронный stampede.

Рабочий backoff:

async function searchWithRetry(payload, maxAttempts = 5) {
	for (let attempt = 0; attempt < maxAttempts; attempt++) {
		const res = await fetch("/api/v1/.../search", { method: "POST", body: payload });
		if (res.status !== 429) return res;
		const ms = 1000 * 2 ** attempt + Math.random() * 1000;
		await new Promise((r) => setTimeout(r, Math.min(ms, 60_000)));
	}
	throw new Error("Rate limited after retries");
}

Браузерные клиенты должны деградировать мягко: «Searching…» с задержкой, а не спам retry, пока пользователь печатает.

Почему вы внезапно «выше лимита»

ПричинаДиагноз
Один ключ в нескольких воркерах.Все вкладываются в один бакет. Дашборд показывает скачки после деплоя.
Search-as-you-type без debounce.На каждое нажатие — запрос. Debounce 150–250 мс.
Webhook fan-out → реиндекс.Бэклог webhook-доставок может разгонять записи.
Бот.API keys → Usage → User-Agent breakdown. Если трафик из одного UA, которого вы не знаете — скрапер. CSP плюс ротация ключа.

Квоты на организацию

Помимо rate limit, у организации месячная квота в search-units. Search-unit — один публичный запрос (suggest, multi-search, federated, geo и т. п.).

Квота проверяется после rate-limit-gate, но до запуска поиска. Когда организация ушла за месячную квоту:

  • Поиск продолжает работать, пока не выйдет за план + overage-бюджет.
  • Дашборд показывает предупреждение на 80 % и баннер «квота закончилась» при исчерпании.
  • Если есть баланс wallet и включён overage-bypass — списываем с wallet и продолжаем.

См. Billing wallet — как работает overage (у Enterprise может быть flat-rate без overage).

Ответы по квоте

Когда и wallet/overage путь иссяк:

HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "error": "quota_exhausted",
  "resetAt": "2026-06-01T00:00:00Z",
  "topUpUrl": "https://app.aacsearch.com/organization/billing/wallet"
}

402 — сознательно. 429 — «идёте слишком быстро», 402 — «использовали то, за что заплатили». В клиенте обрабатывайте по-разному.

Заголовки

ЗаголовокЗначение
x-ratelimit-limitrateLimitPerMinute ключа.
x-ratelimit-remainingОсталось в текущей минуте.
x-ratelimit-resetUTC времени сброса бакета.
retry-afterСекунды до повтора (на 429).

x-request-id тоже возвращается на каждый запрос — кладите в логи, самая полезная вещь для эскалации.

Что не ест rate limit

  • GET /api/v1/health — health probe.
  • GET /scim/v2/ServiceProviderConfig — SCIM discovery.
  • Webhook-и от AACsearch к вам — частота доставки задаётся нами, не вашим лимитом.

Частые ошибки

  • «Поставлю очень высокий лимит, чтобы 429 не было». Лимит защищает вас от стампида, который стоит денег. Ставьте туда, где трафик должен быть.
  • Один ключ на команды. Две команды на одном ключе не могут диагностировать чужой пик. По ключу на сервис.
  • Backoff на неправильный ответ. 5xx — да, backoff. 4xx (включая 429) — клиент чинит. Из 4xx ретраить можно только 429 и 408.

См. также

On this page