#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:
{
"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:
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).
#Bulk scanning with on-demand loading of related data
When you need to read thousands of records of one entity and pull in related data from other entities, the flow looks like this:
Fetch the root list in a single call — VibeCode paginates server-side on its own:
GET /v1/deals?limit=5000&select=id,title,companyId,assignedByIdCollect the
idvalues of the related entities and load them in batches of 50 viaPOST /v1/batch:{ "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
idvalues.
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:
{
"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:
{
"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) orPOST /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). TheRetry-Afterheader carries the recommended pause in seconds.504 QUEUE_TIMEOUT— the request waited for a slot longer than 30 seconds. Theerror.retryAfterbody 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.
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:
{
"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}/search — limit |
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
- Batch calls — full specification of
POST /v1/batch - Filtering syntax — three filter syntaxes for
list,search,aggregate - Entity API — entity reference and single-record endpoints
- Error codes — general reference
- Quickstart — your first request in two minutes