AACSearch
Справочник API

Ошибки и лимиты

Конверт ошибок, все коды API со статусами, семантика лимитов запросов, квоты тарифов и поведение кошелька при превышении.

Конверт ошибок

Любая ошибка — валидация, аутентификация, квота, движок — приходит в одном формате:

{
	"error": {
		"code": "QUOTA_EXCEEDED",
		"message": "Monthly searches quota exhausted (500000/500000 for 2026-10)",
		"request_id": "req_3c5e7a9b1d3f5a7c9e1b3d5f",
		"details": {
			"metric": "searches",
			"limit": 500000,
			"used": 500000,
			"period": "2026-10",
			"resetsAt": "2026-11-01T00:00:00.000Z"
		}
	}
}

code — стабильный идентификатор, по нему можно ветвиться; message предназначен людям и может меняться; request_id дублируется в заголовке x-request-id. details присутствует только тогда, когда есть что сообщить.

curl -sS -i "$AAC_API/indexes/documentation/search?q=тарифы&num_typos=9" \
  -H "Authorization: Bearer $AAC_KEY"
{
	"error": {
		"code": "VALIDATION_ERROR",
		"message": "Request payload is invalid",
		"request_id": "req_7d9f1b3c5e7a9b1d3f5c7a9b",
		"details": {
			"issues": [
				{
					"code": "too_big",
					"path": ["num_typos"],
					"message": "Number must be less than or equal to 3"
				}
			]
		}
	}
}

Коды по статусам

400

КодГде возникает
INVALID_QUERYНеверный синтаксис фильтра, сортировки, списка полей или vector_query; упоминание organization_id в filter_by; отсутствие filter_by при массовом удалении; индекс без поля автоэмбеддинга при semantic=true; больше 20 запросов в multi_search
FIELD_NOT_IN_SCHEMAfilter_by, sort_by, facet_by, query_by или group_by ссылается на поле, которого нет в схеме индекса (в документах оно может быть). details.field — поле из вашего запроса, details.param — параметр. Добавьте поле в разделе «Схема» и переиндексируйте
INVALID_JSONТело вебхука не является JSON
PRIVATE_FIELDquery_by, filter_by, sort_by, facet_by, include_fields или group_by называет приватное поле, а у ключа нет documents:read_private (также /embed/search)
PRIVATE_FIELDS_PRESENTPOST /indexes/{slug}/scoped-key для индекса с приватными полями ключом без documents:read_private. details.fields

401

КодГде возникает
UNAUTHORIZEDНет заголовка Authorization, он некорректен, ключ неизвестен или отозван, нет заголовков подписи вебхука
KEY_ROTATEDКлюч был ротирован, льготный период истёк. Используйте ключ, выданный при ротации
EMBED_SESSION_REQUIRED / EMBED_SESSION_EXPIRED / EMBED_SESSION_INVALID/embed/search, /embed/events, /embed/config, /embed/recommendations вызваны без токена сессии, с истёкшим или недействительным токеном. Выпустите новый через POST /embed/session
EMBED_SESSION_REVOKEDAPI-ключ или сохранённый виджет, выпустивший сессию встраивания, отозван, истёк или отключён; токен перестаёт работать примерно через 30 секунд после изменения
STALE_SIGNATUREОтметка времени вебхука вне окна 300 секунд
INVALID_SIGNATUREНесовпадение HMAC вебхука

402

КодГде возникает
PAYMENT_REQUIREDФункция не входит в тариф (details.feature, details.upgradeTo) или на кошельке не хватает на следующий блок превышения (details.blockCents). Оба случая означают «заплатите или смените тариф», а не «повторите позже»
PLAN_LIMIT_REACHEDСоздание ещё одного индекса или API-ключа либо импорт документов превысит лимит тарифа (индексы, ключи или документы)
FEATURE_NOT_AVAILABLEОрганизация suspended после неоплаты подписки: работает как Free, платная функция отклоняется с details.reason = "past_due" — оплатите счёт, а не повышайте тариф

403

КодГде возникает
FORBIDDENУ ключа нет нужного права или он привязан к другому индексу; сессия встраивания использована не для search:read
ORIGIN_NOT_ALLOWEDЗапрос с заголовком Origin, которого нет в allowedOrigins ключа. Проверяется на сервере для всех маршрутов с API-ключом (и POST /embed/session), а не только через CORS-заголовки; запросы без Origin (сервер-сервер) не затрагиваются
INDEX_MISMATCHСессия встраивания привязана к другому индексу, чем запрошенный
KEY_IN_GRACEВызывающий ключ был ротирован и находится в льготном периоде: он не может ротировать ключи и выпускать ограниченные ключи. Используйте преемника
KEY_LIFETIME_EXCEEDS_CALLERPOST /api-keys/{keyId}/rotate для ключа, который пережил бы вызывающий ключ
FORBIDDEN (на всю организацию)GET /organization/export, DELETE /organization/data или /webhooks/* ключом, привязанным к индексу

Утверждение acl в запросе поиска игнорируется (а не отклоняется), если у ключа есть список allowedOrigins или запрос пришёл с заголовком Origin: ключ, видимый в браузере, не может выбирать, чьи документы он видит. Такой поиск видит только документы без ACL.

404

КодГде возникает
INDEX_NOT_FOUNDНеизвестный слаг индекса в организации ключа; сессия встраивания без доступного индекса. «Коллекция не найдена» на уровне движка отдаётся под этим публичным кодом
DOCUMENT_NOT_FOUNDGET/DELETE по неизвестному external_id
PROFILE_NOT_FOUND?profile= указывает на профиль, которого нет у индекса
CONVERSATION_NOT_FOUNDИдентификатор диалога, не принадлежащий индексу
NOT_FOUND/metrics без валидного x-metrics-token
JOB_NOT_FOUNDЗадача другого тенанта или (для ключа с привязкой) другого индекса
SNAPSHOT_NOT_FOUNDНеизвестный снимок или снимок другого индекса/тенанта

409

КодГде возникает
INDEX_ALREADY_EXISTSДубликат на уровне движка при создании коллекции (переиндексация, консоль); физическое имя коллекции не раскрывается
CONFLICTКонфликтующая запись, обнаруженная сервисным слоем
IDEMPOTENCY_IN_FLIGHTИмпорт документов с тем же Idempotency-Key ещё выполняется — повторите после его завершения
JOB_CONFLICTЗапрошен reindex или restore (в том числе с ?sync=true), пока reindex или restore в очереди или выполняется, либо переэмбеддинг всего индекса при уже активном. details.job_id называет активную задачу
JOB_NOT_CANCELLABLEОтмена завершённой задачи или задачи на этапе swapping
SNAPSHOT_LIMIT_REACHEDДостигнут лимит снимков на индекс; повторите с ?prune=true
KEY_ALREADY_ROTATED / KEY_NOT_ACTIVEПовторная ротация / ротация отозванного или истёкшего ключа

413

КодГде возникает
AUDIO_TOO_LARGEГолосовой фрагмент превышает допустимый размер

422

КодГде возникает
VALIDATION_ERRORОтказ схемы Zod, включает details.issues
IDEMPOTENCY_KEY_REUSEDIdempotency-Key уже использован для другого запроса (отличается метод, путь или тело)
INVALID_DOCUMENTНет content, неверное значение поля, больше 500 документов на импорт или 1000 на вебхук
INVALID_AUDIOАудио не удалось декодировать
EMPTY_TRANSCRIPTIONВ голосовом запросе не распознана речь
CLIENT_ID_REQUIREDPOST /recent без x-aac-client-id
QUERY_TOO_SHORTPOST /recent с запросом короче 2 символов
INVALID_SNAPSHOT_NAMEDELETE /admin/snapshots с небезопасным именем файла

429

КодГде возникает
RATE_LIMITEDПревышен лимит запросов на ключ
QUOTA_EXCEEDEDИсчерпана квота тарифа по метрике

5xx

КодСтатусГде возникает
INTERNAL_ERROR500Необработанное исключение — сообщите request_id
FEATURE_UNSUPPORTED501Функции нужен более новый поисковый движок, чем подключён (FEATURE_UNAVAILABLE — функция ещё не включена в этом развёртывании)
SEARCH_UNAVAILABLE502Движок недоступен или вернул неотображённую ошибку
AI_UNAVAILABLE502Сбой провайдера модели
AI_EMPTY_RESULT502Модель вернула пустой результат
SNAPSHOT_FAILED502Не удался снимок движка
SNAPSHOT_NOT_CONFIGURED503POST /admin/snapshot без SEARCH_SNAPSHOT_DIR
SEARCH_UNAVAILABLE503В развёртывании нет учётных данных поискового движка
AI_NOT_CONFIGURED503Нет OPENAI_API_KEY для ответов, изображений или транскрипции
NL_MODEL_NOT_CONFIGURED503Модель для этой функции не настроена
VOICE_MODEL_NOT_CONFIGURED503Модель для этой функции не настроена
CONVERSATION_MODEL_NOT_CONFIGURED503Модель для этой функции не настроена
WEBHOOK_NOT_CONFIGURED503Нет секрета вебхука у индекса или на сервере
SEARCH_TIMEOUT504Движок не ответил вовремя

Ошибки движка и провайдеров приводятся к этому публичному контракту до выхода из API, внутренние имена в ответе не появляются: 404 движка становится INDEX_NOT_FOUND, 409 — INDEX_ALREADY_EXISTS, 400/422 — INVALID_QUERY (неизвестное поле в filter_by/sort_by/facet_by/query_by/group_by — FIELD_NOT_IN_SCHEMA), таймауты — SEARCH_TIMEOUT, всё остальное (включая отсутствие конфигурации движка) — SEARCH_UNAVAILABLE. Другие алиасы: AI_PROVIDER_ERROR → AI_UNAVAILABLE, ENGINE_FEATURE_UNSUPPORTED → FEATURE_UNSUPPORTED, INDEX_HAS_NO_EMBEDDING_FIELD → INDEX_NOT_VECTOR_ENABLED. Исходное сообщение движка пишется в лог на сервере и связывается через request_id; назовите этот id в поддержке.

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

Запросы ограничиваются на API-ключ в скользящем окне:

НастройкаПо умолчаниюПеременная
Окно60 секундSEARCH_RATE_WINDOW_SECONDS
Запросов в окне600SEARCH_RATE_MAX_REQUESTS

Каждый аутентифицированный ответ несёт x-ratelimit-limit, x-ratelimit-remaining и x-ratelimit-reset (Unix-время в секундах), включая успешные, — так клиент может сам выдерживать темп. Превышение — 429 с временем сброса:

curl -sS -i "$AAC_API/indexes/documentation/search?q=тарифы" \
  -H "Authorization: Bearer $AAC_KEY"
{
	"error": {
		"code": "RATE_LIMITED",
		"message": "Rate limit exceeded",
		"request_id": "req_9b1d3f5a7c9e1b3d5f7a9c1e",
		"details": { "limit": 600, "reset_at": "2026-10-23T10:42:00.000Z" }
	}
}

По умолчанию лимитер работает в процессе, а при заданном REDIS_URL — общий через Redis. На нескольких инстансах без Redis каждый инстанс держит свои 600 — учитывайте это при планировании.

RATE_LIMITED и QUOTA_EXCEEDED — оба 429, но означают разное: первый снимается через секунды, второй — только с началом нового периода или после смены тарифа. Смотрите error.code, а не только статус.

Квоты тарифов

Метрики учёта: searches, documents, ai_tokens и reindex. Включённые в тариф объёмы за период:

FreePro ($29/мес)Enterprise
Поиски10 000500 0005 000 000
Документы1 00050 0001 000 000
AI-токены10 000100 0002 000 000
Переиндексации250500
Индексы510100
API-ключи310100
Коннекторы510100
Результатов на страницу50250500
Хранение журнала запросов7 дней90 дней365 дней

Цены превышения за блок:

МетрикаБлокFreeProEnterprise
Поиски1 000—50¢25¢
Документы1 000—20¢10¢
AI-токены1 000—2¢1¢
Переиндексация1 запуск—5¢2¢

У Free цен превышения нет вообще: при достижении лимита возвращается 429 QUOTA_EXCEEDED, и ничего не списывается. На платных тарифах превышение списывается с предоплаченного кошелька.

Функции, зависящие от тарифа: Free включает поиск по ключевым словам, синонимы, стоп-слова, aiAnswer (RAG, в пределах 10 000 AI-токенов) и scheduledSync, но не vectorSearch, hybridSearch, curations, grouping, geoSearch, multiSearch, reindex, exportDocuments, customSort и queryAnalytics. Использование закрытой функции даёт 402 PAYMENT_REQUIRED от слоя учёта; запрос падает, а не деградирует молча. Недостающий scope API-ключа по-прежнему даёт 403 FORBIDDEN.

Кошелёк и превышение

  1. Расход считается по периоду (YYYY-MM) и сбрасывается в 00:00 UTC первого числа.
  2. В пределах включённого объёма вызов ничего не стоит.
  3. Сверх него каждый блок списывается с кошелька: searches блоками по 1 000, documents и ai_tokens так же, reindex — за запуск.
  4. При нулевом балансе вызов падает с 402 PAYMENT_REQUIRED и инструкцией по пополнению; функция тарифа, требующая оплаты, падает так же.
  5. Пополнения — предоплаченные пакеты на $10, $50 и $200, они покупаются через платёжного провайдера и попадают в журнал операций.

Смотрите состояние заранее:

curl -sS "$AAC_API/billing/wallet" -H "Authorization: Bearer $AAC_KEY"
{
	"wallet": { "balanceCents": 4200, "currency": "USD" },
	"plan": "pro",
	"period": "2026-10",
	"resets_at": "2026-11-01T00:00:00.000Z",
	"transactions": [
		{
			"id": "txn_01HZYA",
			"type": "credit",
			"amount_cents": 5000,
			"balance_after_cents": 5000,
			"description": "Top-up credits_50",
			"metric": null,
			"created_at": "2026-10-02T09:12:00.000Z"
		},
		{
			"id": "txn_01HZYB",
			"type": "debit",
			"amount_cents": -800,
			"balance_after_cents": 4200,
			"description": "search overage: 16 blocks",
			"metric": "searches",
			"created_at": "2026-10-21T18:03:11.000Z"
		}
	],
	"request_id": "req_4f6a8c0e2b4d6f8a0c2e4b6d"
}
curl -sS "$AAC_API/billing/usage" -H "Authorization: Bearer $AAC_KEY"
{
	"usage": {
		"plan": "pro",
		"planName": "Pro",
		"period": "2026-10",
		"resetsAt": "2026-11-01T00:00:00.000Z",
		"searches": { "used": 500000, "limit": 500000 },
		"documents": { "used": 51204, "limit": 50000 },
		"aiTokens": { "used": 24800, "limit": 100000 },
		"indexes": { "used": 3, "limit": 10 },
		"apiKeys": { "used": 2, "limit": 10 },
		"connectors": { "used": 1, "limit": 10 },
		"wallet": { "balanceCents": 4200, "currency": "USD" }
	},
	"request_id": "req_2b4d6f8a0c2e4b6d8f0a2c4e"
}

documents считается по зеркалу документов, а не по счётчику, поэтому отражает фактическое содержимое индекса. wallet равен null, если у тенанта никогда не было пополнений.

Практические рекомендации

  • Повторяйте только на 429. Заголовок Retry-After не отправляется; используйте details.reset_at или x-ratelimit-reset.
  • Не повторяйте 4xx кроме 429. 403 не станет 200 от повторения, а тело с 422 нужно исправить.
  • Повторяйте 5xx с джиттером. SDK по умолчанию делают две попытки с экспоненциальной задержкой.
  • Следите за x-aac-quota-remaining. Падения на границе квоты можно избежать, если алертить на 10% остатка.
  • Алертите по доле пустых выдач, а не по 200. Поиск без результатов — успех по HTTP и провал по продукту.

On this page