For AI agents: markdown of this page — /docs-content-en/search/run.md documentation index — /llms.txt

Search

POST /v1/search

Performs a web search and returns a synthesized answer with links to sources. Supports synchronous mode and streaming via Server-Sent Events (stream: true). Deep research with a multi-step agentic loop is a separate endpoint POST /v1/research.

Request fields (body)

Field Type Required Default Description
query string yes Query text. From 1 to 400 characters
search_depth string no basic Search depth: basic or advanced. The price of each mode is listed in GET /v1/search/providers
provider string no cascade USER → PORTAL → instance default engine Forced engine choice: one of bitrix-search, tavily, brave, exa, you-com, linkup, perplexity, z-ai. jina is available only in POST /v1/research
topic string no general Search topic: general or news. Not all engines support the news mode — those that do populate publishedDate for news results, while the rest ignore it
max_results number no 5 Number of results in the response. From 1 to 20
max_steps number no 3 Maximum number of steps in agentic mode. From 1 to 5. Honored only by engines that support it — for bitrix-search this depends on the instance engine
lang string no ru Search language: ru, en or auto. The default engine ignores this field — see "Known specifics"
include_answer boolean no true Include the synthesized answer in the answer field
include_raw_content boolean no false Request the full text of the pages found. For engines that support it, the text arrives in results[].rawContent
include_images boolean no false Request images for the query. For engines that support it, the links arrive in the top-level images array
include_domains string[] no [] Restrict the search to the domains in the list. Up to 10 hostnames
exclude_domains string[] no [] Exclude domains. Up to 10 hostnames
time_range string | null no null Publication time window: day, week, month, year
stream boolean no false true — streaming via SSE instead of a JSON response

Not all providers support every filter — see the table in Web Search for AI. An unsupported filter is ignored, and the response carries an X-Search-Filters-Ignored header with the list of skipped fields.

Examples

curl — personal key

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/search \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "what is new in Cursor IDE",
    "search_depth": "advanced",
    "max_results": 5,
    "lang": "auto"
  }'

curl — OAuth application

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/search \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "what is new in Cursor IDE",
    "search_depth": "advanced",
    "max_results": 5,
    "lang": "auto"
  }'

JavaScript — personal key

javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/search', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'what is new in Cursor IDE',
    search_depth: 'advanced',
    max_results: 5,
    lang: 'auto',
  }),
})

const data = await res.json()
console.log(data.answer)
data.results.forEach((r) => console.log(`[${r.id}] ${r.title} — ${r.url}`))

JavaScript — OAuth application

javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/search', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'what is new in Cursor IDE',
    search_depth: 'advanced',
    max_results: 5,
    lang: 'auto',
  }),
})

const data = await res.json()

Response fields

Field Type Description
query string The original query text
provider string Identifier of the provider that processed the request (e.g. bitrix-search, tavily)
search_depth string The applied search depth: basic or advanced
answer string | null Synthesized answer. Engines with citation support mark the sources with [N] — see the capability table in the section index. null for brave or when include_answer: false
results array Array of found sources
results[].id number Sequential number. Matches the [N] citation marker in answer for engines that emit citations
results[].url string Page address
results[].title string Page title
results[].content string Always a short description or snippet of the page. The full text arrives in a separate rawContent field, not here
results[].rawContent string | null The full page text when include_raw_content: true for engines that support it. Size capped at roughly 40 KB per result. null when the full text was not requested or the engine does not return it
results[].score number | null Relevance score from the provider — a number from 0 to 1: the higher the value, the more relevant the result. null for brave. For bitrix-search it depends on the instance engine — see the capability table in the section index
results[].publishedDate string | null Publication date in ISO 8601 format. Populated mainly when topic: "news". For bitrix-search it depends on the instance engine — see the capability table in the section index
images array Top-level array of images when include_images: true for engines that support it. Each element is an object with a url field. Empty array when images were not requested or the engine does not return them
ignored_filters string[] The passed filters and parameters the engine could not apply. The same list as the X-Search-Filters-Ignored header, delivered in the response body. May contain topic, include_images, include_domains, time_range, and others — the set depends on the engine
search_id string Request identifier in the format ws_<14 digits>_<8 hex>
upstream_search_id string | null Identifier at the external provider. Useful for incident investigation
cost_vibes number How many Ꝟ were actually charged
duration_ms number Request processing duration in milliseconds
partial_charge boolean Present when parallel requests drained the remaining balance before the current charge completed. The request still succeeded, and the amount actually charged is less than the nominal cost — the total is in cost_vibes
charge_log_failed boolean Present when the usage log could not be written. The result was delivered, no funds were charged (cost_vibes: 0)

Response example

JSON
{
  "query": "what is new in Cursor IDE",
  "provider": "bitrix-search",
  "search_depth": "advanced",
  "answer": "Cursor 3 — a redesign of the IDE interface [1]. An agents window was added [2].",
  "results": [
    {
      "id": 1,
      "url": "https://cursor.com/blog/cursor-3",
      "title": "Meet the new Cursor",
      "content": "Cursor 3 brings a new agent-first interface...",
      "rawContent": null,
      "score": null,
      "publishedDate": null
    },
    {
      "id": 2,
      "url": "https://cursor.com/changelog",
      "title": "Changelog",
      "content": "Latest features in Cursor IDE...",
      "rawContent": null,
      "score": null,
      "publishedDate": null
    }
  ],
  "images": [],
  "ignored_filters": [],
  "search_id": "ws_20260430113025_a1b2c3d4",
  "upstream_search_id": "AG_xyz",
  "cost_vibes": 5,
  "duration_ms": 8523
}

The provider value in the example (bitrix-search) and cost_vibes depend on the instance: without an explicit provider, the request is handled by the default engine configured on the instance (shown by the defaultProvider field in GET /v1/me), and the charged amount is its price from GET /v1/search/providers.

With include_raw_content: true on a supporting engine the rawContent field is populated with the full page text, and with include_images: true the top-level images array is populated with image links. If the engine does not support topic or include_images, those parameters land in ignored_filters.

Output safety. rawContent is unsanitized text from the open web, and images[] are third-party URLs. Before rendering in a browser, sanitize the text and proxy or validate the image links — do not insert them into the DOM as-is.

Streaming (SSE)

With stream: true the response arrives as an event stream with the text/event-stream content type. Each event is an event: <type> + data: <JSON> pair, separated by a blank line.

Possible events:

Event When it occurs data fields
start At the start of the request search_id, provider, search_depth
thinking Intermediate agent reasoning content — a text fragment
tool_call The agent called an internal tool tool (web_search / content_extraction / external_tool), description
tool_result The tool returned a result description, items_count
answer_delta The next fragment of the final answer content — a chunk of text
done The final block the same fields as in the synchronous response
error A failure during processing error.code, error.message

Example stream:

event: start
data: {"search_id":"ws_20260430113025_a1b2c3d4","provider":"bitrix-search","search_depth":"advanced"}

event: thinking
data: {"content":"Let's look into the sources now"}

event: tool_call
data: {"tool":"web_search","description":"web search query"}

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

event: answer_delta
data: {"content":"Cursor 3 — "}

event: answer_delta
data: {"content":"a redesign of the IDE interface [1]."}

event: done
data: {"query":"what is new in Cursor IDE","provider":"bitrix-search","search_depth":"advanced","answer":"Cursor 3 — a redesign of the IDE interface [1].","results":[{"id":1,"url":"https://cursor.com/blog/cursor-3","title":"Meet the new Cursor","content":"...","rawContent":null,"score":null,"publishedDate":null}],"images":[],"ignored_filters":[],"search_id":"ws_20260430113025_a1b2c3d4","upstream_search_id":"AG_xyz","cost_vibes":5,"duration_ms":8523}

Whether a real event stream with intermediate thinking / tool_call / answer_delta is emitted depends on the engine: for bitrix-search this varies by instance, while the other providers stream in buffered mode — only start and done arrive. A progressive stream is indicated by the capabilities.modes.streaming field in GET /v1/search/providers.

Error response example

402 — insufficient balance:

JSON
{
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Insufficient vibes for this search.",
    "userMessage": "Insufficient vibes — top up your balance or add your own BYOK key (see GET /v1/search/providers).",
    "hint": "Add a BYOK key to search for free — see GET /v1/search/providers",
    "required": 5
  }
}

The required field is the amount in Ꝟ needed for the request. The other situations are listed in the error table below.

Errors

HTTP Code Description
400 INVALID_REQUEST Empty query, query longer than 400 characters, max_results outside the 1..20 range, max_steps outside the 1..5 range, provider not from the list of supported providers, unknown value for search_depth, topic, lang or time_range, include_domains / exclude_domains longer than 10 elements
402 INSUFFICIENT_BALANCE The portal balance does not have enough Ꝟ for the selected mode
402 BILLING_FROZEN The billing account is frozen
403 SCOPE_DENIED The key lacks the vibe:search scope
404 PROVIDER_NOT_FOUND The provider passed does not exist in the system or is disabled
404 CREDENTIAL_NOT_FOUND The provider has neither a USER, PORTAL, nor PLATFORM key
404 PROVIDER_DOES_NOT_SUPPORT_SEARCH provider: "jina" was requested — Jina works only in POST /v1/research. The search_id field is present in the response
429 RATE_LIMITED Exceeded the limit of 60 requests per minute per portal, shared across all of the portal's API keys
500 INTERNAL_ERROR Internal server error. The response contains search_id
502 UPSTREAM_ERROR The provider returned an error. No charge
503 FEATURE_NOT_ENABLED Web Search is unavailable on this platform
503 UPSTREAM_TIMEOUT The provider exceeded the request timeout. No charge. The response carries the Retry-After: 30 header — retry the request after the time it specifies

The full list of common API errors — Errors.

Known specifics

Key selection cascade. When the provider field is omitted, the platform picks the source in the order: user key (USER BYOK) → Bitrix24 account key (PORTAL BYOK) → the default engine configured on the instance (shown by the defaultProvider field in GET /v1/me). Your own BYOK key with isDefault: true is used automatically — you do not need to pass provider.

The charge happens only after a successful response. Before the request the balance is checked: if insufficient, 402 INSUFFICIENT_BALANCE is returned without calling the provider. On UPSTREAM_ERROR or UPSTREAM_TIMEOUT the balance does not change — the provider returned no result, so there is nothing to charge for.

The default engine ignores lang in /v1/search. It does not forward lang to the upstream provider — the field always appears in X-Search-Filters-Ignored.

An empty result set is charged in full. When the provider found no sources and returned results: [], cost_vibes is still charged in full. This is by design: the charge covers the provider's processing of the request, not the number of results.

The charge_log_failed field signals a lost audit record. If the provider responds successfully but the usage log cannot be written, the response is still delivered to the client with cost_vibes: 0 and the charge_log_failed: true flag. No Vibe credits were charged — the audit log for this request is also absent. The scenario is rare and is visible in the AI Search section (/search).

provider: "jina" is available only in /v1/research. A request with provider: "jina" on the current endpoint returns 404 PROVIDER_DOES_NOT_SUPPORT_SEARCH. The list of providers supporting /v1/search is checked via the capabilities.modes.search.basic or capabilities.modes.search.advanced field in GET /v1/search/providers.

See also