Para agentes de IA: markdown desta página — /docs-content-en/batch.md índice da documentação — /llms.txt

Os artigos da documentação estão disponíveis atualmente em inglês.

Batch calls

A single HTTP request combines up to 50 operations across different entities. Each call is identified by its own id and processed independently: an error in one call does not cancel the others.

POST /v1/batch

Scope: checked individually per entity (crm, task, im, disk, etc.) | Base URL: https://vibecode.bitrix24.com/v1 | Authorization: X-Api-Key (APP key)

Request fields (body)

Field Type Req. Description
calls array Array of calls (1 to 50). Each element is an object — its format is described in the table below.

Fields of a single call

Field Type Req. Description
id string no Call identifier in the response. If omitted, a sequential index is assigned ("0", "1", ...). Up to 64 characters.
entity string Plural entity name: deals, contacts, companies, tasks, users, files, folders, and others. The full list of entities available to your key is returned by GET /v1/me — see Keys and authorization.
action string Operation: list, get, create, update, delete, fields, search.
entityId number / string for get / update / delete Record identifier.
params object no Operation parameters in the unified entity format (field names in camelCase, filters in the filtering syntax).

The parameters inside params match the parameters of a single Entity API endpoint:

  • list and searchfilter, select, sort, limit. ⚠️ Sorting is passed under the name sort: this endpoint does not translate the field names in order into Bitrix24 names and forwards them verbatim, so the ordering is silently ignored. withTotal is separate: it takes effect only on list and only with a zero offset (see below). A select you pass also applies to the response: records keep only the listed fields plus id. Unknown names are listed in that call's meta as UNKNOWN_SELECT_FIELD warnings, and on entities with a verified field set they reject that sub-call with an UNKNOWN_SELECT_FIELD error — neighboring sub-calls still run. On requisites and bank details such a name breaks the sub-call itself instead of warning: the Bitrix24 refusal arrives in data.errors under the sub-call identifier with the code 100, and its neighbors in the batch still run. The value * (and UF_*, case-insensitive) means "return every field" — no selection is applied
  • get — the include field, if the entity supports it
  • create and update — entity fields (names in camelCase)
  • delete and fields — no parameters needed
  • smart processes (entity: "items") — entityTypeId is required inside params

The record count in a list call. withTotal: false in params means "the count is not needed": data.totals.<id> and meta.<id>.total are absent from the response. It is worth knowing where it applies: the parameter takes effect on calls with action: "list" — both when limit is at most 50 with a zero offset and when limit is above 50. On action: "search" and in the single-entity batch POST /v1/{entity}/batch it is inert — the count is requested as before. It is inert on list for the mail-mailboxes and humanresources-nodes entities too: the count always arrives there. Important: The parameter cancels the Bitrix24 count only when limit is at most 50. With a limit above 50, the platform needs the count to plan the sub-request's walk, so there the parameter removes the number, not the load. The rule for whether total is present is the same as on the single endpoints: a short page carries the exact count even when none was requested, and a call with the count switched off will not have it at an offset above zero — see Paging and record counts. The paging bound is meta.<id>.hasMore in every case.

Canceling the count on the Bitrix24 side — params.start: -1. Besides withTotal, a sub-call has a second, lower-level way to decline the count: the value -1 in params.start is a direct instruction to Bitrix24 not to count the collection. There is then no count in the Bitrix24 response either, so the platform does not publish one: the total key will be absent from both meta.<id> and data.totals.<id>. There is nowhere for the number to come from, and withTotal: true will not bring it back — the client canceled the count itself. The exception is calendar events: the platform receives their whole set in a single response and counts it itself, so total arrives in this mode too.

In this mode the end of the result set is determined by meta.<id>.hasMore, computed from the fullness of the page Bitrix24 returned: filled up to the Bitrix24 page size — 50 records — → true, shorter → false. With action: "list" the client's limit never reaches Bitrix24, so the ceiling stays those same 50. Your own limit is forwarded to Bitrix24 only with action: "search", and there a full page exactly at the limit ({"action":"search","params":{"limit":10,"start":-1}} with ten records) means hasMore: true, not "that is all".

The value is read the way Bitrix24 reads it — a fractional number is truncated, a string is parsed by its numeric prefix — so -1, "-1", -1.5, and "-1abc" all mean the same thing. A positive offset (start: 100) stays an ordinary counting cursor, and total arrives for it as before. A value that is not a number at all ("abc", an empty string, null, an object, an array, true) is treated as not passed: the page is the same as without start, but the sub-call then falls back to the platform's general rule for counting records and may arrive without total.

Important: start: -1 is not a cursor but a refusal to count: it always returns the beginning of the collection, and repeating the call with the same value returns the same page. To page further, switch to a positive offset (start: 100) — total arrives for it.

Check for the key (meta.<id>.total !== undefined) and treat hasMore as the sign of an unread remainder — that way the code works the same in both modes.

The record identifier for get / update / delete is passed in the entityId field at the call level, not in params.id. The call { "entity": "users", "action": "get", "params": { "id": 1 } } without entityId is rejected as MISSING_ENTITY_ID, and the whole batch returns 400 with a validation error. Correct: { "entity": "users", "action": "get", "entityId": 1 }. The params field for get is used only for include. update and delete are built the same way — the identifier goes in entityId, and the fields to change for update go in params: { "entity": "deals", "action": "update", "entityId": 575, "params": { "title": "New title" } } and { "entity": "contacts", "action": "delete", "entityId": 42 }. The success of update and delete is determined by data.summary.succeeded and the absence of the id in data.errors.

Examples

curl — personal key

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/batch \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "calls": [
      {
        "id": "deals",
        "entity": "deals",
        "action": "list",
        "params": {
          "filter": { "stageId": "NEW" },
          "select": ["id", "title", "amount"],
          "limit": 50,
          "withTotal": true
        }
      },
      {
        "id": "contacts",
        "entity": "contacts",
        "action": "list",
        "params": {
          "select": ["id", "name", "lastName"],
          "limit": 20,
          "withTotal": true
        }
      },
      {
        "id": "user1",
        "entity": "users",
        "action": "get",
        "entityId": 1
      }
    ]
  }'

curl — OAuth application

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/batch \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "calls": [
      {
        "id": "deals",
        "entity": "deals",
        "action": "list",
        "params": {
          "filter": { "stageId": "NEW" },
          "select": ["id", "title", "amount"],
          "limit": 50,
          "withTotal": true
        }
      },
      {
        "id": "contacts",
        "entity": "contacts",
        "action": "list",
        "params": {
          "select": ["id", "name", "lastName"],
          "limit": 20,
          "withTotal": true
        }
      },
      {
        "id": "user1",
        "entity": "users",
        "action": "get",
        "entityId": 1
      }
    ]
  }'

JavaScript — personal key

javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/batch', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    calls: [
      { id: 'deals', entity: 'deals', action: 'list', params: { filter: { stageId: 'NEW' }, select: ['id', 'title', 'amount'], limit: 50, withTotal: true } },
      { id: 'contacts', entity: 'contacts', action: 'list', params: { select: ['id', 'name', 'lastName'], limit: 20, withTotal: true } },
      { id: 'user1', entity: 'users', action: 'get', entityId: 1 }
    ]
  })
})

const { data } = await res.json()
console.log('Deals:', data.results.deals)
console.log('Contacts:', data.results.contacts)
console.log('Summary:', data.summary)

JavaScript — OAuth application

javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/batch', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    calls: [
      { id: 'deals', entity: 'deals', action: 'list', params: { filter: { stageId: 'NEW' }, select: ['id', 'title', 'amount'], limit: 50, withTotal: true } },
      { id: 'contacts', entity: 'contacts', action: 'list', params: { select: ['id', 'name', 'lastName'], limit: 20, withTotal: true } },
      { id: 'user1', entity: 'users', action: 'get', entityId: 1 }
    ]
  })
})

const { data } = await res.json()
console.log('Deals:', data.results.deals)

Response fields

Field Type Description
success boolean true if the request is accepted. Partial errors inside data.errors do not flip it to false.
data.results object Results keyed by the id of each call. The value is whatever the corresponding Entity API endpoint would return: an array of records for list / search, an object for get / create, a normalized record for update, an object { id, deleted: true } for delete, a field schema for fields.
data.totals object Total record count matching the filter for list / search calls. Keys are the id values of the corresponding calls. For a list call its presence follows the same rule as meta.total on a single list: no count requested, no key. For a search call the count is always requested. Plus a branch of its own: a negative params.start cancels the count on the Bitrix24 side, and then there is no number even with withTotal: true. Both cases are covered in the paragraphs "The record count in a list call" and "Canceling the count on the Bitrix24 side" above. A call that failed has no key either.
data.errors object Errors keyed by the id of failed calls. Each value is { "code": "...", "message": "..." }.
data.summary.total number Total number of calls in the request.
data.summary.succeeded number Number of successful calls.
data.summary.failed number Number of failed calls.
data.meta object Additional details keyed by the id of calls with action: "list" or action: "search": total (may be absent — see above), returned, hasMore, truncated, and — when a page of results is lost — pageErrorSample.
data.meta.<id>.warnings array Warnings for this call. Present only when there are any. The codes are the same ones a single endpoint returns, in the same shape — code, field, message. Read the array by code, not by position.

Response example

JSON
{
  "success": true,
  "data": {
    "results": {
      "deals": [
        { "id": 575, "title": "Currency test", "amount": 0 },
        { "id": 741, "title": "Equipment delivery", "amount": 250000 }
      ],
      "contacts": [
        { "id": 1, "name": "John", "lastName": "Brown" }
      ],
      "user1": [
        { "ID": "1", "NAME": "Mary", "ACTIVE": true }
      ]
    },
    "totals": {
      "deals": 1798,
      "contacts": 305
    },
    "errors": {},
    "summary": {
      "total": 3,
      "succeeded": 3,
      "failed": 0
    },
    "meta": {
      "deals": { "total": 1798, "returned": 2, "hasMore": true, "truncated": false },
      "contacts": { "total": 305, "returned": 1, "hasMore": true, "truncated": false }
    }
  }
}

Partial errors

If some calls fail validation or return an error on the Bitrix24 side, successful results stay in data.results, failed ones go to data.errors under the same id:

JSON
{
  "success": true,
  "data": {
    "results": {
      "deals": [
        { "id": 575, "title": "Currency test" }
      ]
    },
    "totals": {
      "deals": 1798
    },
    "errors": {
      "unknown": {
        "code": "UNKNOWN_ENTITY",
        "message": "Unknown entity \"foobar\". Check GET /v1/guide for available entities."
      }
    },
    "summary": {
      "total": 2,
      "succeeded": 1,
      "failed": 1
    },
    "meta": {
      "deals": { "total": 1798, "returned": 1, "hasMore": true, "truncated": false }
    }
  }
}

A limit pause arrives on the individual call. When a method is paused for several minutes, the refusal goes to a specific call rather than to the whole batch — to data.errors.<id> here and to data[i].error in the single-entity batch POST /v1/{entity}/batch. Two codes describe the pause: OPERATION_TIME_LIMIT — the paused pair is "your key and this method", and TIMEOUT_QUARANTINE — the pause applies to the whole Bitrix24 account. The envelope still answers 200, so it carries no Retry-After header for an individual call.

An individual call can be refused for other reasons too, and each has its own code. Besides the two pause codes these are RATE_LIMITED — the request rate to the portal was exceeded; QUEUE_OVERFLOW and QUEUE_TIMEOUT — the portal queue is full, or the call did not get its turn there (in the second case the request never reached Bitrix24 and a retry is safe); BITRIX_TIMEOUT — the portal accepted the call and did not answer in time, so the outcome is unknown; ERROR_LOOP_DETECTED — the platform temporarily paused the method after a run of failures; TOKEN_EXPIRED — the application's user OAuth session expired, so re-open the application from the Bitrix24 menu; TOKEN_REFRESH_FAILED — the portal authorization could not be refreshed, and retrying does not help until the key is reconnected. The two authorization errors, TOKEN_EXPIRED and TOKEN_REFRESH_FAILED, have no retryAfter: retrying cannot help until authorization is renewed. The other codes in this list arrive with retryAfter in seconds. A refusal with no code of its own arrives as AUTO_PAGINATION_FAILED in the shared batch call and CALL_FAILED in a single-entity batch.

When the platform itself rejects the call, knowing the pause is in effect, the code arrives together with retryAfter in seconds, scope, and hint — wait out the delay and retry only that call. This applies to the calls the platform runs as separate requests: here those are search, list whose window does not fit into a single Bitrix24 page, and list on the mail-mailboxes and humanresources-nodes entities at any window size; and in a single-entity batch it is any read call — list, get and fields. If Bitrix24 itself applied the pause to a call inside the shared batch request, only code and message arrive: no retry delay is returned in that case, so retry no sooner than five minutes later.

Error response example

If all calls fail validation, 400 INVALID_REQUEST is returned with a per-id breakdown in data.errors:

JSON
{
  "success": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "All calls in the batch failed validation"
  },
  "data": {
    "errors": {
      "x": {
        "code": "MISSING_ENTITY_ID",
        "message": "Action \"get\" requires entityId."
      }
    }
  }
}

Errors

HTTP Code Description
400 INVALID_REQUEST The request body does not match the schema, or all calls failed validation.
400 UNKNOWN_ENTITY One of the calls passed an unknown entity name.
400 ACTION_NOT_SUPPORTED The entity does not support the specified action (e.g. delete for a reference entity that does not support deletion).
400 MISSING_ENTITY_ID Actions get, update, delete require entityId.
400 EMPTY_CREATE_BODY A create call with no body fields. Returned for the specific call inside data.errors.
400 EMPTY_UPDATE_BODY An update call with no body fields. Returned for the specific call inside data.errors.
400 INVALID_PARAMS An object or an array was sent in a field declared as a plain value — including a file sent as the pair "file name and base64 content", which a batch call does not accept. Returned for the specific call inside data.errors.
400 BIZPROC_CALLBACK_BATCH_UNSUPPORTED A create or update call for bizproc-activities or bizproc-robots whose handler points to a Black Hole subdomain. Such a handler is registered with a single request. Returned for that specific call in data.errors.
400 MISSING_REQUIRED_PARAMS A list call for calendar-events without the mandatory type and ownerId in params.
400 UNSUPPORTED_FILTER The call's params.filter contains fields the entity's method does not accept as a filter — for example, calendar-events.
400 MISSING_DYNAMIC_PARAM For smart processes (entity: "items"), entityTypeId was not passed inside params.
400 INVALID_DYNAMIC_PARAM entityTypeId for smart processes is set incorrectly (not a positive integer).
400 USE_DEDICATED_ENTITY A dedicated entity exists for the passed entityTypeId — use it instead of items.
400 ENTITY_CUSTOM_ROUTES The entity works only through specialized routes (e.g. task-comments — via /v1/tasks/:taskId/comments).
400 INVALID_CALL The call object is missing the required entity and action fields.
401 TOKEN_MISSING The key has no configured OAuth tokens, or for an OAuth application no Authorization: Bearer ... was passed.
401 TOKEN_REFRESH_FAILED Failed to refresh the portal OAuth token.
403 MANAGEMENT_KEY_NO_ENTITY_ACCESS The request came from a management key — batch calls are available only to APP keys.
403 SCOPE_NOT_ALLOWED All calls requested scopes the key does not have. If at least one call passes, this code is returned inside data.errors for the specific calls, and the request itself succeeds.
422 BITRIX_ERROR Bitrix24 rejected the call. The response body contains bitrixError.error and bitrixError.error_description.
429 RATE_LIMITED The limit of 30 requests per minute per portal was exceeded — the limit is shared across all of the portal's API keys. The response contains a Retry-After header.
429 QUEUE_OVERFLOW Too many concurrent Bitrix24 calls have accumulated on the portal (by default 100 or more pending). The response is returned instantly with an HTTP header Retry-After: N (seconds) — the client must wait that long and retry with exponential backoff + jitter. Body: error.code = QUEUE_OVERFLOW, error.retryAfter duplicates the header.
429 QUEUE_TIMEOUT The request waited in the portal queue for more than 80 seconds. The request was NOT sent to Bitrix24 — safe to retry (Retry-After). The response contains userMessage and hint.
502 BITRIX_UNAVAILABLE Bitrix24 responded with a 5xx error.
503 BITRIX_TIMEOUT Bitrix24 accepted the request but did not respond within 60 seconds — the outcome is unknown. For write calls inside the batch: re-read the entity first — the change may have been applied.
500 INTERNAL_ERROR Internal proxy error.

Full list of common API errors — Error codes.

The OPERATION_TIME_LIMIT and TIMEOUT_QUARANTINE refusals arrive on the individual call inside a successful 200 — they are described in Partial errors.

Known specifics

search and list with limit > 50 bypass the native Bitrix24 batch call. These calls run as separate sequential requests with paging up to 5000 records — each consumes its own Bitrix24 rate-limit quota independently. The remaining calls — list with limit ≤ 50, get, create, update, delete, fields — are combined into a single batch call on the Bitrix24 side and cost one rate-limit unit in total.

list on the mail-mailboxes and humanresources-nodes entities bypasses the native batch call at any limit. These entities are served by the REST 3.0 method family, and the native Bitrix24 batch call can only dispatch previous-generation methods — such a sub-call used to come back as ERROR_METHOD_NOT_FOUND even with a limit within 50. It now runs as a separate request, like search, and costs its own rate-limit quota; neighboring calls on other entities still travel in one batch. The exception covers list only: get on these entities still does not answer with data inside a batch — read the record through its own route. A failure of such a sub-call arrives as AUTO_PAGINATION_FAILED in the global POST /v1/batch and as CALL_FAILED in POST /v1/{entity}/batch.

The record count always arrives for these two entities. withTotal: false on their list does not switch counting off: the platform returns total and hasMore as usual. The parameter stays valid, but it removes no load from the portal.

Cost of list in Bitrix24 rate-limit units. Every 50 records = 1 unit. With limit > 50 the auto-paginator makes several calls:

limit Bitrix24 units
1–50 1 (native batch)
51–2550 2
2551–5000 3

There is no "1–50" row for mail-mailboxes and humanresources-nodes: their list always runs as separate requests of 50 records, i.e. 1 unit per 50 records starting with the first. One entry in the calls array is one operation for the platform but several requests to the portal — these are different units, do not conflate them when planning the quota. And budget with headroom: when the portal answers with a temporary error the platform repeats the request, and on failure it repeats the whole walk, already-successful pages included. On a failing walk the actual spend reaches roughly eight times the calculated figure.

If an action in one call fails, the rest keep executing. Errors land in data.errors under the same id, successful results in data.results. The success field stays true — check data.summary.failed or the presence of the expected id in data.results.

users.get returns an array, not an object. This is Entity API-specific behavior: the get response for users is [ { ID, NAME, ... } ]. The first element is the requested record.

A page of results is lost when a sub-request fails. If, during auto-pagination of a list / search call, one of the page's sub-requests fails, the result is trimmed to a contiguous prefix and data.meta[<id>].pageErrorSample carries { code, message } of the first failure. The hasMore field stays true in that case — the remaining records can still be fetched.

The code holds either a Bitrix24 error code or a Vibecode one. There are three Vibecode codes today, and they all mean the same thing: the walk was stopped not by Bitrix24 but by Vibecode itself, which returned a contiguous prefix of the result. KEYSET_DISCONTINUITY — a gap in the page sequence was detected, and going on would have returned duplicates. PAGE2_COUNT_FAILED — the record count failed: a timeout, a rate limit, or an error on the Bitrix24 side. LAZY_COUNT_NO_PROGRESS — the method returned the same records instead of the next ones. The action to take is the same in all three cases — fetch the remainder.

The last two codes have a consequence for the meta: meta.<id>.total and data.totals.<id> are absent from such a response — the count was not computed, and the number of returned rows is not a substitute for it. The bound stays hasMore.

calendar-events in a batch call. The mandatory type and ownerId go into the call's params. Filtering is not supported for this entity — any remaining fields in params.filter return UNSUPPORTED_FILTER, and a missing type or ownerId returns MISSING_REQUIRED_PARAMS.

Dedicated entities are not available in a batch call. Chats, messages, Feed, knowledge base, calls and workday are served by dedicated routes — a call with entity: "chats", "messages", "posts", "note", "calls" or "workday" returns an error pointing to the right route. The batch call supports only the entities from the reference. To read the messages of many dialogs in one request, use POST /v1/chats/messages/bulk — up to 50 dialogs per call.

An activity or automation-rule handler on a Black Hole subdomain is registered with a single request. A create or update call for bizproc-activities and bizproc-robots whose handler points to such a subdomain is rejected by the batch call: BIZPROC_CALLBACK_BATCH_UNSUPPORTED arrives in data.errors under that call's id. A handler on your own domain passes through the batch call. The shape of the refusal on POST /v1/{entity}/batch, the behavior on an account without reliable delivery, and what the single registration gives you — Activity and automation rule callback delivery.

A batch call does not accept a file in a field declared as a plain value. The employee photo personalPhoto is declared that way: the pair "file name and base64 content" is rejected inside a batch, INVALID_PARAMS arrives in data.errors under that call's id, and on POST /v1/{entity}/batch the whole request answers 400 BATCH_ITEM_VALIDATION with the item index. The reason is size: a batch sub-call travels as a query string, and batch routes stayed on the shared body cap while the single routes were raised to 40 MiB. Past the sub-call length limit the value would be truncated, and a truncated write still answers with success. Send the file with a single call — POST /v1/users or PATCH /v1/users/:id. Which shapes of the field are accepted and which are rejected — the "Profile photo" section on the Update employee page.

This shape check covers only the fields declared in the entity schema. A field the schema does not declare — FILES of timeline records, for example — is invisible to it: the content travels in the sub-call without a warning, and the same length limit applies to it as well. Send those fields with a single call too.

Batch operations for a single entity. The specialized endpoint POST /v1/{entity}/batch works with one entity. The set of actions differs per entity: the full list is in operations.batch of the GET /v1/guide response (data.batch in GET /v1/{entity}/fields lists WRITE actions only). An action the entity does not have returns 400 ACTION_NOT_SUPPORTED — including a read disabled for the entity through disabledOperations. For delete you pass an ids array, for create and updateitems, and for the reads list / get / fields — a calls array. Writes run in internal batches of 50 records, up to 500 per request, and the response is an array of results with a success flag for each element. For smart processes the path carries the type segment: POST /v1/items/{entityTypeId}/batch. In a list call the filter field goes through the filter translator — field aliases and the operators $gt, $contains, $in work. An invalid filter rejects ONLY its own sub-call: the response is 200, the refusal code sits in data[i].error.code, and the remaining sub-calls run. Check each data element for an error, exactly as in the global POST /v1/batch.

See also