#Глубокий поиск (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 — личный ключ
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-приложение
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 — личный ключ
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-приложение
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 — глубокое исследование недоступно:
{
"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.research — false), поэтому стоимость 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 без обращения к провайдеру. При обрыве потока с ошибкой провайдера баланс не меняется — оплачивать нечего.
#Смотрите также
- Web Search для AI — обзор раздела, тарификация, поддерживаемые возможности
- Поиск (POST /v1/search) — синхронный и потоковый поиск без агентного цикла
- Провайдеры — список движков с поддержкой research
- Свои ключи (BYOK) — добавление BYOK-ключей для research-провайдеров
- Ключи и авторизация — личный ключ и ключ авторизации
- Ошибки — справочник кодов ошибок API