For AI agents: markdown of this page — /docs-content-en/batch.md documentation index — /llms.txt

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 filtering syntax).

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

  • list and searchfilter, select, order, limit
  • 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 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
        }
      },
      {
        "id": "contacts",
        "entity": "contacts",
        "action": "list",
        "params": {
          "select": ["id", "name", "lastName"],
          "limit": 20
        }
      },
      {
        "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
        }
      },
      {
        "id": "contacts",
        "entity": "contacts",
        "action": "list",
        "params": {
          "select": ["id", "name", "lastName"],
          "limit": 20
        }
      },
      {
        "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 } },
      { id: 'contacts', entity: 'contacts', action: 'list', params: { select: ['id', 'name', 'lastName'], limit: 20 } },
      { 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 } },
      { id: 'contacts', entity: 'contacts', action: 'list', params: { select: ['id', 'name', 'lastName'], limit: 20 } },
      { 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.
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, returned, hasMore, truncated, and — when a page of results is lost — pageErrorSample.

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

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 field. Returned for the specific call inside data.errors.
400 EMPTY_UPDATE_BODY An update call with no body field. Returned for the specific call inside data.errors.
400 MISSING_REQUIRED_PARAMS A list call for calendar-events did not pass the mandatory type and ownerId in params.
400 UNSUPPORTED_FILTER Fields the entity's method does not accept as a filter were passed in the call's params.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 more than 100 pending). The response is returned instantly with an HTTP header Retry-After: N (seconds) — the client must wait for it 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 30 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 15 seconds — the outcome is unknown. For write calls inside the batch: re-read the entity first, the change may have applied.
500 INTERNAL_ERROR Internal proxy error.

Full list of common API errors — Error codes.

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.

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–2 550 2
2 551–5 000 3

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 be re-fetched.

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

Batch operations for a single entity. The specialized endpoint POST /v1/{entity}/batch works with one entity and supports the actions create, update, delete, list, get, fields. 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 the whole batch with 400 and names the call index in the message.

See also