#Фильтрация и поиск

Три синтаксиса фильтрации для Entity API. Все стили можно смешивать в одном запросе.

Фильтрация работает в двух местах:

  • GET /v1/{entity}?filter[field]=value — параметр URL для списка
  • POST /v1/{entity}/search — тело запроса { "filter": { ... } }

#Синтаксис 1: MongoDB-style (рекомендуется для AI)

Операторы с префиксом $ внутри объекта поля.

JSON
{
  "filter": {
    "amount": { "$gte": 50000 },
    "stageId": { "$ne": "LOST" },
    "createdAt": { "$gte": "2026-01-01T00:00:00" }
  }
}

Найдёт записи с суммой от 50 000, стадия отличается от LOST, созданные с начала 2026 года.

#Доступные операторы

Оператор Значение Пример
$gt > (больше) { "amount": { "$gt": 10000 } }
$gte >= (больше или равно) { "amount": { "$gte": 50000 } }
$lt < (меньше) { "amount": { "$lt": 100000 } }
$lte <= (меньше или равно) { "amount": { "$lte": 200000 } }
$ne != (не равно) { "stageId": { "$ne": "LOST" } }
$contains подстрока (LIKE) { "title": { "$contains": "поставка" } }
$in входит в массив { "stageId": { "$in": ["NEW", "WON"] } }

Точное совпадение задаётся значением напрямую, без оператора: { "stageId": "NEW" }.

#Комбинирование

Несколько операторов на одном поле — И (AND):

JSON
{
  "filter": {
    "amount": { "$gte": 50000, "$lte": 200000 },
    "stageId": { "$ne": "LOST" }
  }
}

Найдёт записи с суммой от 50 000 до 200 000 и стадией, отличной от LOST.

#Синтаксис 2: Bitrix24 prefix

Оператор как часть имени поля:

JSON
{
  "filter": {
    ">=amount": 50000,
    "<=amount": 200000,
    "!stageId": "LOST"
  }
}

Найдёт записи с суммой от 50 000 до 200 000 и стадией, отличной от LOST.

#Операторы

Префикс Значение
>= больше или равно
> больше
<= меньше или равно
< меньше
! не равно
% подстрока

#Синтаксис 3: Bitrix24 operator keys

Оператор как ключ вложенного объекта:

JSON
{
  "filter": {
    "amount": { ">=": 50000 },
    "stageId": { "!": "LOST" }
  }
}

Найдёт записи с суммой от 50 000 и стадией, отличной от LOST.

#Фильтрация по дате

Поля даты (createdAt, updatedAt, closedAt, beginDate и др.) принимают строки ISO 8601:

JSON
{
  "filter": {
    "createdAt": { "$gte": "2026-01-01T00:00:00" },
    "closedAt": { "$lte": "2026-03-31T23:59:59" }
  }
}

Найдёт записи, созданные с начала 2026 года и закрытые до конца марта.

#Примеры

JSON
{ "filter": { ">=createdAt": "2026-03-01T00:00:00" } }

Найдёт записи, созданные с 1 марта 2026.

JSON
{ "filter": { "updatedAt": { "$gte": "2026-04-09T00:00:00" } } }

Найдёт записи, обновлённые сегодня (9 апреля 2026).

#NOT-фильтры

Исключение значений:

JSON
{ "filter": { "stageId": { "$ne": "LOST" } } }
JSON
{ "filter": { "!stageId": "LOST" } }
JSON
{ "filter": { "stageId": { "!": "LOST" } } }

Все три варианта найдут записи, у которых стадия отличается от LOST.

#Смешивание синтаксисов

Все три стиля можно комбинировать в одном фильтре:

JSON
{
  "filter": {
    "amount": { "$gte": 50000 },
    "!stageId": "LOST",
    "createdAt": { ">=": "2026-01-01T00:00:00" }
  }
}

Найдёт записи с суммой от 50 000, стадией отличной от LOST, созданные с начала 2026 года. Здесь MongoDB-style, prefix и operator используются вместе.

#Эндпоинт поиска

POST /v1/{entity}/search — полнофункциональный поиск:

Terminal
curl -X POST -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  "https://vibecode.bitrix24.tech/v1/deals/search" \
  -d '{
    "filter": {
      "stageId": { "$ne": "LOST" },
      "amount": { "$gte": 100000 }
    },
    "sort": { "amount": "desc" },
    "limit": 200
  }'
Параметр Тип Описание
filter object Условия фильтрации
sort string | object | array Сортировка. Принимаются три формы: "id" / "-amount" / "id,-createdAt" (string), { id: "asc", amount: "desc" } (object, ключи в порядке вставки), ["id", "-amount"] (array). 1/-1 тоже работают вместо asc/desc. Невалидный тип возвращает 400 INVALID_SORT_TYPE, неизвестное направление — INVALID_SORT_DIRECTION.
limit number Количество записей (по умолчанию 50, максимум 5000)
offset number Пропустить N записей
autoWindow boolean false — отключить разбивку по дате

#Примеры по сущностям

#Сделки — по стадии и сумме

JSON
{
  "filter": {
    "stageId": "NEW",
    "amount": { "$gte": 100000 }
  },
  "sort": { "createdAt": "desc" }
}

Найдёт сделки в стадии NEW с суммой от 100 000, отсортированные по дате создания (новые первыми).

#Контакты — по телефону

JSON
{
  "filter": {
    "phone": { "$contains": "+7916" }
  }
}

Найдёт контакты, у которых номер телефона содержит подстроку "+7916".

#Задачи — незакрытые

JSON
{
  "filter": {
    "status": { "$ne": 5 }
  }
}

Найдёт все задачи со статусом, отличным от 5 (завершена). Статусы: 2 = ждёт выполнения, 3 = выполняется, 4 = ожидает контроля, 5 = завершена, 6 = отложена.

#Лиды — за период

JSON
{
  "filter": {
    "createdAt": { "$gte": "2026-01-01T00:00:00", "$lte": "2026-03-31T23:59:59" }
  }
}

Найдёт лиды, созданные в первом квартале 2026 года.

#Календарные события

JSON
{
  "filter": {
    "dateFrom": { "$gte": "2026-04-01T00:00:00" }
  }
}

Найдёт события с датой начала от 1 апреля 2026. Для calendar-events обязательны параметры type и ownerId в URL: /v1/calendar-events?type=user&ownerId=1.

#Фильтрация в Batch

Фильтры работают и в Batch API:

JSON
{
  "calls": [
    {
      "id": "new_deals",
      "entity": "deals",
      "action": "list",
      "params": {
        "filter": { "stageId": "NEW" }
      }
    },
    {
      "id": "search_contacts",
      "entity": "contacts",
      "action": "search",
      "params": {
        "filter": { "phone": { "$contains": "+7" } }
      }
    }
  ]
}

Одним запросом получит список сделок в стадии NEW и найдёт контакты с российскими номерами.

#Pagination patterns

#Один запрос (рекомендуется для широких диапазонов)

JavaScript
// Auto-paginator под капотом разделит широкий диапазон дат на 7-дневные окна,
// последовательно их выберет и склеит результаты с дедупом по id.
const res = await fetch('/v1/deals/search', {
  method: 'POST',
  headers: { 'X-Api-Key': key, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    filter: { closedAt: { $gte: '2026-03-22T00:00:00Z', $lte: '2026-04-22T00:00:00Z' } },
    sort: { id: 'asc' },
    limit: 5000,  // максимум за один запрос
  }),
})
const { data, meta } = await res.json()
// data — все записи; meta.total — реальное общее количество

#Своя пагинация с `autoWindow: false`

JavaScript
// Если нужно работать страницами вручную (например, поэлементная обработка).
// ОБЯЗАТЕЛЬНО передавайте sort: 'id' (или другое стабильное поле), иначе B24
// возвращает страницы в неустойчивом порядке.
let offset = 0
while (true) {
  const res = await fetch('/v1/deals/search', {
    method: 'POST',
    headers: { 'X-Api-Key': key, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      filter: { closedAt: { $gte: '2026-03-22T00:00:00Z', $lte: '2026-04-22T00:00:00Z' } },
      sort: 'id',
      limit: 50,
      offset,
      autoWindow: false,
    }),
  })
  const { data, meta } = await res.json()
  for (const deal of data) process(deal)
  offset += data.length
  if (offset >= meta.total) break
}

#Что НЕ работает

Параллельная offset-пагинация на широких диапазонах возвращает 400 UNSTABLE_OFFSET_PAGINATION:

JavaScript
// ❌ Не работает — offset=0 идёт через window-merge, offset>0 через flat B24,
// результаты несовместимы. Выберите один из паттернов выше.
await Promise.all([
  fetch('/v1/deals/search', { ..., body: JSON.stringify({ filter: WIDE, offset: 0, limit: 50 }) }),
  fetch('/v1/deals/search', { ..., body: JSON.stringify({ filter: WIDE, offset: 50, limit: 50 }) }),
  // ...
])

#Связанные разделы