
# Лимиты и оптимизация

Вайбкод сам объединяет вызовы и пагинирует выборки на стороне сервера. Эта статья описывает встроенные механизмы и подсказывает, какой эндпоинт выбирать под задачу.

**Базовый URL:** `https://vibecode.bitrix24.tech/v1` | **Авторизация:** `X-Api-Key`

[Авто-пагинация](#авто-пагинация-в-list) | [Листание и количество](#листание-и-количество-записей) | [Выгрузка большой коллекции](#выгрузка-большой-коллекции) | [Пакетные вызовы](#пакетные-вызовы-по-нескольким-сущностям) | [Поиск по датам](#поиск-с-разбиением-по-датам) | [Агрегация](#агрегация-вместо-выборки-записей) | [Очередь портала](#очередь-портала) | [Клиентский таймаут](#клиентский-таймаут) | [Кэширование](#кэширование) | [Сводные лимиты](#сводные-лимиты)

## Авто-пагинация в `list`

Параметр `limit` в `GET /v1/{entity}` принимает значения до 5000. Если `limit > 50`, Вайбкод сам разбивает выборку на внутренние страницы по 50 записей и собирает их в один ответ:

```
GET /v1/deals?limit=500&filter[stageId]=NEW
```

Возвращается до 500 записей плюс мета-поле `meta.hasMore` (и `meta.total`, если количество заказывалось — см. ниже). Если под фильтр попадает больше 5000 записей, в выборку попадают первые 5000, а `meta.hasMore` приходит `true` — для остатка нужно либо сузить фильтр, либо использовать `POST /v1/{entity}/search`.

Этот режим рассчитан на выборки в пределах 5000 записей. Коллекция в десятки тысяч записей читается курсором — [Выгрузка большой коллекции](#выгрузка-большой-коллекции).

## Листание и количество записей

Это две разные задачи, и решаются они разными полями. Смешивать их — самая дорогая ошибка на списках.

**Листание идёт по `meta.hasMore`.** Признак выводится из полноты страницы: пришла полная — возможно, есть ещё, пришла неполная — коллекция кончилась. Коллекция ровно кратна `limit` — последний шаг вернёт пустой список, это штатный признак конца. Поле описывает состояние отдельного ответа, а не обещает неизменяемый снимок коллекции.

Если ответ метода содержит `meta.nextAfterId`, для больших обходов используйте сортировку строго по `id` по возрастанию, отключите точный подсчёт через `withTotal=false` и запросите через `select` только нужные поля вместе с `id`. Поле `meta.nextAfterId` несёт идентификатор последней отданной записи. Передайте его обратно как `filter[>id]`, и следующая страница начнётся за ним. Такой обход не зависит от смещения и не дорожает к концу коллекции:

```
GET /v1/deals?order[id]=asc&limit=50&withTotal=false&select=id,title
GET /v1/deals?order[id]=asc&limit=50&withTotal=false&select=id,title&filter[>id]=<meta.nextAfterId предыдущего ответа>
```

Курсор доступен у сделок, лидов, контактов, компаний, предложений и элементов смарт-процессов. У остальных сущностей `meta.nextAfterId` в ответе нет: страницы там берутся через `offset`, а размер выборки сокращается фильтром. Готовый цикл обхода на JavaScript — [Постраничный вывод](./filtering.md#постраничный-вывод).

На внутреннем уровне этот режим следует рекомендованной схеме Битрикс24: `start=-1`, `order=ID ASC`, фильтр `ID` больше последнего полученного идентификатора. Клиент не передаёт `start` в запросе к API Вайбкод. Вайбкод применяет его только к методам, для которых подтверждена совместная работа фильтра по `id` и `start=-1`. Для остальных методов сервис сохраняет корректность вызова и может не применить режим без подсчёта.

Такой обход предполагает, что записи не удаляются, а права доступа не меняются до его завершения. Если любое из условий нарушено, часть записей может быть пропущена. API Вайбкод не обещает полный обход как инвариант клиента.

**Количество берётся из доступной операции агрегации.** Подсчёт коллекции стоит Битрикс24 несоразмерно дорого — заметно дороже, чем отдать страницу. Если нужна точная цифра, сначала прочитайте `operations.search.paginationStability.counting` сущности в `GET /v1/guide`. Когда указание содержит путь агрегации, спросите число одним вызовом [агрегации](#агрегация-вместо-выборки-записей) с функцией `count`. Когда указание есть, но пути нет, дешёвого точного подсчёта нет: читайте `meta.total`, только когда поле пришло, а обход ограничивайте по `meta.hasMore`. Если весь блок `counting` отсутствует вместе с общей операцией поиска, не угадывайте путь агрегации — перейдите по указателю на документацию сущности или домена из того же руководства и используйте только явно описанную операцию счёта. Не нужна цифра — отключите подсчёт параметром `withTotal=false`. У `POST /v1/{entity}/search` это одноимённое поле тела. В поддерживаемом режиме `meta.total` в ответе не придёт, а отдельный `COUNT` не выполняется. **Важно:** как приём оптимизации `withTotal=false` работает только при `limit` не больше 50 — там подсчёт действительно не заказывается. При `limit` больше 50 подсчёт нужен платформе, чтобы спланировать обход, поэтому параметр убирает число, а не нагрузку, и вдобавок отбрасывает точное количество, которое короткая первая страница отдала бы бесплатно.

Если `withTotal` не передан, значение берётся из настройки `totalDefault` на API-ключе, а при её отсутствии — из платформенного умолчания. Действующее сейчас значение показывает блок `totalDefault` в [GET /v1/me](./keys-auth/me.md).

Отключённый подсчёт не всегда означает отсутствие цифры: на вызове с `offset = 0`, где страница пришла короче запрошенного `limit`, точное количество известно из самой страницы и приходит бесплатно. Явный `withTotal=false` в запросе убирает и его — полная таблица присутствия поля в разделе [Листание и количество записей](./entity-api.md#листание-и-количество-записей).

Обратное тоже верно: присутствие `meta.total` не означает, что за него заплатили подсчётом. На многостраничном вызове (`limit` больше 50) платформа выполняет подсчёт, только когда без него не обойтись, — форма ответа при этом не меняется, `meta.total` приходит как приходил. Отдельного действия от вас это не требует.

**Не эмулируйте счётчик обходом.** Пролистать пять тысяч записей, чтобы узнать, что их 4863, — это сто вызовов вместо одного, и для учётной записи Битрикс24 это худшая из возможных нагрузок. Если операция агрегации доступна, один `aggregate` с `count` даёт ту же цифру за один вызов.

`meta.total` — информативное поле. Оно может отставать от текущего состояния коллекции до минуты, поэтому число отданных строк иногда оказывается больше него. Полнота выдачи от `meta.total` не зависит.

## Выгрузка большой коллекции

Широкая выборка одним вызовом рассчитана на коллекции, которые целиком помещаются в 5000 записей. Когда под фильтр попадают десятки тысяч записей, задача меняет форму: такую коллекцию читают курсором, а не одним широким `limit`.

Курсор описан выше, в разделе [Листание и количество записей](#листание-и-количество-записей): сортировка строго по `id` по возрастанию, `withTotal=false`, `select` с нужными полями и обязательным `id`, а `meta.nextAfterId` предыдущего ответа уходит обратно в `filter[>id]`. Каждый вызов читает одну короткую страницу, поэтому обход не дорожает к концу коллекции, а прерванный продолжается с последнего курсора, не начиная сначала. Готовый цикл на JavaScript — [Постраничный вывод](./filtering.md#постраничный-вывод).

Признак того, что выборка не уложилась в отведённое ей время, — `429` с кодом [`OPERATION_TIME_LIMIT`](/docs/errors/limits#operation_time_limit-429) или `RATE_LIMITED`. Оба означают паузу для вызывающего ключа, и повтор того же широкого запроса приходит к тому же результату — переходите на курсор.

### Отбор по пользовательскому полю

Отбор по значению пользовательского поля выполняет сам портал: условие передаётся в `filter` тем же именем, под которым поле стоит в схеме сущности — написание разобрано в разделе [Фильтрация](./filtering.md). Это первый способ, потому что читать приходится совпадения, а не всю коллекцию.

Если на большой коллекции такой запрос приходит в отказ по времени, возьмите поле в `select` и отберите нужные записи на своей стороне. Пользовательское поле передаётся в `select` наравне со стандартными, и курсор при этом работает.

```
GET /v1/companies?order[id]=asc&limit=50&withTotal=false&select=id,title,ufCrm_1698325419&filter[>id]=<meta.nextAfterId предыдущего ответа>
```

В ответе приходит значение поля по каждой записи, дальше отбор идёт без обращения к порталу. Этот путь читает коллекцию целиком, поэтому он запасной. Имена пользовательских полей портала перечисляет `GET /v1/{entity}/fields`.

## Пакетные вызовы по нескольким сущностям

`POST /v1/batch` объединяет до 50 операций над разными сущностями в один HTTP-запрос. Каждый вызов идентифицируется собственным `id`, ошибка одного не отменяет остальные.

Подходит для дашбордов и страниц-сводок, где одной загрузкой нужны данные из разных мест:

```json
{
  "calls": [
    { "id": "deals", "entity": "deals", "action": "list", "params": { "filter": { "stageId": "NEW" }, "limit": 50 } },
    { "id": "tasks", "entity": "tasks", "action": "list", "params": { "filter": { "responsibleId": 1 }, "limit": 20 } },
    { "id": "user", "entity": "users", "action": "get", "entityId": 1 }
  ]
}
```

Полная спецификация — [Пакетные вызовы](/docs/batch).

## Пакетные операции по одной сущности

`POST /v1/{entity}/batch` массово создаёт, обновляет или удаляет до 500 записей одной сущности. Внутри Вайбкод разбивает запрос на пакеты по 50 элементов:

```bash
curl -X POST https://vibecode.bitrix24.tech/v1/deals/batch \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "update",
    "items": [
      { "id": 575, "stageId": "WON" },
      { "id": 741, "stageId": "WON" }
    ]
  }'
```

Действия: `create`, `update`, `delete`, `list`, `get`, `fields`. Для `delete` передаётся массив `ids`, для `create` и `update` — `items`, для `list` / `get` / `fields` — `calls`.

## Массовое сканирование с дозагрузкой связанных данных

Когда нужно прочитать тысячи записей одной сущности и подтянуть к ним связанные данные из других сущностей, поток выглядит так:

1. Выгрузить корневой список одним вызовом — Вайбкод сам пагинирует на сервере:

   ```
   GET /v1/deals?limit=5000&select=id,title,companyId,assignedById
   ```

2. Собрать `id` связанных сущностей и догрузить пакетами по 50 через `POST /v1/batch`:

   ```json
   {
     "calls": [
       { "id": "company-15", "entity": "companies", "action": "get", "entityId": 15 },
       { "id": "company-22", "entity": "companies", "action": "get", "entityId": 22 },
       { "id": "user-1",     "entity": "users",     "action": "get", "entityId": 1 }
     ]
   }
   ```

   Один HTTP-запрос — до 50 связанных записей. Цикл повторяется для следующего пакета `id`.

В таком сценарии корневая выгрузка занимает один сетевой запрос: Вайбкод сам поднимает страницы по 50. Ассоциации догружаются в темпе один HTTP-запрос на 50 элементов вместо запроса на каждый элемент.

## Поиск с разбиением по датам

`POST /v1/{entity}/search` рассчитан на крупные выборки. Если в фильтре есть условие по дате с диапазоном больше 14 дней, Вайбкод автоматически делит запрос на окна по 7 дней и обрабатывает их параллельно:

```json
{
  "filter": { "createdAt": { "$gte": "2026-01-01", "$lte": "2026-04-30" } },
  "select": ["id", "title", "stageId"],
  "limit": 5000
}
```

При частичном отказе окон, когда часть окон вернула данные и ответ приходит с HTTP 200, в `meta` появляются поля:

| Поле | Описание |
|------|---------|
| `meta.autoWindowed` | `true`, когда запрос был разбит на окна по датам. |
| `meta.windowCount` | Количество окон, на которые был разбит запрос. |
| `meta.windowErrors` | Количество окон, по которым Битрикс24 вернул ошибку. Остальные окна возвращают свои данные. |
| `meta.windowErrorSample` | Объект `{ code, message }` — код и текст первого сбойного окна, чтобы видеть причину потери данных. |
| `meta.batchWaves` | Количество волн параллельной отправки окон. Приходит, когда сработала пакетная отправка окон. |
| `meta.hasMore` | Есть ли записи за пределами `limit`. При выборке больше 5000 записей часть остаётся за границей — сузьте фильтр или диапазон дат. |

Если упали **все** окна, этого мета-блока нет — возвращается реальный код ошибки Битрикс24, как для узкого диапазона: `UNKNOWN_FILTER_FIELD`, `INVALID_PARAMS`, `BITRIX_ACCESS_DENIED`, `RATE_LIMITED`, `BITRIX_UNAVAILABLE` или `BITRIX_TIMEOUT` (503). Отдельный код `WINDOWED_SEARCH_FAILED` больше не возвращается.

Разбиение отключается флагом `"autoWindow": false` в теле запроса — применяйте его при таймаутах сети или нестабильной выдаче. Полный список параметров `search` — в документации каждой сущности.

### Неполная выдача

Выдача бывает неполной и тогда, когда ни одно окно не упало. Ответ в этом случае несёт массив `meta.warnings` с записью `{ "code": "WINDOW_TRUNCATED", "field": "...", "message": "..." }`. Причин две, и называет сработавшую текст `message`: либо одно окно держало больше записей, чем возвращает одно чтение окна, либо чтения окон в сумме упёрлись в предельные 5000 записей, и оставшиеся окна не отправлялись. Код у обеих причин один — ветвиться по нему можно, не разбирая текст. Предупреждение приходит на сущностях, чей список Битрикс24 отдаёт постранично.

Поле `field` называет поле диапазона исходным именем Битрикс24, а не тем, которое вы отправили: у сущностей с собственными именами полей это разные строки. Сопоставление имён — в справочнике полей сущности, например [`GET /v1/deals/fields`](./entities/deals/fields.md). Сравнивать `field` с ключами своего фильтра напрямую нельзя.

Ниже — блок `meta` поиска по сделкам за два года. Массив `data` в таком ответе заполнен и содержит набранные записи:

```json
{
  "total": 5000,
  "hasMore": false,
  "autoWindowed": true,
  "windowCount": 105,
  "batchWaves": 2,
  "durationMs": 41230,
  "warnings": [
    {
      "code": "WINDOW_TRUNCATED",
      "field": "createdTime",
      "message": "This result is incomplete: the range filter on \"createdTime\" reached the 5000-row ceiling of a windowed search, so the remaining time windows were never requested. Narrow the date range or add filters — paging is not available on a windowed search."
    }
  ]
}
```

Проверяйте `meta.warnings` до того, как считать выдачу полной. Ни `meta.hasMore`, ни `meta.total` для этого не годятся: в примере выше оба говорят, что выдача закончилась, — они описывают набранное, а не то, что осталось за границей. Пагинация тоже не спасает: при активном разбиении по датам смещение больше нуля отклоняется кодом `400 UNSTABLE_OFFSET_PAGINATION`, поэтому дочитать остаток следующей страницей не получится. Сузьте диапазон дат или добавьте фильтров, чтобы поиск перестал упираться в потолок. Поиск по узкому диапазону, который на окна не разбивается, этим предупреждением не затронут.

## Агрегация вместо выборки записей

Когда нужны суммы, минимумы, максимумы или средние, сначала убедитесь, что `GET /v1/guide` показывает для сущности `operations.aggregate`. После этого `POST /v1/{entity}/aggregate` возвращает результат без отдельной выгрузки записей:

```json
{
  "aggregate": [
    { "field": "amount", "function": "sum" },
    { "field": "amount", "function": "avg" }
  ],
  "filter": { "stageId": "WON" },
  "groupBy": "assignedById"
}
```

Ответ содержит `data.count`, `data.aggregates` и массив `data.groups` с разбивкой по полю группировки. Массив `data.groups` присутствует только при заданном `groupBy` — без него ответ ограничен одним объектом сводных значений. Для функции `count` результат возвращается одним вызовом независимо от размера выборки. Функции `sum` / `avg` / `min` / `max` подгружают записи постранично до 5000 штук. Ответ приходит с `meta.truncated: true`, когда прочитано меньше записей, чем обещал `data.count`, — в том числе когда под фильтр попало больше 5000, — либо когда срез оборвался ошибкой подстраницы. Размер нехватки лежит в `data.meta.recordsShortfall`, оборванный срез — в `data.meta.pageErrorSample`. Потолок не единственная причина этой пометки, поэтому проверять её значением надёжнее, чем сравнивать `data.count` с 5000.

Для функции `count` действует отдельный [канон листания и количества записей](#листание-и-количество-записей). Используйте её только по пути, явно указанному в `operations.search.paginationStability.counting`. Наличие `operations.aggregate` для других функций не доказывает доступность точного счёта, и наоборот. Пример ниже применим только к явно указанному пути. Не обходите коллекцию ради подсчёта:

```json
{
  "aggregate": [ { "field": "*", "function": "count" } ],
  "filter": { "stageId": "NEW" }
}
```

## Очередь портала

У каждого портала Битрикс24 своя очередь к API Вайбкод: одновременно выполняется ограниченное число запросов, остальные ждут места. Если запрос провисел в очереди дольше 30 секунд, возвращается `429 QUEUE_TIMEOUT` с подсказкой `userMessage` и `hint`.

Что снижает нагрузку на очередь:

- Объединять разнородные обращения через `POST /v1/batch` — один HTTP-запрос вместо нескольких.
- Для массового CRUD по одной сущности — `POST /v1/{entity}/batch`, до 500 записей за запрос.
- Для широких выборок — `GET /v1/{entity}?limit=...` с пагинацией на стороне сервера или `POST /v1/{entity}/search` с разбивкой по датам.
- Когда нужны агрегаты, а не записи, и `GET /v1/guide` показывает `operations.aggregate` — `POST /v1/{entity}/aggregate`. Точный счёт использует только путь из `operations.search.paginationStability.counting`.

**Повтор при перегрузке очереди.** Под нагрузкой очередь возвращает два разных кода, и оба означают «повтори позже»:

- `429 QUEUE_OVERFLOW` — очередь переполнена, запрос отклонён сразу, за миллисекунды. В заголовке `Retry-After` — рекомендованная пауза в секундах.
- `429 QUEUE_TIMEOUT` — запрос ждал места дольше 30 секунд. Запрос не был отправлен в Битрикс24 — безопасно повторить. В теле `error.retryAfter` — рекомендованная пауза.

Виджеты аналитики, которые шлют пачку `/search` подряд, должны делать повтор с экспоненциальной задержкой и случайным разбросом по времени, учитывая `Retry-After`, а не повторять мгновенно в цикле — это усугубляет перегрузку. Снизьте параллелизм: выполняйте запросы последовательно или объедините их в `POST /v1/batch`.

```javascript
async function callWithBackoff(url, options, maxRetries = 4) {
  for (let attempt = 0; ; attempt++) {
    const res = await fetch(url, options)
    if (res.status !== 429) return res
    if (attempt >= maxRetries) return res

    // Оба кода (QUEUE_OVERFLOW, QUEUE_TIMEOUT) отдают паузу в заголовке Retry-After;
    // error.retryAfter в теле дублирует то же значение
    const headerWait = Number(res.headers.get('Retry-After'))
    const bodyWait = Number((await res.clone().json())?.error?.retryAfter) || 0
    const baseSec = headerWait || bodyWait || Math.min(2 ** attempt, 30)
    const jitterMs = Math.floor(Math.random() * 1000)
    await new Promise(r => setTimeout(r, baseSec * 1000 + jitterMs))
  }
}
```

**Паузы по отдельному методу.** Кроме очереди запрос отбивают ещё две паузы. Обе отвечают `429` с заголовком `Retry-After` и снимаются автоматически, поэтому цикл повтора выше подходит и для них. Поле `error.scope` говорит, на кого пауза распространяется:

- `429 OPERATION_TIME_LIMIT`, `scope: "apiKey"` — Битрикс24 приостановил этот метод для вашего ключа примерно на 5 минут: метод исчерпал бюджет рабочего времени на портале. Остальные методы и другие ключи портала работают.
- `429 TIMEOUT_QUARANTINE`, `scope: "portal"` — метод несколько раз подряд не ответил порталу за отведённое вызову время, и пара «портал + метод» поставлена на паузу на стороне Вайбкод. Пауза действует для всех ключей портала. Не сокращайте интервал повторов: раз в 5 минут один вызов пропускается как проба восстановления, и частый повтор занимает этот слот собой — метод остаётся закрытым дольше. Механизм в процессе раскатки: пока он не включён на портале, этот код не приходит.

Эти паузы измеряются минутами, а не секундами, как отказы очереди. Ждать их в пользовательском запросе не стоит — переносите повтор в фоновую задачу.

Описание обоих кодов — [Ошибки](/docs/errors).

## Клиентский таймаут

Ответ приходит не мгновенно: запрос сначала ждёт места в очереди портала, потом выполняется в Битрикс24. Обе фазы ограничены сверху, как и удержание соединения платформой, а таймаут на стороне клиента должен превышать сумму первых двух.

| Фаза | Предел | Что приходит по истечении |
|------|--------|---------------------------|
| Ожидание места в очереди портала | 30 секунд | `429 QUEUE_TIMEOUT` — в Битрикс24 запрос не ушёл, повтор безопасен |
| Один вызов в Битрикс24 | 15 секунд | `503 BITRIX_TIMEOUT` |
| Удержание соединения платформой | 660 секунд | соединение закрывается |

Отсюда рабочее значение: таймаут ожидания ответа — от 60 секунд, а не 30. Ожидание в очереди запрос проходит один раз, за себя целиком, а не за каждую страницу. Дальше запрос с `limit > 50` читает записи несколькими последовательными вызовами по 50 штук, каждый со своим пределом в 15 секунд, поэтому широкая выборка отвечает дольше одной страницы.

Что помогает вместо одного длинного запроса:

- Читать страницами по 50 записей и идти [курсором](#листание-и-количество-записей). Каждый вызов короткий, а прерванный обход продолжается с последнего `meta.nextAfterId`, не начиная сначала.
- Задавать таймауты раздельно — на установление соединения и на ожидание данных. В Python: `requests.get(url, headers=headers, timeout=(10, 60))` — 10 секунд на соединение, 60 на ожидание данных от сервера.
- Для выборок по широкому диапазону дат — [`POST /v1/{entity}/search`](#поиск-с-разбиением-по-датам): диапазон разбивается на окна, которые выполняются параллельными волнами.

## Загрузка сообщений из нескольких диалогов

`POST /v1/chats/messages/bulk` возвращает сообщения не более чем из 50 диалогов в одном ответе и принимает курсоры `lastId` / `firstId` и `limit` для каждого диалога:

```json
{
  "dialogs": [
    { "dialogId": "chat253", "limit": 20 },
    { "dialogId": "chat741", "lastId": 9357, "limit": 50 }
  ]
}
```

Скоуп: `im`. Формат ответа — `{ results, errors, summary }`, аналогично `/v1/batch`.

## Кэширование

Часть ответов отдаётся из кэша, чтобы повторные чтения не нагружали Битрикс24 и очередь портала. Кэш прозрачен — тело ответа совпадает с некэшированным, а заголовок `X-Cache` показывает, откуда пришёл ответ.

### Кэш ответов `/v1/users`, `/v1/statuses` и `/v1/{entity}/fields`

Ответы `GET /v1/users`, `GET /v1/statuses` и `GET /v1/{entity}/fields` кэшируются на стороне сервера. Пользователи — 60 секунд. Справочники CRM и схемы полей — 5 минут. Это ускоряет дашборды, которые запрашивают эти эндпоинты при каждой загрузке.

Кэш `GET /v1/users` привязан к личному ключу `vibe_api_...`. Кэш `GET /v1/statuses` привязан к порталу: личные ключи одного портала используют одну запись, потому что стадии и справочники CRM общие для портала.

Кэш `GET /v1/{entity}/fields` привязан к порталу, ключу авторизации, сущности, параметрам пути, параметрам запроса и языку ответа. Полные ответы сохраняются на 5 минут. Ответы с предупреждением `fields_partial` не сохраняются, чтобы следующий запрос мог получить полную схему полей.

Для ключа авторизации OAuth-приложения `vibe_app_...` кэш не используется — у разных пользователей разный доступ к данным портала.

Запись через API сбрасывает соответствующий кэш сразу. `POST /v1/users`, `PATCH /v1/users/:id` и аналогичные операции сбрасывают кэш пользователей и справочников. `POST` / `PATCH` / `DELETE` на `/v1/userfields/:entity` и `/v1/items/:entityTypeId/userfields` сбрасывают кэш `GET /v1/{entity}/fields` для этой сущности или смарт-процесса. Правка напрямую в интерфейсе Битрикс24 кэшу не видна, поэтому такие изменения могут отображаться с задержкой до конца времени жизни кэша: до 60 секунд для пользователей и до 5 минут для справочников и схем полей.

Чтобы получить заведомо свежие данные в обход кэша, добавьте заголовок `Cache-Control: no-cache`. Для `GET /v1/{entity}/fields` также работает параметр `refresh=true`:

```bash
curl -H "X-Api-Key: YOUR_API_KEY" \
  -H "Cache-Control: no-cache" \
  https://vibecode.bitrix24.tech/v1/users
```

Заголовок `X-Cache` в ответе показывает, как был обработан запрос:

| Значение | Что означает |
|----------|--------------|
| `HIT` | Ответ отдан из кэша |
| `MISS` | Ответ получен из Битрикс24 и сохранён в кэш |
| `COALESCED` | Запрос присоединился к уже выполняющемуся обращению за теми же данными |
| `BYPASS` | Кэш не использовался — ключ авторизации OAuth-приложения, заголовок `Cache-Control: no-cache` или `refresh=true` для `/fields`. Причина указывается в заголовке `X-Cache-Bypass-Reason` |

### HTTP-кэш служебных эндпоинтов

`GET /v1/openapi.json` и `GET /v1/guide` возвращают объёмные документы, которые клиенту незачем перекачивать при каждом запуске. Оба ответа несут стандартные заголовки HTTP-кэша:

- `/v1/openapi.json` — `Cache-Control: public, max-age=300`. Спецификация одинакова для всех клиентов, поэтому её может хранить любой кэш.
- `/v1/guide` — `Cache-Control: private, max-age=300`. Состав ответа зависит от скоупов ключа, поэтому общий кэш хранить его не должен.
- `ETag` — отпечаток содержимого, меняется только при изменении документа или набора скоупов ключа.

Сохраните `ETag` из первого ответа и передавайте его в заголовке `If-None-Match` при повторных запросах. Если содержимое не изменилось, эндпоинт отвечает `304 Not Modified` с пустым телом вместо повторной передачи всего документа:

```bash
# Первый вызов — полное тело и ETag (openapi.json не требует авторизации)
curl -i https://vibecode.bitrix24.tech/v1/openapi.json
# ... ETag: "a1b2c3d4e5f6a7b8"

# Повторный вызов — 304 Not Modified, тело не передаётся
curl -i -H 'If-None-Match: "a1b2c3d4e5f6a7b8"' \
  https://vibecode.bitrix24.tech/v1/openapi.json
```

Заголовок `max-age=300` также разрешает клиенту и промежуточному кэшу повторно использовать ответ в течение 5 минут, не обращаясь к серверу.

## Сводные лимиты

| Сценарий | Ограничение |
|----------|-------------|
| `GET /v1/{entity}` — `limit` | до 5000 записей, при `limit > 50` — авто-пагинация на стороне Вайбкод |
| `POST /v1/batch` — число вызовов | до 50 в одном запросе |
| `POST /v1/{entity}/batch` — массовый CRUD | до 500 записей в одном запросе |
| `POST /v1/{entity}/batch` — чтение `list` / `get` / `fields` | до 50 вызовов в массиве `calls` |
| `POST /v1/{entity}/search` — `limit` | до 5000 записей, при диапазоне дат > 14 дней — окна по 7 дней |
| `POST /v1/chats/messages/bulk` — диалогов | до 50 в одном запросе |
| Очередь портала | ограниченное число одновременных запросов, ожидание до 30 секунд |
| Кэш ответов `/v1/users` / `/v1/statuses` / `/v1/{entity}/fields` | 60 секунд / 5 минут / 5 минут, обход — заголовок `Cache-Control: no-cache`, для `/fields` также `refresh=true` |

## Смотрите также

- [Batch](/docs/batch)
- [Синтаксис фильтрации](/docs/filtering)
- [Entity API](/docs/entity-api)
- [Коды ошибок](/docs/errors)
- [Быстрый старт](/docs/quickstart)
