[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"docs-optimization":3,"docs-tabs-optimization":6},{"content":4,"lastmod":5},"\n# Лимиты и оптимизация\n\nВайбкод сам объединяет вызовы и пагинирует выборки на стороне сервера. Эта статья описывает встроенные механизмы и подсказывает, какой эндпоинт выбирать под задачу.\n\n**Базовый URL:** `https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1` | **Авторизация:** `X-Api-Key`\n\n[Авто-пагинация](#авто-пагинация-в-list) | [Пакетные вызовы](#пакетные-вызовы-по-нескольким-сущностям) | [Поиск по датам](#поиск-с-разбиением-по-датам) | [Агрегация](#агрегация-вместо-выборки-записей) | [Очередь портала](#очередь-портала) | [Кэширование](#кэширование) | [Сводные лимиты](#сводные-лимиты)\n\n## Авто-пагинация в `list`\n\nПараметр `limit` в `GET \u002Fv1\u002F{entity}` принимает значения до 5000. Если `limit > 50`, Вайбкод сам разбивает выборку на внутренние страницы по 50 записей и собирает их в один ответ:\n\n```\nGET \u002Fv1\u002Fdeals?limit=500&filter[stageId]=NEW\n```\n\nВозвращается до 500 записей плюс мета-поля `meta.total` и `meta.hasMore`. Если под фильтр попадает больше 5000 записей, ответ обрезается, а в `meta.truncated` приходит `true` — для остатка нужно либо сузить фильтр, либо использовать `POST \u002Fv1\u002F{entity}\u002Fsearch`.\n\n## Пакетные вызовы по нескольким сущностям\n\n`POST \u002Fv1\u002Fbatch` объединяет до 50 операций над разными сущностями в один HTTP-запрос. Каждый вызов идентифицируется собственным `id`, ошибка одного не отменяет остальные.\n\nПодходит для дашбордов и страниц-сводок, где одной загрузкой нужны данные из разных мест:\n\n```json\n{\n  \"calls\": [\n    { \"id\": \"deals\", \"entity\": \"deals\", \"action\": \"list\", \"params\": { \"filter\": { \"stageId\": \"NEW\" }, \"limit\": 50 } },\n    { \"id\": \"tasks\", \"entity\": \"tasks\", \"action\": \"list\", \"params\": { \"filter\": { \"responsibleId\": 1 }, \"limit\": 20 } },\n    { \"id\": \"user\", \"entity\": \"users\", \"action\": \"get\", \"entityId\": 1 }\n  ]\n}\n```\n\nПолная спецификация — [Пакетные вызовы](\u002Fdocs\u002Fbatch).\n\n## Пакетные операции по одной сущности\n\n`POST \u002Fv1\u002F{entity}\u002Fbatch` массово создаёт, обновляет или удаляет до 500 записей одной сущности. Внутри Вайбкод разбивает запрос на пакеты по 50 элементов:\n\n```bash\ncurl -X POST https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fdeals\u002Fbatch \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"action\": \"update\",\n    \"items\": [\n      { \"id\": 575, \"stageId\": \"WON\" },\n      { \"id\": 741, \"stageId\": \"WON\" }\n    ]\n  }'\n```\n\nДействия: `create`, `update`, `delete`, `list`, `get`, `fields`. Для `delete` передаётся массив `ids`, для `create` и `update` — `items`, для `list` \u002F `get` \u002F `fields` — `calls`.\n\n## Массовое сканирование с дозагрузкой связанных данных\n\nКогда нужно прочитать тысячи записей одной сущности и подтянуть к ним связанные данные из других сущностей, поток выглядит так:\n\n1. Выгрузить корневой список одним вызовом — Вайбкод сам пагинирует на сервере:\n\n   ```\n   GET \u002Fv1\u002Fdeals?limit=5000&select=id,title,companyId,assignedById\n   ```\n\n2. Собрать `id` связанных сущностей и догрузить пакетами по 50 через `POST \u002Fv1\u002Fbatch`:\n\n   ```json\n   {\n     \"calls\": [\n       { \"id\": \"company-15\", \"entity\": \"companies\", \"action\": \"get\", \"entityId\": 15 },\n       { \"id\": \"company-22\", \"entity\": \"companies\", \"action\": \"get\", \"entityId\": 22 },\n       { \"id\": \"user-1\",     \"entity\": \"users\",     \"action\": \"get\", \"entityId\": 1 }\n     ]\n   }\n   ```\n\n   Один HTTP-запрос — до 50 связанных записей. Цикл повторяется для следующего пакета `id`.\n\nВ таком сценарии корневая выгрузка занимает один сетевой запрос: Вайбкод сам поднимает страницы по 50. Ассоциации догружаются в темпе один HTTP-запрос на 50 элементов вместо запроса на каждый элемент.\n\n## Поиск с разбиением по датам\n\n`POST \u002Fv1\u002F{entity}\u002Fsearch` рассчитан на крупные выборки. Если в фильтре есть условие по дате с диапазоном больше 14 дней, Вайбкод автоматически делит запрос на окна по 7 дней и обрабатывает их параллельно:\n\n```json\n{\n  \"filter\": { \"$gte\": { \"createdAt\": \"2026-01-01\" }, \"$lte\": { \"createdAt\": \"2026-04-30\" } },\n  \"select\": [\"id\", \"title\", \"stageId\"],\n  \"limit\": 5000\n}\n```\n\nВ ответе появляются мета-поля:\n\n| Поле | Описание |\n|------|---------|\n| `meta.windowCount` | Количество окон, на которые был разбит запрос. |\n| `meta.windowErrors` | Количество окон, по которым Битрикс24 вернул ошибку. Остальные окна возвращают свои данные. |\n| `meta.truncated` | `true`, если под фильтр попало больше 5000 записей и часть осталась за пределами выборки. |\n\nРазбиение отключается флагом `\"autoWindow\": false` в теле запроса — применяйте его при таймаутах сети или нестабильной выдаче. Полный список параметров `search` — в документации каждой сущности.\n\n## Агрегация вместо выборки записей\n\nКогда нужны только счётчики, суммы, минимумы, максимумы или средние — `POST \u002Fv1\u002F{entity}\u002Faggregate` возвращает результат одним вызовом, без выгрузки самих записей:\n\n```json\n{\n  \"aggregate\": [\n    { \"field\": \"amount\", \"function\": \"sum\" },\n    { \"field\": \"amount\", \"function\": \"avg\" }\n  ],\n  \"filter\": { \"stageId\": \"WON\" },\n  \"groupBy\": \"assignedById\"\n}\n```\n\nОтвет содержит `data.count`, `data.aggregates` и массив `data.groups` с разбивкой по полю группировки. Массив `data.groups` присутствует только при заданном `groupBy` — без него ответ ограничен одним объектом сводных значений. Для функции `count` результат возвращается одним вызовом независимо от размера выборки. Функции `sum` \u002F `avg` \u002F `min` \u002F `max` подгружают записи постранично до 5000 штук — при большем объёме приходит `meta.truncated: true`.\n\n## Очередь портала\n\nУ каждого портала Битрикс24 своя очередь к API Вайбкод: одновременно выполняются до двух запросов, остальные ждут места. Если запрос провисел в очереди дольше 30 секунд, возвращается `429 QUEUE_TIMEOUT` с подсказкой `userMessage` и `hint`.\n\nЧто снижает нагрузку на очередь:\n\n- Объединять разнородные обращения через `POST \u002Fv1\u002Fbatch` — один HTTP-запрос вместо нескольких.\n- Для массового CRUD по одной сущности — `POST \u002Fv1\u002F{entity}\u002Fbatch`, до 500 записей за запрос.\n- Для широких выборок — `GET \u002Fv1\u002F{entity}?limit=...` с пагинацией на стороне сервера или `POST \u002Fv1\u002F{entity}\u002Fsearch` с разбивкой по датам.\n- Когда нужны числа, а не записи — `POST \u002Fv1\u002F{entity}\u002Faggregate`.\n\n**Повтор при перегрузке очереди.** Под нагрузкой очередь возвращает два разных кода, и оба означают «повтори позже»:\n\n- `429 QUEUE_OVERFLOW` — очередь переполнена, запрос отклонён сразу, за миллисекунды. В заголовке `Retry-After` — рекомендованная пауза в секундах.\n- `429 QUEUE_TIMEOUT` — запрос ждал места дольше 30 секунд. Запрос не был отправлен в Битрикс24 — безопасно повторить. В теле `error.retryAfter` — рекомендованная пауза.\n\nВиджеты аналитики, которые шлют пачку `\u002Fsearch` подряд, должны делать повтор с экспоненциальной задержкой и случайным разбросом по времени (backoff с джиттером), учитывая `Retry-After`, а не повторять мгновенно в цикле — это усугубляет перегрузку. Снизьте параллелизм: выполняйте запросы последовательно или объедините их в `POST \u002Fv1\u002Fbatch`.\n\n```javascript\nasync function callWithBackoff(url, options, maxRetries = 4) {\n  for (let attempt = 0; ; attempt++) {\n    const res = await fetch(url, options)\n    if (res.status !== 429) return res\n    if (attempt >= maxRetries) return res\n\n    \u002F\u002F Оба кода (QUEUE_OVERFLOW, QUEUE_TIMEOUT) отдают паузу в заголовке Retry-After;\n    \u002F\u002F error.retryAfter в теле дублирует то же значение\n    const headerWait = Number(res.headers.get('Retry-After'))\n    const bodyWait = Number((await res.clone().json())?.error?.retryAfter) || 0\n    const baseSec = headerWait || bodyWait || Math.min(2 ** attempt, 30)\n    const jitterMs = Math.floor(Math.random() * 1000)\n    await new Promise(r => setTimeout(r, baseSec * 1000 + jitterMs))\n  }\n}\n```\n\n## Загрузка сообщений из нескольких диалогов\n\n`POST \u002Fv1\u002Fchats\u002Fmessages\u002Fbulk` возвращает сообщения не более чем из 50 диалогов в одном ответе и принимает курсоры `lastId` \u002F `firstId` и `limit` для каждого диалога:\n\n```json\n{\n  \"dialogs\": [\n    { \"dialogId\": \"chat253\", \"limit\": 20 },\n    { \"dialogId\": \"chat741\", \"lastId\": 9357, \"limit\": 50 }\n  ]\n}\n```\n\nСкоуп: `im`. Формат ответа — `{ results, errors, summary }`, аналогично `\u002Fv1\u002Fbatch`.\n\n## Кэширование\n\nЧасть ответов отдаётся из кэша, чтобы повторные чтения не нагружали Битрикс24 и очередь портала. Кэш прозрачен — тело ответа совпадает с некэшированным, а заголовок `X-Cache` показывает, откуда пришёл ответ.\n\n### Кэш ответов `\u002Fv1\u002Fusers` и `\u002Fv1\u002Fstatuses`\n\nОтветы `GET \u002Fv1\u002Fusers` и `GET \u002Fv1\u002Fstatuses` кэшируются на стороне сервера: пользователи — 60 секунд, справочники CRM — 5 минут. Это ускоряет дашборды, которые запрашивают эти эндпоинты при каждой загрузке.\n\nКэш привязан к личному ключу `vibe_api_...`. Для ключа авторизации OAuth-приложения `vibe_app_...` кэш не используется — у разных пользователей разный доступ к данным портала.\n\nЗапись через API — `POST \u002Fv1\u002Fusers`, `PATCH \u002Fv1\u002Fusers\u002F:id` и аналогичные — сбрасывает кэш сразу. Правка напрямую в интерфейсе Битрикс24 кэшу не видна, поэтому такие изменения могут отображаться с задержкой до конца времени жизни кэша: до 60 секунд для пользователей и до 5 минут для справочников.\n\nЧтобы получить заведомо свежие данные в обход кэша, добавьте заголовок `Cache-Control: no-cache`:\n\n```bash\ncurl -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Cache-Control: no-cache\" \\\n  https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fusers\n```\n\nЗаголовок `X-Cache` в ответе показывает, как был обработан запрос:\n\n| Значение | Что означает |\n|----------|--------------|\n| `HIT` | Ответ отдан из кэша |\n| `MISS` | Ответ получен из Битрикс24 и сохранён в кэш |\n| `COALESCED` | Запрос присоединился к уже выполняющемуся обращению за теми же данными |\n| `BYPASS` | Кэш не использовался — ключ авторизации OAuth-приложения или заголовок `Cache-Control: no-cache`. Причина указывается в заголовке `X-Cache-Bypass-Reason` |\n\n### HTTP-кэш служебных эндпоинтов\n\n`GET \u002Fv1\u002Fopenapi.json` и `GET \u002Fv1\u002Fguide` возвращают объёмные документы, которые клиенту незачем перекачивать при каждом запуске. Оба ответа несут стандартные заголовки HTTP-кэша:\n\n- `\u002Fv1\u002Fopenapi.json` — `Cache-Control: public, max-age=300`. Спецификация одинакова для всех клиентов, поэтому её может хранить любой кэш.\n- `\u002Fv1\u002Fguide` — `Cache-Control: private, max-age=300`. Состав ответа зависит от скоупов ключа, поэтому общий кэш хранить его не должен.\n- `ETag` — отпечаток содержимого, меняется только при изменении документа или набора скоупов ключа.\n\nСохраните `ETag` из первого ответа и передавайте его в заголовке `If-None-Match` при повторных запросах. Если содержимое не изменилось, эндпоинт отвечает `304 Not Modified` с пустым телом вместо повторной передачи всего документа:\n\n```bash\n# Первый вызов — полное тело и ETag (openapi.json не требует авторизации)\ncurl -i https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fopenapi.json\n# ... ETag: \"a1b2c3d4e5f6a7b8\"\n\n# Повторный вызов — 304 Not Modified, тело не передаётся\ncurl -i -H 'If-None-Match: \"a1b2c3d4e5f6a7b8\"' \\\n  https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fopenapi.json\n```\n\nЗаголовок `max-age=300` также разрешает клиенту и промежуточному кэшу повторно использовать ответ в течение 5 минут, не обращаясь к серверу.\n\n## Сводные лимиты\n\n| Сценарий | Ограничение |\n|----------|-------------|\n| `GET \u002Fv1\u002F{entity}` — `limit` | до 5000 записей, при `limit > 50` — авто-пагинация на стороне Вайбкод |\n| `POST \u002Fv1\u002Fbatch` — число вызовов | до 50 в одном запросе |\n| `POST \u002Fv1\u002F{entity}\u002Fbatch` — массовый CRUD | до 500 записей в одном запросе |\n| `POST \u002Fv1\u002F{entity}\u002Fbatch` — чтение `list` \u002F `get` \u002F `fields` | до 50 вызовов в массиве `calls` |\n| `POST \u002Fv1\u002F{entity}\u002Fsearch` — `limit` | до 5000 записей, при диапазоне дат > 14 дней — окна по 7 дней |\n| `POST \u002Fv1\u002Fchats\u002Fmessages\u002Fbulk` — диалогов | до 50 в одном запросе |\n| Очередь портала | 2 одновременных запроса, ожидание до 30 секунд |\n| Кэш ответов `\u002Fv1\u002Fusers` \u002F `\u002Fv1\u002Fstatuses` | 60 секунд \u002F 5 минут, обход — заголовок `Cache-Control: no-cache` |\n\n## Смотрите также\n\n- [Пакетные вызовы](\u002Fdocs\u002Fbatch)\n- [Синтаксис фильтрации](\u002Fdocs\u002Ffiltering)\n- [Entity API](\u002Fdocs\u002Fentity-api)\n- [Коды ошибок](\u002Fdocs\u002Ferrors)\n- [Быстрый старт](\u002Fdocs\u002Fquickstart)\n","2026-07-02",{}]