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
Fieldquery Typestring Requiredyes Default— DescriptionQuery text. From 1 to 400 characters
Fieldsearch_depth Typestring Requiredno Defaultbasic DescriptionSearch depth: basic or advanced. advanced costs more — switch to it only when the basic response lacks the data you need, see How to choose the depth. The price of each mode is listed in GET /v1/search/providers
Fieldprovider Typestring Requiredno Defaultcascade USER → PORTAL → instance default engine DescriptionForced engine choice: one of bitrix-search, tavily, brave, exa, you-com, linkup, perplexity, z-ai. jina is available only in POST /v1/research
Fieldtopic Typestring Requiredno Defaultgeneral DescriptionSearch topic: general or news. Not all engines support the news mode — those that do populate publishedDate for news results, while the rest ignore it
Fieldmax_results Typenumber Requiredno Default5 DescriptionNumber of results in the response. From 1 to 20
Fieldmax_steps Typenumber Requiredno Default3 DescriptionMaximum 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
Fieldlang Typestring Requiredno Defaultru DescriptionSearch language: ru, en or auto. The default engine ignores this field — see "Known specifics"
Fieldinclude_answer Typeboolean Requiredno Defaulttrue DescriptionInclude the synthesized answer in the answer field
Fieldinclude_raw_content Typeboolean Requiredno Defaultfalse DescriptionRequest the full text of the pages found. For engines that support it, the text arrives in results[].rawContent
Fieldinclude_images Typeboolean Requiredno Defaultfalse DescriptionRequest images for the query. For engines that support it, the links arrive in the top-level images array
Fieldinclude_domains Typestring[] Requiredno Default[] DescriptionRestrict the search to the domains in the list. Up to 10 hostnames
Fieldexclude_domains Typestring[] Requiredno Default[] DescriptionExclude domains. Up to 10 hostnames
Fieldtime_range Typestring | null Requiredno Defaultnull DescriptionPublication time window: day, week, month, year
Fieldstream Typeboolean Requiredno Defaultfalse Descriptiontrue — 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.

How to choose the depth

A request without search_depth runs in basic mode. advanced costs more. The prices of both modes on your instance are listed in GET /v1/search/providers.

Task Depth
TaskFind a fact, price, date, link or recent news item Depthbasic
TaskGive a language model context for answering the user Depthbasic
TaskRequests in a loop, on a schedule, or on every dashboard or widget refresh Depthbasic
TaskA one-off request when the basic response lacks the data you need Depthadvanced
TaskA summary of one topic across many sources Depthadvanced
TaskMulti-step research with a report DepthPOST /v1/research, not advanced in a loop

Rule for agents and apps: send the request with basic first. Switch to advanced only if the basic response lacks the data you need. Do not make advanced your default.

A request with advanced differs from the examples below only in the search_depth value:

JSON
{
  "query": "what is new in Cursor IDE",
  "search_depth": "advanced",
  "max_results": 5,
  "lang": "auto"
}

Apps that refresh on their own — dashboards, widgets, scheduled runs — pay for every request. Spend per period is refresh rate × number of widgets × mode price. Cache the result on your side and do not repeat an identical request more often than the data changes.

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": "basic",
    "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": "basic",
    "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: 'basic',
    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: 'basic',
    max_results: 5,
    lang: 'auto',
  }),
})

const data = await res.json()

Response fields

Field Type Description
Fieldquery Typestring DescriptionThe original query text
Fieldprovider Typestring DescriptionIdentifier of the provider that processed the request (e.g. bitrix-search, tavily)
Fieldsearch_depth Typestring DescriptionThe applied search depth: basic or advanced
Fieldanswer Typestring | null DescriptionSynthesized answer. Engines with citation support mark the sources with [N]. Comes back null when include_answer: false, and for engines that do not support synthesis: the capabilities.output.synthesizedAnswer flag in GET /v1/search/providers shows which engines do
Fieldresults Typearray DescriptionArray of found sources
Fieldresults[].id Typenumber DescriptionSequential number. Matches the [N] citation marker in answer for engines that emit citations
Fieldresults[].url Typestring DescriptionPage address
Fieldresults[].title Typestring DescriptionPage title
Fieldresults[].content Typestring DescriptionAlways a short description or snippet of the page. The full text arrives in a separate rawContent field, not here
Fieldresults[].rawContent Typestring | null DescriptionThe 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
Fieldresults[].score Typenumber | null DescriptionRelevance score from the provider. For tavily, a number from 0 to 1: the higher the value, the more relevant the result. Comes back null when the engine does not return a score: the capabilities.output.relevanceScore flag in GET /v1/search/providers shows which engines do
Fieldresults[].publishedDate Typestring | null DescriptionPublication date in ISO 8601 format. Comes back null when the engine does not return a date: the capabilities.output.publishedDate flag in GET /v1/search/providers shows which engines do. Engines that support topic: "news" populate it for news results
Fieldimages Typearray DescriptionTop-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
Fieldignored_filters Typestring[] DescriptionThe 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
Fieldsearch_id Typestring DescriptionRequest identifier in the format ws_<14 digits>_<8 hex>
Fieldupstream_search_id Typestring | null DescriptionIdentifier at the external provider. Useful for incident investigation
Fieldbilling_source Typestring DescriptionOptional. Payment source: wallet — wallet, including usage beyond the AI quota, quota — Bitrix24 account AI quota, cowork — Cowork seat quota, byok — your own provider key. Omitted for a free request using a platform key
Fieldcost_vibes Typenumber DescriptionHow many Ꝟ were actually charged to the wallet. When billing_source is quota, cowork or byok, the value is 0
Fieldduration_ms Typenumber DescriptionRequest processing duration in milliseconds
Fieldpartial_charge Typeboolean DescriptionPresent 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
Fieldcharge_log_failed Typeboolean DescriptionOptional. true if charging or writing the usage log failed. The successful search result is returned to the client. If the charge completed before the log failed, cost_vibes retains the amount actually charged to the wallet

Response example

JSON
{
  "query": "what is new in Cursor IDE",
  "provider": "bitrix-search",
  "search_depth": "basic",
  "answer": "Cursor 3 — a redesign of the IDE interface. An agents window was added.",
  "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",
  "billing_source": "wallet",
  "cost_vibes": 5,
  "duration_ms": 8523
}

The provider value in the example (bitrix-search) depends on the instance: without an explicit provider, the request is handled by the default engine configured on the instance. The defaultProvider field in GET /v1/me shows the engine, and GET /v1/search/providers shows its pricing. The example shows a wallet charge. When using AI quota or Cowork seat quota, cost_vibes is 0. The payment source selection order is described under Pricing.

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
Eventstart When it occursAt the start of the request data fieldssearch_id, provider, search_depth
Eventthinking When it occursIntermediate agent reasoning data fieldscontent — a text fragment
Eventtool_call When it occursThe agent called an internal tool data fieldstool (web_search / content_extraction / external_tool), description
Eventtool_result When it occursThe tool returned a result data fieldsdescription, items_count
Eventanswer_delta When it occursThe next fragment of the final answer data fieldscontent — a chunk of text
Eventdone When it occursThe final block data fieldsThe same fields as in the synchronous response, including billing_source and cost_vibes
Eventerror When it occursA failure during processing data fieldserror.code, error.message

Example stream:

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

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."}

event: done
data: {"query":"what is new in Cursor IDE","provider":"bitrix-search","search_depth":"basic","answer":"Cursor 3 — a redesign of the IDE interface.","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","billing_source":"wallet","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

Quota refusals arrive as regular HTTP responses with an error object before the SSE stream opens. Check the HTTP status and error.code before reading stream events.

HTTP Code Description
HTTP400 CodeINVALID_REQUEST DescriptionEmpty query, query longer than 400 characters, max_results outside the 1..20 range, max_steps outside the 1..5 range, provider outside the list of supported providers, an unknown value for search_depth, topic, lang or time_range, include_domains / exclude_domains longer than 10 elements
HTTP402 CodeINSUFFICIENT_BALANCE DescriptionThe portal balance does not have enough Ꝟ for the selected mode
HTTP402 CodeBILLING_FROZEN DescriptionThe billing account is frozen
HTTP402 CodeAI_QUOTA_EXHAUSTED DescriptionAI quota is exhausted and wallet overage is unavailable. The reason is in error.reason, and the reset time is in error.resetAt. Wait for the reset, enable overage in your Vibecode account and top up the wallet, or use your own provider key. The optional error.learnMore field contains additional information
HTTP429 CodeAI_PACING_LIMITED DescriptionThe AI quota pacing limit is reached. The response includes error.reason, error.overageDenied, error.resetAt and the Retry-After header in seconds. Wait for the specified time or use your own provider key
HTTP402 CodeCOWORK_QUOTA_EXHAUSTED DescriptionA Cowork seat quota window is exhausted. The error.window field is fiveHour, week or month. Wait until error.resetAt or for the delay in the Retry-After header in seconds, or use your own provider key
HTTP402 CodeCOWORK_SUBSCRIPTION_INACTIVE DescriptionThere is no active Cowork seat. Activate or resume the seat, or use your own provider key
HTTP402 Codecompany_budget_exhausted DescriptionThe monthly company spend budget set by the portal administrator is exhausted. The scope field names the budget that was hit: USER — the caller's own budget, PORTAL — the budget of the whole portal. The canRequest field tells whether an increase can be requested: true for a personal budget, false for the portal one, which only an administrator raises. The rejection arrives when paying from the wallet, including usage beyond the AI quota. A search made with your own BYOK key is free and keeps working
HTTP403 CodeSCOPE_DENIED DescriptionThe key lacks the vibe:search scope
HTTP404 CodePROVIDER_NOT_FOUND DescriptionThe provider passed does not exist in the system or is disabled
HTTP404 CodeCREDENTIAL_NOT_FOUND DescriptionThe provider has no USER, PORTAL, or PLATFORM key
HTTP404 CodePROVIDER_DOES_NOT_SUPPORT_SEARCH Descriptionprovider: "jina" was requested — Jina works only in POST /v1/research. The search_id field is present in the response
HTTP429 CodeRATE_LIMITED DescriptionExceeded the limit of 60 requests per minute per portal, shared across all of the portal's API keys. The current limit is returned in the x-ratelimit-limit header
HTTP429 CodeSEARCH_DAILY_CAP_REACHED DescriptionWith this request, total search usage from the wallet, AI quota and Cowork seat quota for the current day in UTC+3 would exceed the cap set by the Bitrix24 account admin. The provider is not called and nothing is charged. The cap is shared by /v1/search and POST /v1/research. The response carries a Retry-After header, and the error body carries resetAt — the start of the next day in UTC+3 — and hint. Searches on your own BYOK key are not limited by the cap
HTTP500 CodeINTERNAL_ERROR DescriptionInternal server error. The response contains search_id
HTTP401/403/429/502 CodeUPSTREAM_ERROR DescriptionThe provider returned an error; no charge. The upstream_status field in the response carries the provider's original status. For a BYOK key, provider 401 and 403 responses keep their status — the provider rejected your key, so retrying without replacing it will not help. A provider 429 keeps its status for any key and carries the Retry-After header — retry later. Every other provider error, including a rejected platform-engine key, arrives as 502 — retry the request
HTTP503 CodeFEATURE_NOT_ENABLED DescriptionWeb Search is unavailable on this platform
HTTP503 CodeUPSTREAM_TIMEOUT DescriptionThe provider exceeded the request timeout. No charge. The response carries the Retry-After: 30 header — retry the request after the time it specifies

For the full list of common API errors, see 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 calling the provider, the platform checks whether payment can proceed from the selected source. Wallet payments, including AI quota overage, require balance and company budget checks. Provider errors consume neither quota nor wallet funds.

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. When the provider found no sources and returned results: [], the successful request consumes quota or wallet funds according to the selected payment source. The charge covers the provider's processing of the request, not the number of results.

An accounting failure does not invalidate the search result. When charge_log_failed: true, do not resend the request just to write the usage log: another search is a separate request with a separate charge.

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.

A Bitrix24 account admin can turn off the advanced mode. Paid search through the platform engine with search_depth: "advanced" then runs as basic, and the request is not rejected. The response and the SSE start and done events carry search_depth: "basic", and the response headers, synchronous and streaming alike, include X-Search-Depth-Downgraded: advanced. The charge follows the basic price, and the request counts toward the daily spend cap at that price. Search on your own BYOK key is not affected.

See also