#Limits and optimization

The Vibe API merges calls and paginates result sets server-side on its own. This article describes the built-in mechanisms and helps you pick the right endpoint for the job.

Base URL: https://vibecode.bitrix24.tech/v1 | Authorization: X-Api-Key

#Auto-pagination in `list`

The limit parameter in GET /v1/{entity} accepts values up to 5000. When limit > 50, VibeCode splits the result set into internal pages of 50 records and assembles them into a single response:

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

Returns up to 500 records plus the meta.total and meta.hasMore meta fields. If more than 5000 records match the filter, the response is truncated and meta.truncated comes back true — for the remainder, either narrow the filter or use POST /v1/{entity}/search.

#Batch calls across multiple entities

POST /v1/batch merges up to 50 operations over different entities into a single HTTP request. Each call is identified by its own id; a failure in one does not cancel the others.

Suited to dashboards and summary pages where a single load needs data from different places:

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 }
  ]
}

Full specification — Batch calls.

#Batch operations on a single entity

POST /v1/{entity}/batch bulk-creates, updates, or deletes up to 500 records of a single entity. Internally VibeCode splits the request into batches of 50 items:

Terminal
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" }
    ]
  }'

Actions: create, update, delete, list, get, fields. For delete pass an ids array; for the rest — items (CUD) or calls (read).

When you need to read thousands of records of one entity and pull in related data from other entities, the flow looks like this:

  1. Fetch the root list in a single call — VibeCode paginates server-side on its own:

    GET /v1/deals?limit=5000&select=id,title,companyId,assignedById
  2. Collect the id values of the related entities and load them in batches of 50 via 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 }
      ]
    }

    One HTTP request — up to 50 related records; the loop repeats for the next batch of id values.

In this scenario the root fetch takes one network request (VibeCode raises the pages of 50 on its own), and the associations are loaded at a pace of one HTTP request per 50 items instead of a request per item.

#Search with date windowing

POST /v1/{entity}/search is designed for large result sets. If the filter contains a date condition with a range greater than 14 days, VibeCode automatically splits the request into 7-day windows and processes them in parallel:

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

The response includes meta fields:

Field Description
meta.windowCount Number of windows the request was split into.
meta.windowErrors Number of windows for which Bitrix24 returned an error. The remaining windows return their data.
meta.truncated true if more than 5000 records matched the filter and part of them fell outside the result set.

Windowing is disabled with the "autoWindow": false flag in the request body — appropriate during network timeouts or unstable output. The full list of search parameters is in each entity's documentation.

#Aggregation instead of fetching records

When you only need counts, sums, minimums, maximums, or averages — POST /v1/{entity}/aggregate returns the result in a single call, without fetching the records themselves:

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

The response contains data.count, data.aggregates and (when groupBy is present) a data.groups array broken down by the grouping field. Without groupBy the response is limited to a single object of summary values. For the count function Bitrix24 returns the result in a single call regardless of result-set size; sum / avg / min / max load records page by page up to 5000 — at a larger volume meta.truncated: true comes back.

#Portal queue

Each Bitrix24 portal has its own queue to the Vibe API: up to two requests run concurrently, the rest wait for a slot. If a request hangs in the queue longer than 30 seconds, 504 QUEUE_TIMEOUT is returned with a userMessage and a hint.

What reduces the load on the queue:

  • Merge disparate calls via POST /v1/batch — one HTTP request instead of several.
  • For bulk CRUD on a single entity — POST /v1/{entity}/batch (up to 500 records per request).
  • For wide result sets — GET /v1/{entity}?limit=... (VibeCode paginates server-side) or POST /v1/{entity}/search (date windowing).
  • When you need numbers, not records — POST /v1/{entity}/aggregate.

Retry on queue overload. Under load the queue returns two different codes, and both mean "retry later":

  • 503 QUEUE_OVERFLOW — the queue is full, the request is rejected immediately (within milliseconds). The Retry-After header carries the recommended pause in seconds.
  • 504 QUEUE_TIMEOUT — the request waited for a slot longer than 30 seconds. The error.retryAfter body field carries the recommended pause.

Analytics widgets that fire a burst of /search calls in a row must retry with exponential backoff and random jitter, respecting Retry-After, rather than retrying instantly in a loop — that makes the overload worse. Lower the concurrency: run the requests sequentially or combine them into 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 !== 503 && res.status !== 504) return res
    if (attempt >= maxRetries) return res

    // 503 — pause in the Retry-After header; 504 — in the error.retryAfter body field
    const headerWait = Number(res.headers.get('Retry-After'))
    let bodyWait = 0
    if (res.status === 504) {
      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))
  }
}

#Loading messages from multiple dialogs

POST /v1/chats/messages/bulk returns messages from no more than 50 dialogs in a single response and accepts lastId / firstId cursors and a limit per dialog:

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

Scope: im. Response format — { results, errors, summary }, the same as /v1/batch.

#Summary limits

Scenario Limit
GET /v1/{entity}limit up to 5000 records; when limit > 50, auto-pagination on the VibeCode side
POST /v1/batch — number of calls up to 50 in one request
POST /v1/{entity}/batch — bulk CRUD up to 500 records in one request
POST /v1/{entity}/batch — read actions (list / get / fields) up to 50 calls in the calls array
POST /v1/{entity}/searchlimit up to 5000 records; for a date range > 14 days — 7-day windows
POST /v1/chats/messages/bulk — dialogs up to 50 in one request
Portal queue 2 concurrent requests, wait up to 30 seconds

#See also