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

POST /v1/research

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

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

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

#Примеры

#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()

#Поток событий (SSE)

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

Событие Когда возникает Поля data
start В начале запроса research_id, provider
thinking Промежуточные размышления агента content — текстовый фрагмент
tool_call Агент вызвал внутренний инструмент tool (web_search / content_extraction / external_tool), description
tool_result Инструмент вернул результат description, items_count
section_complete Завершён раздел исследования с привязанными источниками section.title, section.content, section.sourceIds
answer_delta Очередной фрагмент финального ответа content — кусок текста
done Финальный блок см. таблицу «Поля события done» ниже
error Сбой во время обработки error.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 Идентификатор у внешнего провайдера. Полезен для разбора инцидентов
cost_vibes number Сколько Ꝟ списано фактически
duration_ms number Длительность обработки запроса в миллисекундах
partial_charge boolean Присутствует, когда параллельные запросы исчерпали остаток баланса до конца текущего списания. Запрос завершился успешно, фактически списано меньше номинальной стоимости — итог в cost_vibes
charge_log_failed boolean Присутствует, когда не удалось записать журнал использования. Результат отдан, средства не списаны (cost_vibes: 0)

#Пример потока событий

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

event: tool_call
data: {"tool":"web_search","description":"Cursor 3 release"}

event: tool_result
data: {"description":"got 8 sources","items_count":8}

event: tool_call
data: {"tool":"content_extraction","description":"reading top sources"}

event: answer_delta
data: {"content":"Cursor 3 — это переработанная среда "}

event: answer_delta
data: {"content":"для AI-разработки [1][2]. Windsurf, в свою очередь, "}

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":["Поддерживает ли Cursor 3 Claude Sonnet 4?"],"reasoning":null,"research_id":"wr_20260504112304_a1b2c3d4","upstream_search_id":"tvly-research-abc","cost_vibes":0,"duration_ms":18230}

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

503 — глубокое исследование недоступно:

JSON
{
  "error": {
    "code": "FEATURE_NOT_ENABLED",
    "message": "Research mode is not enabled on this platform."
  }
}

Для биллинговых ошибок (INSUFFICIENT_BALANCE, BILLING_FROZEN) в ответе дополнительно приходят поля userMessage и hint с подсказкой по дальнейшим действиям. Остальные ситуации — в таблице ошибок ниже.

#Ошибки

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

HTTP Код Описание
400 INVALID_REQUEST Пустой query, query длиннее 2000 символов, maxSteps вне диапазона 1..10, provider не из списка девяти, includeDomains или excludeDomains длиннее 50 элементов
402 INSUFFICIENT_BALANCE На балансе портала недостаточно Ꝟ для платного провайдера. Перейдите на BYOK или пополните баланс
402 BILLING_FROZEN Биллинг-аккаунт заморожен
403 SCOPE_DENIED Ключу не хватает скоупа vibe:search
404 PROVIDER_NOT_FOUND Провайдер с таким идентификатором отсутствует или недоступен
404 CREDENTIAL_NOT_FOUND Для провайдера нет ни USER, ни PORTAL, ни PLATFORM-ключа
404 PROVIDER_DOES_NOT_SUPPORT_RESEARCH Запрошен провайдер без поддержки research: bitrix-search, brave или z-ai. Эти провайдеры доступны только в `POST /v1/search`
429 RATE_LIMITED Превышен лимит 20 запросов в минуту на API-ключ
503 FEATURE_NOT_ENABLED Глубокое исследование недоступно на этой платформе

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

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

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

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

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

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

Тарификация для bitrix-search:research — 100 Ꝟ за запрос. Платформенный провайдер bitrix-search сейчас не входит в список research-провайдеров (его поле capabilities.modes.researchfalse), поэтому стоимость 100 Ꝟ остаётся служебным показателем для будущего расширения. Для всех BYOK-провайдеров стоимость на стороне Вайбкод — 0 Ꝟ, оплата идёт напрямую с вашего аккаунта у поставщика.

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

Списание происходит только после успешного завершения потока. Перед запуском проверяется баланс: при нехватке возвращается 402 INSUFFICIENT_BALANCE без обращения к провайдеру. При обрыве потока с ошибкой провайдера баланс не меняется — оплачивать нечего.

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