# Hosted tools

> Use web search, fetching, code execution and other tools that run on the provider's side, with per-tool configuration and per-call billing.

Source: https://0.0.0.0:3000/docs/features/hosted-tools
Documentation index: https://0.0.0.0:3000/docs/llms.txt

Hosted tools run at the provider instead of in your code. You declare them in `tools` next to any function tools; the provider runs them while generating, and you receive the outcome: the final text, citations and inline files. Queries, generated code, call traces, resource identifiers and credentials are never returned.

Hosted tools are a fixed, versioned set. A request routes only to endpoints that support every tool it asks for, and each tool call is billed at the endpoint's published per-call rate.

:::note Check availability first
An endpoint offers a hosted tool only when it lists it in `supported_hosted_tools`. Few endpoints may offer a given tool, so check [discovery](#discover-support) before you rely on one.
:::

## Request a hosted tool

Add the tool to `tools`. The simplest form names a common tool type:

```request
{
  "method": "POST",
  "path": "/chat/completions",
  "body": {
    "model": "openai/gpt-6-luna",
    "messages": [
      { "role": "user", "content": "What changed in the latest stable release of SQLite? Cite your sources." }
    ],
    "tools": [{ "type": "web_search" }],
    "max_tool_calls": 3
  }
}
```

There are two forms:

| Form | Example | Routes to |
| --- | --- | --- |
| Common type | `{"type": "web_search"}` | Any endpoint that supports one of the tool IDs behind that type |
| Namespaced ID | `{"type": "hosted", "id": "openrouter.web_search@1"}` | Only endpoints that support that exact tool ID |

Use a common type when any implementation will do. Use a namespaced ID when you need a specific implementation or its configuration keys: common types accept a smaller set of configuration keys, and several accept none.

:::fields Hosted tool
- `type` `string` required — A common tool type from the table below, or `hosted` for the namespaced form.
- `id` `string` — Required when `type` is `hosted`: one of the [namespaced IDs](#namespaced-ids). Not allowed on common types.
- `configuration` `object` — Tool-specific settings. Only the keys listed for that tool are accepted. At most 16 properties and 32 KiB. See [Configuration reference](#configuration-reference).
- `resource_aliases` `string[]` — Up to 32 unique names of provider-configured resources. See [Resource aliases](#resource-aliases).
:::

Each tool may appear once per request. Hosted tools cannot take `function`, `parameters` or `strict`.

## Common tool types

A common type resolves to whichever of its namespaced IDs the selected endpoint supports.

| Common type | Resolves to | Configuration keys |
| --- | --- | --- |
| `web_search` | `openrouter.web_search@1`, `openai.web_search@1`, `anthropic.web_search@1`, `google.search@1` | None |
| `web_fetch` | `openrouter.web_fetch@1`, `anthropic.web_fetch@1` | None |
| `datetime` | `openrouter.datetime@1` | `timezone` |
| `image_generation` | `openrouter.image_generation@1`, `openai.image_generation@1` | [Image generation keys](#image-generation) |
| `code_execution` | `openai.code_interpreter@1`, `anthropic.code_execution@1`, `google.code_execution@1` | None |
| `file_search` | `openai.file_search@1`, `google.file_search@1` | None |
| `shell` | `openai.shell@1` | None |
| `mcp` | `openai.mcp@1`, `anthropic.mcp@1` | None |
| `tool_search` | `openai.tool_search@1`, `anthropic.tool_search@1` | None |
| `skills` | `openai.skills@1` | None |
| `maps` | `google.maps@1` | None |
| `url_context` | `google.url_context@1` | None |
| `advisor` | `openrouter.advisor@1`, `anthropic.advisor@1` | `model`, `max_uses`, `caching` |
| `apply_patch` | `openrouter.apply_patch@1` | None |
| `fusion` | `openrouter.fusion@1` | `models` |
| `subagent` | `openrouter.subagent@1` | `model`, `max_turns` |

"None" means the tool accepts an omitted, `null` or empty `configuration` only. To configure web search, for example, use a namespaced ID such as `openrouter.web_search@1`.

## Namespaced IDs

The namespaced form is `{"type": "hosted", "id": "<id>"}`. New tools arrive only with a new versioned ID.

| ID | Configuration keys |
| --- | --- |
| `openrouter.web_search@1` | `max_results`, `search_prompt`, `engine` |
| `openrouter.web_fetch@1` | `max_uses`, `max_content_tokens`, `allowed_domains`, `blocked_domains`, `citations` |
| `openrouter.datetime@1` | `timezone` |
| `openrouter.image_generation@1` | [Image generation keys](#image-generation) |
| `openrouter.apply_patch@1` | None |
| `openrouter.fusion@1` | `models` |
| `openrouter.advisor@1` | `model`, `max_uses`, `caching` |
| `openrouter.subagent@1` | `model`, `max_turns` |
| `openai.web_search@1` | `external_web_access`, `filters`, `search_context_size`, `user_location` |
| `openai.file_search@1` | `max_num_results`, `top_k`, `metadata_filter` |
| `openai.code_interpreter@1` | None |
| `openai.image_generation@1` | [Image generation keys](#image-generation) |
| `openai.shell@1` | None |
| `openai.mcp@1` | `server_label`, `server_description`, `allowed_tools` |
| `openai.connectors@1` | `server_label`, `server_description`, `allowed_tools` |
| `openai.tool_search@1` | `execution`, `description` |
| `openai.skills@1` | None |
| `anthropic.web_search@1` | `max_uses`, `allowed_domains`, `blocked_domains`, `user_location` |
| `anthropic.web_fetch@1` | `max_uses`, `max_content_tokens`, `allowed_domains`, `blocked_domains`, `citations` |
| `anthropic.code_execution@1` | None |
| `anthropic.advisor@1` | `model`, `max_uses`, `caching` |
| `anthropic.tool_search@1` | None |
| `anthropic.mcp@1` | `server_label`, `server_description`, `allowed_tools` |
| `google.search@1` | `search_types`, `time_range_filter` |
| `google.maps@1` | None |
| `google.url_context@1` | None |
| `google.file_search@1` | `max_num_results`, `top_k`, `metadata_filter` |
| `google.code_execution@1` | None |

`openai.connectors@1` has no common type; request it by ID. An accepted ID doesn't mean any endpoint offers the tool: check [discovery](#discover-support) first.

## Configure a tool

This request restricts a fetch tool to one domain and asks for citations:

```request
{
  "method": "POST",
  "path": "/chat/completions",
  "body": {
    "model": "openai/gpt-6-luna",
    "messages": [
      { "role": "user", "content": "Summarize the changelog at https://docs.example.com/changes" }
    ],
    "tools": [
      {
        "type": "hosted",
        "id": "anthropic.web_fetch@1",
        "configuration": {
          "max_uses": 2,
          "allowed_domains": ["docs.example.com"],
          "citations": { "enabled": true }
        }
      }
    ]
  }
}
```

A key the tool doesn't accept, a value outside its range or an unknown key inside a nested object fails with `400 invalid_request`; configuration is never silently dropped. Configuration never carries credentials or upstream resource identifiers; use [resource aliases](#resource-aliases) for resources.

## Configuration reference

Each key is accepted only by the tools listed for it in the tables above.

### Search and fetch

:::fields
- `max_results` `integer` — 1–20. Results per search (`openrouter.web_search@1`).
- `search_prompt` `string` — 1–4096 bytes. Guidance for the search step (`openrouter.web_search@1`).
- `engine` `string` — `auto`, `native` or `exa` (`openrouter.web_search@1`).
- `max_uses` `integer` — 1–20. Maximum uses of the tool in one response.
- `max_content_tokens` `integer` — 1–1,000,000. Limit on fetched content.
- `allowed_domains` `string[]` — Up to 100 unique domains, each 1–253 bytes.
- `blocked_domains` `string[]` — Up to 100 unique domains, each 1–253 bytes.
- `citations` `object` — `{"enabled": boolean}`.
- `external_web_access` `boolean` — Allow live web access (`openai.web_search@1`).
- `filters` `object` — `{"allowed_domains": string[]}` with the same limits as above (`openai.web_search@1`).
- `search_context_size` `string` — `low`, `medium` or `high` (`openai.web_search@1`).
- `user_location` `object` — Approximate location to localize results.
  - `type` `string` — `approximate`.
  - `country` `string` — Two-letter country code.
  - `city` `string` — 1–128 bytes.
  - `region` `string` — 1–128 bytes.
  - `timezone` `string` — 1–128 bytes.
- `search_types` `object` — `{"web_search": {}, "image_search": {}}`; include the types to enable (`google.search@1`).
- `time_range_filter` `object` — `start_time` and `end_time` strings, 1–128 bytes each (`google.search@1`).
:::

### Files and connected services

:::fields
- `max_num_results` `integer` — 1–100.
- `top_k` `integer` — 1–100.
- `metadata_filter` `string` — 1–4096 bytes.
- `server_label` `string` — 1–128 bytes.
- `server_description` `string` — 1–4096 bytes.
- `allowed_tools` `string[]` — Up to 256 unique names, each 1–128 bytes.
- `execution` `string` — `server` (`openai.tool_search@1`).
- `description` `string` — 1–4096 bytes (`openai.tool_search@1`).
:::

### Models and agents

:::fields
- `model` `string` — 1–256 bytes. The model the tool uses (`advisor`, `subagent` and their IDs).
- `max_uses` `integer` — 1–20 (`advisor` and its IDs).
- `caching` `object` — `type` `ephemeral` and `ttl` `5m` or `1h` (`advisor` and its IDs).
- `max_turns` `integer` — 1–64 (`subagent`, `openrouter.subagent@1`).
- `models` `string[]` — Up to 16 unique model names, each 1–256 bytes (`fusion`, `openrouter.fusion@1`).
- `timezone` `string` — 1–128 bytes (`datetime`, `openrouter.datetime@1`).
:::

### Image generation

Accepted by `image_generation`, `openrouter.image_generation@1` and `openai.image_generation@1`.

:::fields
- `background` `string` — `auto`, `opaque` or `transparent`.
- `input_fidelity` `string` — `low` or `high`.
- `model` `string` — 1–256 bytes.
- `moderation` `string` — `auto`.
- `output_compression` `integer` — 0–100.
- `output_format` `string` — `png`, `jpeg` or `webp`.
- `partial_images` `integer` — 0–3.
- `quality` `string` — `auto`, `low`, `medium` or `high`.
- `size` `string` — `auto`, `1024x1024`, `1024x1536` or `1536x1024`.
:::

## Resource aliases

Some tools work on resources that belong to the provider: file and vector stores for `file_search`, MCP servers for `mcp`, and connectors for `openai.connectors@1`. You refer to such a resource by an **alias**, a short name the provider gave you for it:

```json
{
  "type": "hosted",
  "id": "openai.file_search@1",
  "configuration": { "max_num_results": 8 },
  "resource_aliases": ["product-manuals"]
}
```

- Each alias matches `^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$`; up to 32 unique aliases per tool.
- The resource ID and any credential stay with the provider and never appear in your request, the response or usage.
- Aliases belong to one provider. Use them with an endpoint whose provider gave you the alias, and pin that endpoint with `provider.only`.

## Discover support

Models and endpoints list the namespaced IDs they support in `supported_hosted_tools`; the field is omitted when there are none. A model row describes one representative offer, so check the endpoints of the model for the full picture:

```request
{
  "method": "GET",
  "path": "/models/openai/gpt-6-luna/endpoints"
}
```

```json title="Endpoint excerpt"
{
  "name": "Example Provider: primary",
  "tag": "example-provider/primary",
  "supported_parameters": ["max_tokens", "reasoning", "tools", "tool_choice"],
  "supported_hosted_tools": ["openrouter.web_search@1", "openrouter.web_fetch@1", "openrouter.datetime@1"]
}
```

An endpoint that supports `openrouter.web_search@1` also serves requests for the common `web_search` type. The [catalogue](/models) can filter models by hosted tool and shows each endpoint's per-call rates.

## Routing requirements

A request with hosted tools routes only to endpoints that support tool calling and **every** requested hosted tool; if none qualifies, it fails with `503 provider_unavailable`. A tool is never dropped to find an endpoint.

- **`tool_choice: "none"`** removes all tools from the request, hosted ones included, and removes the requirement.
- **A named `tool_choice`** sends only that function tool, so hosted tools are unavailable in that request.
- **`tool_choice: "required"`** is satisfied only by a function call returned to you. Hosted tool executions are not returned as calls and do not count.
- **Function tools** can be combined with hosted tools. Only function calls come back in `tool_calls`; see [Tool calling](/docs/features/tool-calling).

## Results

Hosted tool output arrives in the assistant message:

```json title="Response excerpt"
{
  "choices": [
    {
      "index": 0,
      "finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "The latest release adds new JSON functions and improves query planning [1].",
        "annotations": [
          {
            "type": "url_citation",
            "url": "https://docs.example.com/changes",
            "title": "Release History",
            "start_index": 71,
            "end_index": 74
          }
        ]
      }
    }
  ],
  "usage": {
    "prompt_tokens": 1240,
    "completion_tokens": 96,
    "total_tokens": 1336,
    "hosted_tool_usage": { "openrouter.web_search@1": 1 },
    "cost": 0.004375,
    "cost_details": { "upstream_inference_cost": 0.004375 }
  }
}
```

:::fields Message fields
- `content` `string | null` — The answer, informed by the tool results.
- `annotations` `object[]` — Citations. Up to 256 per response.
  - `type` `string` — `url_citation` or `file_citation`.
  - `url` `string` — Cited URL, for `url_citation`.
  - `title` `string` — Title of the cited source.
  - `filename` `string` — Cited file, for `file_citation`.
  - `start_index` `integer` — Start of the cited span in `content`.
  - `end_index` `integer` — End of the cited span in `content`.
- `artifacts` `object[]` — Files produced by a tool, such as a generated image or a file from code execution. At most `max_tool_calls` per response.
  - `type` `string` — `image`, `audio` or `file`, derived from the media type.
  - `filename` `string` — File name.
  - `media_type` `string` — MIME type.
  - `data` `string` — Base64 content.
:::

In a stream, annotations and artifacts arrive as `delta.annotations` and `delta.artifacts`. See [Streaming](/docs/features/streaming). Hosted tool executions never appear in `tool_calls`.

## Usage and billing

Each hosted tool is billed per call, as a SKU named by its namespaced ID. The reported quantity appears in `usage.hosted_tool_usage` (only when non-zero) and on [`GET /generation`](/docs/api-reference/chat/get-generation):

```json
"hosted_tool_usage": { "openrouter.web_search@1": 2, "openrouter.web_fetch@1": 1 }
```

- The charge is the quantity times the endpoint's per-call rate for that SKU, plus the platform fee, in your key's billing currency. An endpoint serves a hosted tool only if it publishes a per-call rate for it.
- Hosted-tool charges are based on the usage the platform accepts from the provider's response. Quantities are whole calls, bounded by `max_tool_calls`.
- While the request runs, funds are held for up to `max_tool_calls` calls of each requested tool (128 when you omit it). You're charged only for actual usage, but a lower `max_tool_calls` reduces the hold and the chance of a `402`. See [Credits](/docs/account/credits).

## Errors

| Status | `error_type` | Cause |
| --- | --- | --- |
| `400` | `invalid_request` | Unknown type or ID, `id` on a common type, duplicate tool, disallowed or out-of-range configuration key, invalid or duplicate resource alias. |
| `402` | `insufficient_credits` | The balance or key budget can't cover the hold, including `max_tool_calls` hosted calls. |
| `503` | `provider_unavailable` | No eligible endpoint supports tool calling and every requested hosted tool. |
| `502` | `provider_unavailable` | The endpoint couldn't run the request, for example because it doesn't have the resource alias. |

## Best practices

- **Start with a common type** and switch to a namespaced ID only when you need its configuration or a specific implementation.
- **Set `max_tool_calls`** to the number of calls a request needs. It bounds cost and the hold on your balance.
- **Ask for citations in the prompt** and render `annotations` next to the answer so readers can verify sources.
- **Check discovery before relying on a tool.** An accepted ID doesn't mean any endpoint serves it.
- **Pin endpoints when you use resource aliases**, since aliases exist only on the provider that configured them.

## Next steps

:::cards
- {wrench} [Tool calling](/docs/features/tool-calling) Functions that your own code executes.
- {waves} [Streaming](/docs/features/streaming) Receive annotations and artifacts as they arrive.
- {receipt} [Usage accounting](/docs/account/usage-accounting) How SKU usage and fees appear in cost.
- {network} [Provider routing](/docs/routing/provider-routing) Pin endpoints with `only` and `order`.
- {boxes} [Models](/docs/models) Read capabilities and hosted tools from the catalogue.
:::
