> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.mixlayer.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.mixlayer.com/_mcp/server.

# Server-Side Tools

> Use Mixlayer-hosted tools, including Exa-powered web search

Server-side tools run inside Mixlayer. Add one to a request and the model can call it as needed; Mixlayer executes the call and returns the result to the model automatically. Your application does not need to implement the tool-call loop.

## Web search

The `web_search` tool searches the public web using [Exa](https://exa.ai). It is available over the OpenAI compatible [Responses](/api-reference/inference-ap-is/responses/responses) and [Chat Completions](/api-reference/inference-ap-is/chat-completions/chat-completions) endpoints.

#### Responses

```json
{
  "model": "qwen/qwen3.5-4b-free",
  "input": "What changed in Rust this week? Include sources.",
  "tools": [
    {
      "type": "web_search",
      "category": "news",
      "max_results": 5
    }
  ],
  "include": ["web_search_call.action.sources"]
}
```

#### Chat Completions

```json
{
  "model": "qwen/qwen3.5-4b-free",
  "messages": [
    {"role": "user", "content": "What changed in Rust this week? Include sources."}
  ],
  "web_search_options": {
    "category": "news",
    "max_results": 5
  }
}
```

The model decides whether to search and may search more than once. You are charged for each search it performs, not merely for enabling the tool.

### Options

| Option                    | Type      | Default | Description                                                                                                                     |
| ------------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `category`                | string    | —       | Restricts results to `company`, `people`, `research paper`, `news`, `personal site`, or `financial report`.                     |
| `filters.allowed_domains` | string\[] | `[]`    | Searches only these domains. Cannot be combined with `blocked_domains`.                                                         |
| `filters.blocked_domains` | string\[] | `[]`    | Excludes these domains. Cannot be combined with `allowed_domains` and is not supported with the `company` or `people` category. |
| `user_location`           | object    | —       | Approximate user location used to localize the search. Its shape differs by API; see [Location](#location).                     |
| `max_results`             | integer   | `10`    | Maximum results returned by each search. Must be from 1 through 100.                                                            |
| `max_characters`          | integer   | `10000` | Maximum text characters retrieved for each result. Must be greater than zero.                                                   |

Each domain list accepts at most 100 unique bare domain names such as `example.com`. Do not include a scheme, path, port, query, fragment, or wildcard. Domains are normalized to lowercase and a trailing dot is removed.

### Location

Use an approximate location with any combination of `country`, `city`, `region`, and `timezone`. `country` must be a two-letter country code. `timezone` must be an IANA-style name such as `America/Los_Angeles`.

| Field      | Type            | Required | Description                                       |
| ---------- | --------------- | -------- | ------------------------------------------------- |
| `type`     | `"approximate"` | Yes      | Location type. No other value is supported.       |
| `country`  | string          | No       | Two-letter country code, normalized to uppercase. |
| `city`     | string          | No       | Non-empty city name.                              |
| `region`   | string          | No       | Non-empty region or state name.                   |
| `timezone` | string          | No       | IANA-style timezone containing no whitespace.     |

Responses uses a flat location object:

```json
{
  "type": "web_search",
  "user_location": {
    "type": "approximate",
    "country": "US",
    "city": "San Francisco",
    "region": "California",
    "timezone": "America/Los_Angeles"
  }
}
```

Chat Completions nests the location fields under `approximate`:

```json
{
  "web_search_options": {
    "user_location": {
      "type": "approximate",
      "approximate": {
        "country": "US",
        "city": "San Francisco",
        "region": "California",
        "timezone": "America/Los_Angeles"
      }
    }
  }
}
```

### Results and sources

For Responses, each completed search adds a `web_search_call` output item:

```json
{
  "type": "web_search_call",
  "id": "ws_...",
  "status": "completed",
  "action": {
    "type": "search",
    "query": "Rust changes this week",
    "sources": [
      {"type": "url", "url": "https://example.com/article", "title": "Article title"}
    ]
  }
}
```

Pass `include: ["web_search_call.action.sources"]` to include `action.sources`; otherwise the source list is omitted. The search call item itself is always returned when a search runs.

For Chat Completions, sources are returned in `choices[0].message.sources`. Streaming responses send them in a `choices[0].delta.sources` chunk. Each source has `url` and `title` fields.

Both APIs report the number of searches in `usage.server_tool_use.web_search_requests`.

### Responses streaming and WebSocket

Responses streaming emits these events for each search:

| Event                                  | Meaning                                            |
| -------------------------------------- | -------------------------------------------------- |
| `response.output_item.added`           | An in-progress `web_search_call` item was added.   |
| `response.web_search_call.in_progress` | The search call was initiated.                     |
| `response.web_search_call.searching`   | The search is being executed.                      |
| `response.web_search_call.completed`   | The search completed.                              |
| `response.output_item.done`            | The completed `web_search_call` item is available. |

The same request options, output items, and lifecycle events work over the [Responses WebSocket transport](/api-reference/inference-ap-is/responses/responses-websocket). Put the web search tool in each `response.create` message where it should be available.

### Pricing

| Usage                                      | Price                         |
| ------------------------------------------ | ----------------------------- |
| Search request, including up to 10 results | \$0.007 per search            |
| Results above the first 10                 | \$0.001 per additional result |

Model input and output tokens are billed separately at the selected model's rates. For example, one search returning 25 results incurs one search-request unit and 15 additional-result units, plus model token usage.

## Combining tools

Web search can be used alongside your own function tools. `web_search` is reserved for the built-in tool, so custom functions cannot use that name. A request may include the built-in tool only once.