Esc to close · ⌘K / Ctrl-K opens search anywhere
By default a model answers only from its training data — it can't see today's web. Add
:online to the model id (or send the web plugin) and BharatRouter
makes the request live: it searches the web, feeds the model cited results,
and the model answers from them. It works for every model we route — Krutrim, Sarvam,
GLM, GPT, Claude — so India-first models get to be online too.
OpenRouter-compatible. The :online suffix, the web
plugin, and the url_citation annotation shape all match OpenRouter's web plugin —
so migrating is a base-URL swap, not a rewrite.
:online suffixAppend :online to any model id. Simplest possible switch:
curl https://api.bharatrouter.com/v1/chat/completions \
-H "Authorization: Bearer br-..." -H "Content-Type: application/json" \
-d '{
"model": "glm-4.6:online",
"messages": [{"role": "user", "content": "Who won the F1 race last weekend?"}]
}' web plugin (tunable)For control over the engine, result count and domain filters, send a web
plugin instead of the suffix:
{
"model": "glm-4.6",
"plugins": [{
"id": "web",
"engine": "exa",
"max_results": 5,
"search_prompt": "Some relevant web results:",
"include_domains": ["reuters.com"],
"exclude_domains": ["reddit.com"]
}],
"messages": [{"role": "user", "content": "..."}]
} | Field | Default | Meaning |
|---|---|---|
engine | exa | Search engine — exa, brave, or tavily (BYOK; see below). |
max_results | 5 | How many results to fetch and inject (1–20). |
search_prompt | "Some relevant web results:" | The preamble prepended to the injected context block. |
include_domains | — | Restrict results to these domains. |
exclude_domains | — | Drop results from these domains. |
BharatRouter is native-first: if the model you routed to runs web search itself and you've brought a key for it, we let the provider search server-side; otherwise we run our own search layer and inject the results. You don't choose — the router picks the best available path per model.
We call a hosted search engine, fetch the top results with their text, and inject them as a cited context block ahead of your messages — then route to whatever model you asked for. This is how a model with no browsing of its own (GLM, Krutrim, Sarvam…) goes live.
The default engine is Exa (the same engine OpenRouter's plugin uses, so
answers behave the same on migration). Brave (independent index) and
Tavily are also available. All three are BYOK — save
your engine key once under its id (exa/brave/tavily) on
the dashboard BYOK tab or PUT /me/byok/exa, and :online uses it. The
engine bills your own account.
Some providers run web search inside their own API, tuned to their model. Where the routed model supports it and you've saved that provider's key, BharatRouter forwards the provider's native search instead of our layer — you get the provider's first-party browsing. This is BYOK-only: we hold no serving key for these providers, so native search runs on your key and the provider bills you directly (a BYOK request is ₹0 on our side).
| Provider | Models | Surface |
|---|---|---|
| Anthropic | claude-opus-4.8, claude-sonnet-5, claude-haiku-4.5 | Native Messages API (/v1/messages). Claude runs its web_search tool server-side. On the OpenAI-compatible chat surface, Claude uses the search layer instead (the compat API can't accept the native tool). |
| OpenAI | gpt-4o-search-preview, gpt-4o-mini-search-preview | Chat completions + the Responses API. Sent as web_search_options. Regular gpt-4o/gpt-5 don't search natively — use them with the search layer. |
Example — Claude with native search via the Messages API (your saved Anthropic key, or a forwarded Claude session):
POST https://api.bharatrouter.com/v1/messages
{ "model": "claude-sonnet-5:online",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Latest on the RBI repo rate?"}] } Results come back as url_citation annotations on the assistant message — the
same shape as OpenRouter, so existing citation-parsing keeps working:
"annotations": [
{ "type": "url_citation",
"url_citation": { "url": "https://...", "title": "...", "content": "..." } }
] On streaming responses the annotations arrive in a leading chunk; provider-native search emits citations in the provider's own shape.
Every :online response is tagged so you can see what happened:
| Header | Example | Meaning |
|---|---|---|
x-br-web-search | exa:5 | Search layer ran on exa and injected 5 results. |
x-br-web-search | anthropic:native | Claude ran its own native search server-side. |
x-br-web-search | openai:native | OpenAI ran native search via web_search_options. |
x-br-web-search | exa:no-key | :online asked for, but no engine key saved — answered without browsing (fail-open). |
Web search sends your query to a search provider, all of which are hosted outside India, so
an :online request is tagged residency: global — enabling search
means the query text leaves India. A request pinned
data_policy: india_only should not use
:online. Inference itself stays zero-retention by default; the injected search
context follows the same rule.
Search never breaks your completion. If the engine errors, returns nothing, or no key is
configured, the model answers without browsing and the x-br-web-search header
tells you why (:error, :0, :no-key). You always get an
answer.