# Importing CRM records

Move existing data into the CRM: up to 100 records per request, keeping the author and the dates from your previous system.

**Import does not run automation.** Robots, triggers and business processes configured to fire on record creation do not run on imported records — that is a property of the Bitrix24 operation itself, not a parameter of ours. If you want automation to run, create the records the usual way with `POST /v1/{entity}`.

`POST /v1/{entity}/import`

**Scope:** `crm` | **Base URL:** `https://vibecode.bitrix24.com/v1` | **Authorization:** `X-Api-Key` (APP key)

## Which objects support it

| Path | Object |
|------|--------|
| `POST /v1/leads/import` | Leads |
| `POST /v1/deals/import` | Deals |
| `POST /v1/contacts/import` | Contacts |
| `POST /v1/companies/import` | Companies |
| `POST /v1/quotes/import` | Quotes |
| `POST /v1/invoices/import` | Invoices |
| `POST /v1/items/{entityTypeId}/import` | Smart-process items |

No other entity has this route — Bitrix24 can only import CRM objects.

## How import differs from create

| | `POST /v1/{entity}` | `POST /v1/{entity}/import` |
|---|---|---|
| Bitrix24 permission | permission to add | **permission to import** — a separate one, granted by the account administrator |
| Robots and business processes | run | **do not run** |
| Record author and dates | set by Bitrix24 | you may supply your own (see below) |
| Per request | one record | up to 100 records |
| Response | the created record in full | the outcome of each record |

## Request fields (body)

| Field | Type | Req. | Description |
|------|-----|:---:|---------|
| `items` | array | ★ | Array of records, 1 to 100. Each record takes the same fields as `POST /v1/{entity}`, plus the service fields below. |

```bash
curl -X POST 'https://vibecode.bitrix24.com/v1/leads/import' \
  -H 'X-Api-Key: YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "items": [
      {
        "title": "Website request",
        "name": "Jane",
        "lastName": "Doe",
        "phone": "+15551234567",
        "email": "jane@example.com",
        "assignedById": 31,
        "createdBy": 11
      }
    ]
  }'
```

## Service fields

Six fields Bitrix24 normally fills in itself are accepted on import only:

| Field | What it sets |
|------|-------------|
| `createdBy` | Who created the record |
| `createdTime` | When the record was created |
| `updatedBy` | Who changed the record |
| `updatedTime` | When the record was changed |
| `movedBy` | Who moved the record to its current stage |
| `movedTime` | When the stage changed |

Only an **account administrator** may set them. For any other user Bitrix24 answers that record with `CRM_FIELD_ERROR_VALUE_NOT_VALID` — the remaining records of the batch are still created.

Pass an existing employee. Bitrix24 does not check the identifier for existence and will store any number, leaving a phantom author on the card. The record stays manageable either way.

The available set differs per object: deals and invoices have all six, leads have the author plus the creation and change dates. The exact list for a given entity comes from `GET /v1/{entity}/fields` — service fields are marked there with `"importable": true` next to `"readonly": true`.

## The date window

`createdTime` has a window set by Bitrix24, and it cannot be worked around:

- the date may not be **later than the current moment**;
- the date may not be **earlier than that of the newest existing record of this object**.

So a full history transfer works into an empty CRM, or into one whose records are all older than the ones you are moving. Bitrix24 will not let you backdate records into a populated CRM — it answers `CRM_FIELD_ERROR_VALUE_NOT_VALID` with an explanation. Every successfully imported record raises the lower bound of the window, so move your batches oldest-first.

`updatedTime` must be no earlier than `createdTime`, and `movedTime` must fall between the two.

If you do not need the history, simply omit the dates — the author can be set without them.

## Response

The response comes back with status `200` even when some records failed — import is not transactional, and the outcome has to be read per record.

```json
{
  "success": true,
  "data": {
    "results": [
      { "index": 0, "success": true, "id": 147 },
      {
        "index": 1,
        "success": false,
        "error": "CRM_FIELD_ERROR_VALUE_NOT_VALID",
        "message": "The value of the \"Created on\" field cannot be in the future"
      }
    ],
    "summary": { "total": 2, "succeeded": 1, "failed": 1 }
  }
}
```

| Field | Description |
|------|-------------|
| `results[].index` | Position of the record in the `items` array you sent. `results` is always the same length as `items`. |
| `results[].success` | Whether the record was created. |
| `results[].id` | Identifier of the created record. Present only when `success: true`. |
| `results[].error` | Bitrix24 error code. |
| `results[].message` | Explanation of the error. |
| `summary` | Totals: `total`, `succeeded`, `failed`. |

**Always check `summary.failed`.** Status `200` means "the request was processed", not "every record was created".

A repeated import creates duplicates — the operation is not idempotent. If repeats are possible, write the identifier from your previous system into `originatorId` and `originId`: they let you find what has already been transferred.

## Errors

| HTTP | Code | When |
|------|-----|-------|
| 400 | `IMPORT_ITEM_VALIDATION` | `items` is missing, empty or not an array; an element is not an object or is empty; an element carries a field that is not writable. The text names the position: `Item at index N: …` |
| 400 | `IMPORT_LIMIT_EXCEEDED` | More than 100 records in one request. Split the payload and send the parts in order. |
| 400 | `INVALID_DYNAMIC_PARAM` | Invalid `entityTypeId` in the `/v1/items/{entityTypeId}/import` path, or that type has a route of its own (for deals, `/v1/deals/import`). |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key is in read-only mode. |
| 403 | `SCOPE_DENIED` | The key has no `crm` scope. |
| 429 | `RATE_LIMITED` | Too frequent. Import is rate-limited per account so a bulk transfer cannot block other integrations. Retry after the interval in the `Retry-After` header. |
| 404 | — | This entity has no import. |

Per-record errors arrive inside the `200`, in `results[]` — those codes come from Bitrix24 itself.

## Things to plan for when moving a database

- **One stream.** Order is guaranteed within a single request but not between parallel requests, and Bitrix24 requires non-decreasing creation dates. Import sequentially.
- **Throughput.** A hundred records per request takes roughly 12–15 seconds; the rate limit allows about a thousand records per minute per account. Budget time for a database of tens of thousands.
- **A failure mid-batch.** If the connection to Bitrix24 drops after some records were already created, the response still comes back with status `200`: the created ones carry identifiers, the rest are marked as failed. That response is your only record of what went through — check it before retrying.
- **Phone and email.** Pass them as usual (`"phone": "+15551234567"` or an array of objects) — the platform converts them into the shape import accepts.

## Related pages

- [Creating a record](./entities/leads/create.md) — the ordinary path, with automation
- [Batch calls](./batch.md) — up to 50 operations across different entities in one request
- [Lead fields](./entities/leads/fields.md) — which fields exist and which of them are service fields
