---
title: AI hosted tools
description: web_search, web_fetch, tool_search and datetime — tools the platform executes for a model, and what you observe.
type: reference
product: ai
summary: The tools the platform runs on the model's behalf
updated: 2026-09-30
order: 4
---

A hosted tool is one the platform executes for the model, declared in `tools`
next to your own function tools. Five rules describe every one of them, and
they exist so that a request cannot be quietly rewritten behind your back.

- **Listing is not execution.** Declaring `web_search` advertises it. With the
  default `tool_choice: auto` the model chooses whether to call it; the
  platform never searches before a model call.
- **The model authors the arguments.** Even a forced `tool_choice` only
  constrains the model's next call. The query or URL is always model-authored,
  never a copy of your raw message.
- **One loop, one terminal.** Tool activity is an item on the way to the
  assistant answer. The stream continues after each tool round and ends once.
- **Zero results is a valid outcome.** An empty search is reported as an empty
  search, not an error and not a reason to silently try another engine.
- **Failures stay typed.** A tool that cannot run under your policy fails
  before it is dispatched, instead of running with fewer constraints.

## web_search

The model searches the web when it judges the question needs current
information. You observe each call as a `web_search_call` item with its status
and results.

```json
{
  "model": "openai/gpt-5.5",
  "input": "Which of these providers shipped a new reasoning model this week?",
  "tools": [{ "type": "web_search" }]
}
```

| Field | Type | What it does |
| --- | --- | --- |
| `type` (required) | `"web_search"` | `web_search_preview` is accepted as the same operation. |
| `engine` | string | Which engine executes the search. Omit for `auto`. |
| `filters.allowed_domains` | string[] | Domains that may be used as sources. Up to 100. |
| `filters.blocked_domains` | string[] | Domains that must not be used. A blocked domain wins over an allowed match, subdomains included. Up to 100. |
| `external_web_access` | boolean | `false` requires cache- or index-only retrieval; engines that would fetch the live web are not used. |
| `user_location` | object | `{"type":"approximate","country":"HK"}`. Only the country is honoured; city, region and timezone are refused before any search runs. |
| `search_context_size` | `"low"`, `"medium"`, `"high"` | How much retrieved context to give the model. |

`search_content_types` (`["text"]`) and `return_token_budget` (`"default"` or
`"unlimited"`) are also accepted on the declaration.

### Engines

| `engine` | Search | Fetch | Notes |
| --- | --- | --- | --- |
| `auto` | Yes | Yes | The default. Eligible native execution first, then managed engines that can honour your policy. |
| `native` | Yes | Yes | Strict: only provider-native execution on the model actually serving the request. |
| `parallel` | Yes | Yes | Managed search and page retrieval. |
| `exa` | Yes | Yes | Managed search and page retrieval, including cache-only retrieval. |
| `firecrawl` | Yes | Yes | Managed search and page retrieval, including cache-only retrieval. |
| `perplexity` | Yes | No | Search only. Domain filters are limited to 20 domains. |
| `brave` | Yes | No | Search only. |
| `tavily` | Yes | Yes | Managed search and page retrieval. |
| `anysearch` | Yes | Yes | Managed search and page retrieval. |

A named engine is strict: if it cannot execute the request under your policy,
the request fails instead of falling back. `auto` falls back only among engines
that can honour the policy you set.

<Callout tone="warning" title="Filters are a source policy">
	Domain filters constrain the sources admitted to the model's context — the
	search results and the pages opened afterwards. They are not a network
	guarantee about every host involved in retrieving a page.
</Callout>

## web_fetch

`web_fetch` opens one URL the model chose and returns the page content to the
model.

```json
{
  "model": "openai/gpt-5.5",
  "input": "Read https://example.com/pricing and list the plans.",
  "tools": [{ "type": "web_fetch", "engine": "firecrawl" }]
}
```

- It accepts only `type` and `engine`. Source filters are inherited from the
  `web_search` declaration in the same request.
- Pages are bounded to 1 MiB per returned page and extracted as clean markdown
  before they reach the model.
- You observe a `web_search_call` whose action is
  `{"type":"open_page","url":…}`. There is no separate `web_fetch_call` item.
- A fetch engine that cannot satisfy a requested constraint, such as cache-only
  retrieval, is not used for that call.

## tool_search

`tool_search` lets the model load tool definitions it was not given up front.
Either the platform executes the discovery over the tools you declared, or your
client does, and the choice is explicit.

```json
{ "type": "tool_search" }
```

```json
{ "type": "tool_search", "execution": "client" }
```

- Platform execution produces a `tool_search_call` and a separate
  `tool_search_output` carrying the loaded definitions, then continues with
  those tools available. Client execution emits only the call; you return the
  output with the same `call_id`.
- `defer_loading: true` on a function does not by itself turn on platform
  discovery; list `tool_search` as well.
- A function literally named `tool_search` is your function, not the hosted
  operation. A request cannot list both.

## datetime

`datetime` gives the model the current date and time with no external provider
call, useful for relative dates such as "next Tuesday". The optional
`timezone` is an IANA name and defaults to `UTC`.

```json
{ "type": "datetime", "timezone": "Asia/Hong_Kong" }
```

## What you observe

Tool activity appears on the same response and stream as everything else. On a
streaming request each call reports progress before the answer continues:

```text
event: response.web_search_call.in_progress
event: response.web_search_call.searching
event: response.web_search_call.completed
…then the assistant continuation, in the same request:
event: response.output_text.delta
event: response.completed
```

Non-streaming responses carry the same items inside `output`, in order. A
completed search may add URL citations to the assistant text; request the
sources it used with `include`. Tool results are evidence for the model: they
are never rewritten into instructions and never end the turn on their own.

<RelatedDocs
	links={[
		{
			href: '/docs/ai/responses',
			label: 'Responses API',
			description: 'The endpoints, fields and streaming events these tools ride on.',
		},
		{
			href: '/docs/ai/errors',
			label: 'Errors and retries',
			description: 'The typed failures a tool can return.',
		},
	]}
/>
