# Web search
Any catalog model can search the web. Ask for it in the request and Keln runs the search, passes
the results to the model, bills the search, and returns the answer with citations and, when you
ask, the sources it used. It works the same on chat completions, the Responses API, and the
Anthropic-compatible API, each in its own native shape, so [Codex](../codex.md), Claude Code,
[Claude Desktop](../claude-desktop.md), and the OpenAI and Anthropic SDKs need no extra setup.
Price: **$10 per 1,000 searches**, on top of tokens. A search is one query, whatever the number
of results. Keln never bills errors, attempts past the cap, or searches the model did not run.
## Turning it on
Search is off unless the request asks for it.
=== "Chat completions"
Send either of these. They are equivalent:
```json
{ "model": "zai-org/glm-5.3", "messages": [...], "web_search_options": {} }
```
```json
{ "model": "zai-org/glm-5.3", "messages": [...], "plugins": [{ "id": "web" }] }
```
=== "Responses"
OpenAI's hosted tool, unchanged. Codex sends this by default.
```json
{ "model": "zai-org/glm-5.3", "input": "...", "tools": [{ "type": "web_search" }] }
```
=== "Anthropic (Claude Code)"
Anthropic's server tool, unchanged. Claude Code's WebSearch sends this.
```json
{ "model": "zai-org/glm-5.3", "messages": [...],
"tools": [{ "type": "web_search_20250305", "name": "web_search", "max_uses": 5 }] }
```
## What comes back
The model decides when to search, and may search more than once in a turn. Results reach the
model as ranked excerpts, up to 10 per search. What you receive, per API:
| API | Search activity | Citations | Sources on request |
|---|---|---|---|
| Chat completions | in usage only | `message.annotations[]`, OpenAI's `url_citation` shape plus `content`, the excerpt the citation refers to | `web_search_options.include_sources: true` → `message.web_search_sources[]` |
| Responses | a `web_search_call` item per search, with its lifecycle events and `action.query` | `url_citation` annotations on `output_text` | `include: ["web_search_call.action.sources"]` → `action.sources` on the item |
| Anthropic | `server_tool_use` and `web_search_tool_result` blocks, results included | `citations` on the text block | always, in the result block |
Citations are the links the model itself wrote. Each appears in the text as a markdown link named
by its domain, and Keln also returns it as an annotation with exact character offsets. Keln never
adds a citation the model did not make.
`usage` reports the search count on every API: `usage.web_search_requests` on chat and Responses,
`usage.server_tool_use.web_search_requests` on Anthropic. `usage.cost` includes the fee.
## Limits and settings
- **Searches per turn.** 10, which is also the maximum. To allow fewer, set
`web_search_options.max_uses` on chat, or the tool's `max_uses` on Anthropic. Past the cap, Keln
asks the model to answer with what it has.
- **Results per search.** 10, fixed.
- **Your own tools still work.** Keln offers search alongside them under `tool_choice: "auto"`.
With `"none"` or a forced choice, Keln does not offer search, serves the request as usual, and
names the field in the `x-keln-inert-params` header. If you define a function tool named
`web_search` and also opt into search, Keln returns `400 web_search_name_reserved`.
- **Search results do not persist across turns** on chat and Responses, same as OpenAI: only the
model's answer is kept. On the Anthropic-compatible API, the result block you receive is the state, so
sending it back restores the results for the next turn.
- **Accepted but not applied.** Keln ignores `search_context_size`, `user_location`, and domain
`filters`, and names them in the `x-keln-inert-params` header.
## Privacy
Keln sends the model's search queries to a third-party search engine, passes the results back to
the model, and stores neither the queries nor the results; its logs keep only the length of each
query and the number of results. Full statement:
[Privacy & zero data retention](privacy.md#web-search).
## Errors
| Code | Meaning |
|---|---|
| `web_search_name_reserved` | the request opts into search and also defines a function tool named `web_search` |
| `web_search_unavailable` | search is not enabled on this deployment |
A search that fails on the engine side is not an error to you. Keln tells the model, the model
answers with what it knows, and Keln does not bill the search.
Web search
Any catalog model can search the web. Ask for it in the request and Keln runs the search, passes
the results to the model, bills the search, and returns the answer with citations and, when you
ask, the sources it used. It works the same on chat completions, the Responses API, and the
Anthropic-compatible API, each in its own native shape, so Codex, Claude Code,
Claude Desktop, and the OpenAI and Anthropic SDKs need no extra setup.
Price: $10 per 1,000 searches, on top of tokens. A search is one query, whatever the number
of results. Keln never bills errors, attempts past the cap, or searches the model did not run.
The model decides when to search, and may search more than once in a turn. Results reach the
model as ranked excerpts, up to 10 per search. What you receive, per API:
API
Search activity
Citations
Sources on request
Chat completions
in usage only
message.annotations[], OpenAI's url_citation shape plus content, the excerpt the citation refers to
a web_search_call item per search, with its lifecycle events and action.query
url_citation annotations on output_text
include: ["web_search_call.action.sources"] → action.sources on the item
Anthropic
server_tool_use and web_search_tool_result blocks, results included
citations on the text block
always, in the result block
Citations are the links the model itself wrote. Each appears in the text as a markdown link named
by its domain, and Keln also returns it as an annotation with exact character offsets. Keln never
adds a citation the model did not make.
usage reports the search count on every API: usage.web_search_requests on chat and Responses,
usage.server_tool_use.web_search_requests on Anthropic. usage.cost includes the fee.
Limits and settings
Searches per turn. 10, which is also the maximum. To allow fewer, set
web_search_options.max_uses on chat, or the tool's max_uses on Anthropic. Past the cap, Keln
asks the model to answer with what it has.
Results per search. 10, fixed.
Your own tools still work. Keln offers search alongside them under tool_choice: "auto".
With "none" or a forced choice, Keln does not offer search, serves the request as usual, and
names the field in the x-keln-inert-params header. If you define a function tool named
web_search and also opt into search, Keln returns 400 web_search_name_reserved.
Search results do not persist across turns on chat and Responses, same as OpenAI: only the
model's answer is kept. On the Anthropic-compatible API, the result block you receive is the state, so
sending it back restores the results for the next turn.
Accepted but not applied. Keln ignores search_context_size, user_location, and domain
filters, and names them in the x-keln-inert-params header.
Privacy
Keln sends the model's search queries to a third-party search engine, passes the results back to
the model, and stores neither the queries nor the results; its logs keep only the length of each
query and the number of results. Full statement:
Privacy & zero data retention.
Errors
Code
Meaning
web_search_name_reserved
the request opts into search and also defines a function tool named web_search
web_search_unavailable
search is not enabled on this deployment
A search that fails on the engine side is not an error to you. Keln tells the model, the model
answers with what it knows, and Keln does not bill the search.