I am using this session.update via WebRTC with the realtime speech api:
let sessionUpdate: [String: Any] = [
"type": "session.update",
"session": [
"instructions": sessionInstructions,
"voice": sessionVoice,
"tool_choice": "auto",
"tools": [
[
"type": "web_search"
],
[ ... other tools
but in my testing, when I ask it for recent information, it says it will look it up, but then seems to hang indefinitely.
Is this expected to work? If not, any recommended alternatives in the meantime?
I just got confirmation from Justin Umberti in this x/twitter post that it doesn’t work yet.
Workaround: you can define a tool like this:
{
"type" : "function",
"name" : "GPT-web-search",
"parameters" : {
"type" : "object",
"properties" : {
"query" : {
"type" : "string",
"description" : "The user’s question or topic to research across the live web."
}
},
"required" : [
"query"
]
},
"description" : "Comprehensive and fast web search"
}
and then in the tool call, make a call to gpt-5 or gpt-4o, with a payload like this:
const payload = {
model: 'gpt-4o',
input: [
{
role: 'system',
content: [{ type: 'input_text', text: 'You are a focused research assistant. Use live web search to answer succinctly with citations when available.' }],
},
{
role: 'user',
content: [{ type: 'input_text', text: query }],
},
],
tools: [{ type: 'web_search' }],
tool_choice: 'auto' as const,
};
As long as the result of the search is returned to the realtime api, then it should proceed as if it was doing a built-in web search.
I hit the same issue and ended up using the function-tool workaround too.
I suspect this is more than a missing tool flag. Web search in Realtime has a harder lifecycle than web search in Responses.
In Responses, the flow is simple: request → search → answer.
In Realtime voice, the user can interrupt while the search is running. Then the app has to decide whether to cancel the lookup, ignore a late result, speak an acknowledgement, avoid stale answers, and keep the hidden search call out of the persistent conversation thread.
Those are client/product decisions.
So the workaround may actually be the right architecture:
- expose a normal Realtime function tool
- have the client call Responses API with web search
- return the result as
function_call_output
- send
response.create
- guard against stale results if the user has already moved on
Built-in Realtime web search would be nice, but for interruptible voice apps I can see why OpenAI might leave this to client logic rather than make one default behavior fit every product.