لوكلاء AI: ملف markdown لهذه الصفحة — /docs-content-en/search/run.md فهرس التوثيق — /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:
{
"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
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
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
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
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 — 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 |
Fieldcost_vibes |
Typenumber | DescriptionHow many Ꝟ were actually charged |
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 | DescriptionPresent when the usage log could not be written. The result was delivered, no funds were charged (cost_vibes: 0) |
Response example
{
"query": "what is new in Cursor IDE",
"provider": "bitrix-search",
"search_depth": "basic",
"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 |
|---|---|---|
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 |
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 [1]."}
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 [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:
{
"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 |
|---|---|---|
| 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 | 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 only on a paid call. 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 exact value arrives in the x-ratelimit-limit header (the cap is divided across replicas) |
| HTTP429 | CodeSEARCH_DAILY_CAP_REACHED |
DescriptionWith this request, the Bitrix24 account's paid search spend 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 |
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 are 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.
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.