Для AI-агентов: markdown этой страницы — /docs-content/search/research.md индекс документации — /llms.txt

Глубокий поиск (research)

POST /v1/research

Запускает глубокий итеративный поиск с многошаговым агентным циклом «искать → читать → анализировать». Возвращает структурированный отчёт с разделами, источниками и опциональными уточняющими вопросами. Эндпоинт всегда работает в потоковом режиме через Server-Sent Events — один запрос длится 10–60 секунд и не помещается в типичный таймаут синхронного HTTP.

Поля запроса (body)

Поле Тип Обяз. По умолч. Описание
Полеquery Типstring Обяз.да По умолч.— ОписаниеТекст исследовательского запроса. От 1 до 2000 символов
Полеprovider Типstring Обяз.нет По умолч.research-движок по умолчанию инстанса ОписаниеПровайдер с поддержкой research: tavily, exa, you-com, linkup, perplexity или jina. Платформенный bitrix-search поддерживает research в зависимости от инстанса. Актуальный список — в GET /v1/search/providers с capabilities.modes.research: true
ПолеmaxSteps Типnumber Обяз.нет По умолч.5 ОписаниеМаксимум шагов агентного цикла. От 1 до 10. Адаптер ограничивает значение, если внешний провайдер не принимает столько шагов
Полеlang Типstring Обяз.нет По умолч.auto ОписаниеЯзык исследования: ru, en или auto
ПолеincludeDomains Типstring[] Обяз.нет По умолч.[] ОписаниеПоиск только по доменам из списка. До 50 имён хоста. Отдельные движки берут не больше 20 — при превышении список обрезается, а в кадре done появляется ignored_filters с includeDomains_truncated
ПолеexcludeDomains Типstring[] Обяз.нет По умолч.[] ОписаниеИсключить домены. До 50 имён хоста. Отдельные движки берут не больше 20 — при превышении список обрезается, а в кадре done появляется ignored_filters с excludeDomains_truncated

Примеры

curl — личный ключ

Terminal
curl -N -X POST https://vibecode.bitrix24.tech/v1/research \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Сравни возможности Cursor 3 и Windsurf для AI-кодинга",
    "provider": "tavily",
    "maxSteps": 5,
    "lang": "ru"
  }'

curl — OAuth-приложение

Terminal
curl -N -X POST https://vibecode.bitrix24.tech/v1/research \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Сравни возможности Cursor 3 и Windsurf для AI-кодинга",
    "provider": "tavily",
    "maxSteps": 5,
    "lang": "ru"
  }'

JavaScript — личный ключ

javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/research', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'Сравни возможности Cursor 3 и Windsurf для AI-кодинга',
    provider: 'tavily',
    maxSteps: 5,
    lang: 'ru',
  }),
})

const reader = res.body.getReader()
const decoder = new TextDecoder()
let buffer = ''

while (true) {
  const { value, done } = await reader.read()
  if (done) break
  buffer += decoder.decode(value, { stream: true })
  const events = buffer.split('\n\n')
  buffer = events.pop() ?? ''
  for (const block of events) {
    const eventLine = block.split('\n').find(l => l.startsWith('event: '))
    const dataLine = block.split('\n').find(l => l.startsWith('data: '))
    if (!eventLine || !dataLine) continue
    const event = eventLine.slice(7)
    const data = JSON.parse(dataLine.slice(6))
    if (event === 'done') {
      console.log('Готово:', data.synthesized_answer.slice(0, 200))
      console.log('Источников:', data.sources.length)
    }
  }
}

JavaScript — OAuth-приложение

javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/research', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'Сравни возможности Cursor 3 и Windsurf для AI-кодинга',
    provider: 'tavily',
    maxSteps: 5,
    lang: 'ru',
  }),
})

const reader = res.body.getReader()
const decoder = new TextDecoder()
let buffer = ''

while (true) {
  const { value, done } = await reader.read()
  if (done) break
  buffer += decoder.decode(value, { stream: true })
  const events = buffer.split('\n\n')
  buffer = events.pop() ?? ''
  for (const block of events) {
    const eventLine = block.split('\n').find(l => l.startsWith('event: '))
    const dataLine = block.split('\n').find(l => l.startsWith('data: '))
    if (!eventLine || !dataLine) continue
    const event = eventLine.slice(7)
    const data = JSON.parse(dataLine.slice(6))
    if (event === 'done') {
      console.log('Готово:', data.synthesized_answer.slice(0, 200))
      console.log('Источников:', data.sources.length)
    }
  }
}

Поля ответа

Ответ приходит как поток text/event-stream. Каждое событие — пара event: <type> + data: <JSON>, разделённая пустой строкой. Поток гарантированно завершается одним из двух событий: done (успех) или error (сбой).

События потока

Событие Когда возникает Поля data
Событиеstart Когда возникаетВ начале запроса Поля dataresearch_id, provider
Событиеthinking Когда возникаетПромежуточные размышления агента Поля datacontent — текстовый фрагмент
Событиеtool_call Когда возникаетАгент вызвал внутренний инструмент Поля datatool (web_search / content_extraction / external_tool), description
Событиеtool_result Когда возникаетИнструмент вернул результат Поля datadescription, items_count
Событиеsection_complete Когда возникаетЗавершён раздел исследования с привязанными источниками Поля datasection.title, section.content, section.sourceIds
Событиеanswer_delta Когда возникаетОчередной фрагмент финального ответа Поля datacontent — кусок текста
Событиеdone Когда возникаетФинальный блок Поля dataсм. таблицу «Поля события done» ниже
Событиеerror Когда возникаетСбой во время обработки Поля dataerror.code, error.message

Какие именно промежуточные события придут — зависит от провайдера. Ни одно из них не обязательно: получение start и done достаточно для штатного завершения.

Поля события `done`

Поле Тип Описание
Полеquery Типstring ОписаниеТекст исходного запроса
Полеprovider Типstring ОписаниеИдентификатор провайдера, который обработал запрос (например, tavily, exa)
Полеsynthesized_answer Типstring ОписаниеПолный синтезированный ответ исследования
Полеoutline Типobject | null ОписаниеСтруктура разделов отчёта. null, если провайдер не возвращает разбивку
Полеoutline.sections Типarray ОписаниеМассив разделов
Полеoutline.sections[].title Типstring ОписаниеЗаголовок раздела
Полеoutline.sections[].content Типstring ОписаниеТекст раздела
Полеoutline.sections[].sourceIds Типnumber[] ОписаниеИдентификаторы источников из sources[], на которые опирается раздел
Полеsources Типarray ОписаниеМассив источников исследования
Полеsources[].id Типnumber ОписаниеПорядковый номер источника
Полеsources[].url Типstring ОписаниеАдрес страницы
Полеsources[].title Типstring ОписаниеЗаголовок страницы
Полеsources[].fullContent Типstring ОписаниеПолный текст страницы. Длина ограничена провайдером
Полеsources[].publishedDate Типstring | null ОписаниеДата публикации в формате ISO 8601
Полеfollow_up_questions Типstring[] | null ОписаниеУточняющие вопросы для углубления исследования. null, если провайдер не предоставляет
Полеreasoning Типarray | null ОписаниеШаги рассуждения агента. null, если провайдер не предоставляет
Полеreasoning[].step Типnumber ОписаниеПорядковый номер шага
Полеreasoning[].description Типstring ОписаниеКраткое описание шага
Полеresearch_id Типstring ОписаниеИдентификатор запроса в формате wr_<14 цифр>_<8 hex>
Полеupstream_search_id Типstring | null ОписаниеИдентификатор у внешнего провайдера. Полезен для разбора инцидентов
Полеbilling_source Типstring ОписаниеНеобязательное. Источник оплаты: wallet — кошелёк, включая оплату сверх квоты ИИ, quota — квота ИИ портала, cowork — квота места Cowork, byok — собственный ключ провайдера. Для бесплатного запроса через платформенный ключ поле отсутствует
Полеcost_vibes Типnumber ОписаниеСколько Ꝟ фактически списано с кошелька. При billing_source, равном quota, cowork или byok, значение равно 0
Полеduration_ms Типnumber ОписаниеДлительность обработки запроса в миллисекундах
Полеpartial_charge Типboolean ОписаниеПрисутствует, когда параллельные запросы исчерпали остаток баланса до конца текущего списания. Запрос завершился успешно, фактически списано меньше номинальной стоимости — итог в cost_vibes
Полеcharge_log_failed Типboolean ОписаниеНеобязательное. true, если произошёл сбой списания или записи журнала использования. Успешный результат исследования возвращается клиенту. Если списание состоялось до сбоя журнала, cost_vibes сохраняет фактически списанную с кошелька сумму
Полеignored_filters Типstring[] ОписаниеНеобязательное. Переданные фильтры, которые движок не смог применить. Например, includeDomains_truncated, когда передано больше 20 доменов и список обрезан до 20

Пример ответа

event: start
data: {"research_id":"wr_20260504112304_a1b2c3d4","provider":"tavily"}

event: tool_call
data: {"tool":"web_search","description":"Web research started"}

event: tool_result
data: {"description":"Web research completed in 18s","items_count":8}

event: done
data: {"query":"Сравни возможности Cursor 3 и Windsurf для AI-кодинга","provider":"tavily","synthesized_answer":"Cursor 3 — это переработанная среда для AI-разработки [1][2]. Windsurf, в свою очередь, делает упор на agentic-flow [3]…","outline":null,"sources":[{"id":1,"url":"https://cursor.com/blog/cursor-3","title":"Meet the new Cursor","fullContent":"…","publishedDate":"2026-04-15T00:00:00Z"}],"follow_up_questions":null,"reasoning":null,"ignored_filters":["lang"],"research_id":"wr_20260504112304_a1b2c3d4","upstream_search_id":"tvly-research-abc","billing_source":"byok","cost_vibes":0,"duration_ms":18230}

Пример ответа при ошибке

402 — на балансе не хватает Ꝟ:

JSON
{
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Insufficient vibes for this research request. Top up your account to continue.",
    "userMessage": "Недостаточно vibes — пополните баланс или добавьте свой BYOK-ключ (см. GET /v1/search/providers).",
    "hint": "Add a BYOK key to search for free — see GET /v1/search/providers",
    "required": 25,
    "topupUrl": "https://vibecode.bitrix24.tech/r/upgrade?c=ai_quota_copay&sc=api&m=api&t=topup&p=…&e=…&s=…"
  }
}

Четыре поля приходят внутри объекта error, а не на верхнем уровне ответа: userMessage — текст на языке пользователя, hint — подсказка по дальнейшим действиям, required — сколько Ꝟ нужно на этот запрос, topupUrl — ссылка на пополнение баланса. Набор общий для INSUFFICIENT_BALANCE и BILLING_FROZEN. Остальные ситуации — в таблице ошибок ниже.

Ошибки

Ошибки до открытия SSE-потока возвращаются обычным JSON-ответом. Ошибки, возникшие после установки соединения, приходят как событие error в потоке.

HTTP Код Описание
HTTP400 КодINVALID_REQUEST ОписаниеПустой query, query длиннее 2000 символов, maxSteps вне диапазона 1..10, provider не из списка поддерживаемых провайдеров, includeDomains или excludeDomains длиннее 50 элементов
HTTP402 КодINSUFFICIENT_BALANCE ОписаниеНа балансе портала недостаточно Ꝟ для платного провайдера. Перейдите на BYOK или пополните баланс
HTTP402 КодBILLING_FROZEN ОписаниеБиллинг-аккаунт заморожен
HTTP402 КодAI_QUOTA_EXHAUSTED ОписаниеКвота ИИ исчерпана, оплата сверх квоты недоступна. Причина — в error.reason, время сброса — в error.resetAt. Дождитесь сброса, разрешите перерасход в кабинете и пополните кошелёк или используйте свой ключ провайдера. Необязательное поле error.learnMore содержит дополнительные сведения
HTTP429 КодAI_PACING_LIMITED ОписаниеДостигнут лимит равномерного расходования квоты ИИ. Ответ содержит error.reason, error.overageDenied, error.resetAt и заголовок Retry-After в секундах. Дождитесь указанного времени или используйте свой ключ провайдера
HTTP402 КодCOWORK_QUOTA_EXHAUSTED ОписаниеИсчерпано окно квоты места Cowork. Поле error.window принимает fiveHour, week или month. Дождитесь error.resetAt или задержки из заголовка Retry-After в секундах, либо используйте свой ключ провайдера
HTTP402 КодCOWORK_SUBSCRIPTION_INACTIVE ОписаниеНет активного места Cowork. Активируйте или возобновите место, либо используйте свой ключ провайдера
HTTP402 Кодcompany_budget_exhausted ОписаниеИсчерпан месячный бюджет расхода компании, который задаёт администратор портала. Поле scope называет пробитый бюджет: USER — личный бюджет вызывающего, PORTAL — бюджет всего портала. Поле canRequest говорит, можно ли запросить увеличение: у личного бюджета true, у портального false — его поднимает только администратор. Отказ приходит при оплате из кошелька, включая оплату сверх квоты ИИ. Исследование на своём BYOK-ключе бесплатно и продолжает работать
HTTP403 КодSCOPE_DENIED ОписаниеКлючу не хватает скоупа vibe:search
HTTP404 КодPROVIDER_NOT_FOUND ОписаниеПровайдер с таким идентификатором отсутствует или недоступен
HTTP404 КодCREDENTIAL_NOT_FOUND ОписаниеДля провайдера нет ни USER, ни PORTAL, ни PLATFORM-ключа
HTTP404 КодPROVIDER_DOES_NOT_SUPPORT_RESEARCH ОписаниеЗапрошен провайдер без поддержки research: brave, z-ai или bitrix-search, когда у движка инстанса нет режима research. Такие провайдеры доступны только в POST /v1/search
HTTP429 КодRATE_LIMITED ОписаниеПревышен лимит 20 запросов в минуту на портал — общий для всех его API-ключей. Действующее значение — в заголовке x-ratelimit-limit
HTTP429 КодSEARCH_DAILY_CAP_REACHED ОписаниеС этим запросом суммарный расход поиска из кошелька, квоты ИИ и квоты места Cowork за текущие сутки по московскому времени превысил бы потолок, который задал администратор портала. Отказ приходит обычным JSON-ответом до открытия потока, провайдер не вызывается, списания нет. Потолок общий для /v1/research и POST /v1/search. Ответ несёт заголовок Retry-After, а тело ошибки — поля resetAt с началом следующих суток по московскому времени и hint. Исследование на своём BYOK-ключе потолок не ограничивает
HTTP503 КодFEATURE_NOT_ENABLED ОписаниеГлубокое исследование недоступно на этой платформе

Ошибки внутри SSE-потока:

Код Описание
КодUPSTREAM_ERROR ОписаниеПровайдер вернул ошибку во время обработки. Списания нет
КодPROVIDER_DOES_NOT_SUPPORT_RESEARCH ОписаниеЗащитная проверка адаптера на стороне сервера — фактически дубль HTTP-кода 404, всплывает при рассинхронизации каталога
КодINTERNAL_ERROR ОписаниеВнутренняя ошибка сервера во время обработки

Полный список общих ошибок API — Ошибки.

Известные особенности

Только потоковый режим. Синхронного варианта /v1/research без SSE не существует — глубокое исследование длится 10–60 секунд и не вписывается в обычные HTTP-таймауты прокси. Если клиент не может работать с потоком, накапливайте события на сервере-посреднике и отдавайте их пакетом конечному приложению.

Лимит 20 запросов в минуту на портал. Действующее значение — в заголовке x-ratelimit-limit. Ниже, чем у POST /v1/search, потому что один research-запрос потребляет ресурсы провайдера в десятки раз дольше обычного поиска. Лимит общий для всех API-ключей портала — они делят его между собой, поэтому распределение нагрузки по нескольким ключам предел не поднимает. При превышении возвращается 429 RATE_LIMITED.

Тариф платформенного research-движка зависит от инстанса. Сколько Ꝟ стоит режим research у платформенного движка, возвращает GET /v1/search/providers в поле pricing.research. Для всех BYOK-провайдеров стоимость на стороне платформы — 0 Ꝟ, оплата идёт напрямую с вашего аккаунта у поставщика.

Каскад выбора ключа отличается от /v1/search. В POST /v1/search при опущенном provider платформа сначала проверяет USER/PORTAL-дефолт по любому провайдеру и только потом уходит в движок по умолчанию инстанса. В research всё иначе: если поле provider опущено, сервер сразу подставляет research-движок по умолчанию, настроенный на инстансе, не проверяя USER/PORTAL-дефолты по другим провайдерам. Если для этого движка нет ни одного ключа, возвращается 404 CREDENTIAL_NOT_FOUND — даже если у пользователя есть дефолтный ключ для exa или другого research-провайдера. Каскад USER → PORTAL → PLATFORM применяется уже внутри выбранного провайдера. Чтобы пойти через другой research-провайдер, передавайте provider явно.

Списание происходит только после успешного завершения потока. До обращения к провайдеру проверяется возможность оплаты из выбранного источника. Для оплаты из кошелька, включая перерасход квоты ИИ, проверяются баланс и бюджет компании. При обрыве потока с ошибкой провайдера квота и кошелёк не расходуются. Порядок выбора источника оплаты — в разделе Тарификация.

Смотрите также