> ## Documentation Index
> Fetch the complete documentation index at: https://docs.overcontrolgroup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Web search

> Find web sources and answer with citations during a Lume request.

Search the web with Lume to find sources and answer questions with citations. OverControl runs the search during your request, so your application does not need to execute a search function.

**Lume 3.5**, **Lume 3.5 Max**, and **Lume Omni** support `web_search` on Chat Completions and Messages. Lume 2 does not support this server-side tool.

## Before you start

Create an API key in the [dashboard](https://app.overcontrolgroup.com/dashboard) with `chat:completions` for Chat Completions or `messages:create` for Messages. If your key has a model allowlist, include `lume-3.5`. Keep the key on your server and set it in the shell where you run the examples:

```bash theme={null}
export OVERCONTROL_API_KEY="your_api_key"
```

Send one authentication header per request: `Authorization: Bearer` or `x-api-key`. Supplying both with values returns HTTP 401.

For SDK examples, install the client you use. cURL examples do not need an SDK.

```bash Python: Chat Completions theme={null}
python -m pip install openai
```

```bash Python: Messages theme={null}
python -m pip install anthropic
```

Run Python examples from a `.py` file.

Messages accepts an optional `anthropic-version` header. If supplied, it must be one valid calendar date in `YYYY-MM-DD` form. It is a compatibility hint and does not change the supported features. The SDK supplies its version header automatically.

## Chat Completions

Call `POST /v1/chat/completions` with `chat:completions`. Declare the tool in `tools` and use the named choice shown below to require it. Omit `tool_choice` when the model should decide whether it needs the tool.

```bash cURL theme={null}
curl https://api.overcontrolgroup.com/v1/chat/completions \
  -H "Authorization: Bearer $OVERCONTROL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "lume-3.5",
  "max_tokens": 512,
  "messages": [
    {
      "role": "user",
      "content": "Find the official Python tutorial and summarize what it covers, with sources."
    }
  ],
  "tools": [
    {
      "type": "web_search",
      "max_uses": 1,
      "allowed_domains": [
        "docs.python.org"
      ]
    }
  ],
  "tool_choice": {
    "type": "web_search"
  }
}'
```

```python Python theme={null}
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["OVERCONTROL_API_KEY"],
    base_url="https://api.overcontrolgroup.com/v1",
)

response = client.chat.completions.create(
    model="lume-3.5",
    max_tokens=512,
    messages=[
        {
            "role": "user",
            "content": "Find the official Python tutorial and summarize what it covers, with sources."
        }
    ],
    tools=[
        {
            "type": "web_search",
            "max_uses": 1,
            "allowed_domains": [
                "docs.python.org"
            ]
        }
    ],
    tool_choice={
        "type": "web_search"
    },
)

choice = response.choices[0]
if choice.finish_reason == "length":
    raise RuntimeError("The answer reached its output limit.")
answer = choice.message.model_dump(exclude_none=True)
print(answer.get("content", ""))
for annotation in answer.get("annotations", []):
    if annotation.get("type") == "url_citation":
        source = annotation["url_citation"]
        print(source["title"], source["url"])
print(response.usage.model_dump(exclude_none=True))
```

Read the answer from `choices[0].message.content`. Source references appear in `choices[0].message.annotations` with `type: "url_citation"`. Each `url_citation` has `url`, `title`, `start_index`, and `end_index`. Use those offsets to associate a source with the cited text, and display source links with the answer.

Chat returns the final answer and annotations; it does not return the native Messages server-tool result blocks. An absent citation is not evidence that a claim was verified. Check the answer and successful-operation counts when the task depends on retrieval.

## Messages

Call `POST /v1/messages` with `messages:create`. The named choice is `{"type":"tool","name":"web_search"}`. The declaration uses the same tool options as Chat.

```bash cURL theme={null}
curl https://api.overcontrolgroup.com/v1/messages \
  -H "x-api-key: $OVERCONTROL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "lume-3.5",
  "max_tokens": 512,
  "messages": [
    {
      "role": "user",
      "content": "Find the official Python tutorial and summarize what it covers, with sources."
    }
  ],
  "tools": [
    {
      "type": "web_search",
      "max_uses": 1,
      "allowed_domains": [
        "docs.python.org"
      ]
    }
  ],
  "tool_choice": {
    "type": "tool",
    "name": "web_search"
  }
}'
```

```python Python theme={null}
import os
from anthropic import Anthropic

client = Anthropic(
    api_key=os.environ["OVERCONTROL_API_KEY"],
    base_url="https://api.overcontrolgroup.com",
)

message = client.messages.create(
    model="lume-3.5",
    max_tokens=512,
    messages=[
        {
            "role": "user",
            "content": "Find the official Python tutorial and summarize what it covers, with sources."
        }
    ],
    tools=[
        {
            "type": "web_search",
            "max_uses": 1,
            "allowed_domains": [
                "docs.python.org"
            ]
        }
    ],
    tool_choice={
        "type": "tool",
        "name": "web_search"
    },
)

if message.stop_reason == "max_tokens":
    raise RuntimeError("The answer reached its output limit.")
for block in message.content:
    item = block.model_dump(exclude_none=True)
    if item["type"] == "text":
        print(item["text"])
        for citation in item.get("citations", []):
            print("Citation:", citation)
    elif item["type"] == "web_search_tool_result":
        result = item["content"]
        if isinstance(result, dict) and result.get("type", "").endswith("_error"):
            print("Tool error:", result["error_code"])
        else:
            print("Tool result:", result)
print(message.usage.model_dump(exclude_none=True))
```

Messages returns a `content` array with server tool calls, tool results, and answer text. Read all blocks; `content[0]` may be a tool call rather than text. HTTP 200 does not imply every tool operation succeeded.

A `server_tool_use` block identifies the search. Its paired `web_search_tool_result` contains source entries with URLs and titles, or a `web_search_tool_result_error`. Text citations use `type: "web_search_result_location"` with a source URL, title, and cited text. Preserve opaque fields when you reuse the returned history.

## Control the search

| Tool option | Behavior |
| - | - |
| `max_uses` | Positive integer, default 3; values above 16 are reduced to 16. Limits search operations for the request. |
| `allowed_domains` | Restrict sources to these domains and their subdomains. |
| `blocked_domains` | Exclude these domains and their subdomains. Use this instead of an allowlist. |

Each domain list supports up to 100 entries. Use hostnames without a scheme or port, such as `docs.python.org`. Search also accepts a path restriction such as `docs.python.org/3/tutorial/`; path patterns may use `*`. Do not combine nonempty `allowed_domains` and `blocked_domains` lists.

Search citations are enabled by default. Use the returned source information to make links available to your reader. The `citations` and `max_content_tokens` controls described for fetch are not search controls.

You can declare `web_fetch` alongside search when the model should read a discovered page. A search result can supply an eligible fetch URL. Search and fetch each have their own `max_uses` budget; a successful search does not mean a page has also been fetched.

## Continue a conversation

For Chat, include the earlier user and assistant turns with your next question. The returned annotations are source metadata, not a reusable server-tool transcript. To fetch a previously cited URL on a new Chat request, include that URL explicitly in user text.

For Messages, preserve the full returned assistant `content`, including server calls, results, citations, and opaque fields, before appending the next user turn. Keep the relevant tool declarations on the follow-up. The returned server-tool history is account-bound and has a seven-day replay lifetime. Do not fabricate or edit source blocks, and do not reuse another account's history. Expired or invalid history is rejected; start a fresh retrieval when needed.

## Streaming and output limits

Set `stream: true` to receive protocol-native events. This server-side workflow runs before emitting the result: the connection can send SSE comment keepalives while model and tool work completes, then emits the answer and source events. It does not provide live token-by-token or per-operation progress during that work.

Chat citations arrive in `choices[].delta.annotations`. To receive final usage, add `stream_options: {"include_usage":true}` and handle the usage-only chunk with `choices: []` before `[DONE]`. Messages sends server-tool/result blocks, text and citation deltas, and final usage in `message_delta`, followed by `message_stop`. Check in-stream errors as well as the initial HTTP status. [Streaming](https://docs.overcontrolgroup.com/text/streaming.md) provides complete SDK stream readers.

The supported models have a 400,000-token context window and up to 32,768 output tokens. Both APIs reduce the output budget to the model maximum and remaining context space. Messages requires a positive `max_tokens`; Chat defaults to the model maximum when no budget is supplied. The examples set 512 output tokens and one tool use. Tool output and intermediate model work also need context; high `max_uses` does not guarantee every permitted operation will run.

## Usage and pricing

The request consumes model tokens plus successful tool operations. Retrieved content and intermediate model turns contribute to token usage. Failed operations do not incur the successful-operation charge, but model tokens already used can still consume allowance. Declaring a tool does not itself incur an operation charge.

Read `usage.server_tool_use.web_search_requests` for successful operations and the normal token fields for model usage. Streaming clients must read final usage. [Pricing](https://docs.overcontrolgroup.com/pricing.md#server-side-tool-pricing) lists rates per 1,000 successful operations and a combined-cost example.

Before execution, the request reserves estimated usage; unused reserved usage is released afterward. Set an explicit output budget and a small `max_uses` to bound work. A request can require more available usage to start than its final cost. `max_uses` is an operation ceiling, not a complete monetary ceiling.

## Common failures

| Failure | What to do |
| - | - |
| HTTP 400 for the declaration | Check the model, tool type, positive limits, and domain-filter format. |
| HTTP 401 or 403 | Check the key, send one authentication header, and enable the inference endpoint's scope. |
| HTTP 404 on Messages | Check model ID and the key's model allowlist. |
| HTTP 429 with `Insufficient credits` | Review available usage or reduce the output/tool budget. |
| HTTP 502, `server_tool_workflow_failed` on Chat | The workflow could not complete; retry only when appropriate for your application. Messages uses its native error body. |
| Messages `max_uses_exceeded` | The tool reached its operation limit. Increase it only if the task needs more work. |
| Messages `invalid_tool_input` | The tool invocation was invalid. Adjust the request or try again. |
| Messages `too_many_requests` or `unavailable` | The web operation could not complete; it may be worth retrying later. |

Messages tool errors appear inside the tool-result `content` object as an `error_code`, even on HTTP 200. Chat exposes the final answer, annotations, and usage rather than those native error blocks. On Chat, inspect the answer and counters instead of looking for a Messages result object. Keep `X-Request-Id` for support; Messages also returns `request-id`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.