Skip to content
RU

Web search

A model answers from the data it was trained on, so about yesterday’s news, today’s exchange rate or last week’s release it will either stay silent or invent something. The web_search parameter on POST /v1/chat/completions turns search on: the model decides on its own when to search, and returns an answer with links to the sources.

Value What it does
true or "auto" The provider’s native search where the model has one, Exa otherwise
"native" The provider’s built-in search only
"exa" Neutral Exa search regardless of the model
absent or false No search

Native search exists for OpenAI, Anthropic, Google and xAI models — except the older GPTs (gpt-4o, chatgpt-4o, gpt-4-turbo), which fall back to Exa. Do not infer it from the name: GET /v1/models reports capabilities.native_search per model.

"native" on a model without built-in search is not an error — the value is silently downgraded to "auto" and the request goes to Exa. That is deliberate: the engine choice usually lives in client settings and outlives a model switch, so a hard error would break a conversation for no good reason.

Links arrive in message.annotations, in the OpenAI format:

{
"message": {
"role": "assistant",
"content": "The ECB left rates unchanged at its September meeting.",
"annotations": [
{"type": "url_citation", "url_citation": {"url": "https://www.ecb.europa.eu/press/", "title": "ECB press release"}}
]
}
}

When streaming, sources arrive in their own chunk carrying delta.annotations before the final chunk with finish_reason — a client that reads the stream to the end will not miss them. Nothing is mixed into the prose: content stays clean and the links stay structured.

perplexity/sonar, sonar-pro and sonar-deep-research always search, regardless of web_search: search is part of the model and there is nothing to switch off. You do not need to pass the parameter, and the engine choice does not apply to them — which is exactly why their capabilities.native_search is false. Sources come back the same way, in annotations.

Search is billed per request, separately from tokens, on top of the usual charge for the answer. Each model declares its own native-search price — it is in the catalog as pricing.web_search_rub_per_request (and web_search_usd_per_request). Exa has a single price across all models, so it does not appear on a model card; the actual charge for a given request is visible in GET /v1/history.

curl -s https://api.mixen.ai/v1/models \
-H "Authorization: Bearer $MIXEN_API_KEY" \
| python -c "import json,sys; [print(m['id'], m['pricing'].get('web_search_rub_per_request')) for m in json.load(sys.stdin)['data'] if m['pricing'].get('web_search_rub_per_request')]"

Before generating with search enabled we reserve the cost of the search and of the retrieved context on your balance — a request will not start if the money only covers the answer itself. A reserve is not a charge: whatever is not used stays on the balance, and you are billed for actual usage.

from openai import OpenAI
client = OpenAI(base_url="https://api.mixen.ai/v1", api_key=MIXEN_API_KEY)
resp = client.chat.completions.create(
model="gemini-3.1-pro",
messages=[{"role": "user", "content": "What did OpenAI ship this week?"}],
extra_body={"web_search": "native"},
)
print(resp.choices[0].message.content)
for a in resp.choices[0].message.annotations or []:
print("", a["url_citation"]["url"])
  • The model searches, not you. web_search only permits search; whether to go online is the model’s call. For a question it already knows the answer to there will be no search — and no charge for one.
  • Results arrive as input tokens. What is found is injected into the context, so such a request costs more than usual even beyond the search fee itself.
  • web_search is not an OpenAI field. Official SDKs take it via extra_body (Python) or as an extra body field (TypeScript, cURL).