Menu
Platform
AI
App store purchases
Database
Flags
Jobs and cron
Localization
Monitoring
Notifications
Payments
Queues
Sandboxes
Webhooks
Getting Started
Authentication
KV Store
Deploy & Infrastructure
Reference
AI hosted tools
The tools the platform runs on the model's behalf
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_searchadvertises it. With the defaulttool_choice: autothe model chooses whether to call it; the platform never searches before a model call. - The model authors the arguments. Even a forced
tool_choiceonly 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.
{
"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.
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.
#web_fetch
web_fetch opens one URL the model chose and returns the page content to the
model.
{
"model": "openai/gpt-5.5",
"input": "Read https://example.com/pricing and list the plans.",
"tools": [{ "type": "web_fetch", "engine": "firecrawl" }]
}- It accepts only
typeandengine. Source filters are inherited from theweb_searchdeclaration 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_callwhose action is{"type":"open_page","url":…}. There is no separateweb_fetch_callitem. - 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.
{ "type": "tool_search" }{ "type": "tool_search", "execution": "client" }- Platform execution produces a
tool_search_calland a separatetool_search_outputcarrying the loaded definitions, then continues with those tools available. Client execution emits only the call; you return the output with the samecall_id. defer_loading: trueon a function does not by itself turn on platform discovery; listtool_searchas well.- A function literally named
tool_searchis 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.
{ "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:
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.completedNon-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.