Using the Web Search Tool with the Responses API

Ma Mahalakshmi V Updated 16 Sep 2026
7 min read ·Lesson 66 of 224

How the Web Search Tool Fits Into the Responses API

The Responses API treats web search as one of its built-in tools, alongside things like file search and code execution. Unit 9, Lesson 1 showed the minimum needed to turn it on. This lesson treats it as a first-class mechanism you need to understand structurally: what actually happens on the server when you enable it, what the returned response object looks like, and how to read the pieces of it your application will actually depend on.

Conceptually, enabling web search does not change how you call client.responses.create(). You still send a model name and an input. What changes is that you also pass a tools list containing a web search tool definition. When the model decides a request would benefit from current information, it invokes the tool itself — you do not manually trigger a search — the server performs the search, feeds the results back into the model's context, and the model produces a final answer informed by what it found. This entire loop happens inside a single call to responses.create(); you do not need to handle intermediate steps yourself, unlike some other tool-calling patterns where you must execute a function locally and send its result back.

This "the model decides" behavior is important to internalize. Adding the web search tool does not force a search on every request. If the model judges that a question does not require current information (for example, "explain what a for loop does"), it may answer directly from its own knowledge without searching at all. This is generally the right default, since it avoids unnecessary latency and cost, but it also means you cannot always assume a search happened just because you enabled the tool. Later lessons in this unit cover how to inspect the response to confirm whether a search actually occurred.

Minimal Example

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-5.6-terra",
    tools=[{"type": "web_search"}],
    input="What are the current LTS versions of Node.js, and when does the current one reach end of life?",
)

print(response.output_text)

The key addition compared to a plain call is the tools parameter, a list containing one dictionary with "type": "web_search". This tells the Responses API that the web search tool is available for the model to use during this request. Nothing else about the call changes — model and input behave exactly as they do without the tool.

Note: The exact tool type string ("web_search") and any additional configuration keys accepted alongside it are specific to the API version you are targeting. Confirm the current tool name and accepted parameters against the official OpenAI API reference before relying on this in production, since built-in tool identifiers are among the details most likely to be revised between API versions.

Running this, response.output_text gives you the final, synthesized answer — a plain string, exactly as it would be without any tool. This is deliberate: output_text is a convenience property that flattens the final assistant message regardless of how many intermediate steps (like a web search) the model took to produce it. If all you need is the answer text, you never have to touch anything else on the response object.

Inspecting What Happened Under the Hood

For anything beyond a toy example, you will want to know more than just the final text — specifically, whether a search actually happened and what was found. The Responses API exposes this through the output list on the response object, which contains an ordered sequence of "items" representing each step the model took.

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-5.6-terra",
    tools=[{"type": "web_search"}],
    input="What is the current record for the fastest marathon time, and who holds it?",
)

for item in response.output:
    print("item type:", item.type)

print("---")
print(response.output_text)

Iterating over response.output lets you see every distinct item type the model produced during this call. When a web search is performed, you will typically see an item representing the search call itself (often something like web_search_call) followed by the model's final message item (typically message). If the model answered without searching, you will only see the message item.

This distinction matters for debugging and for cost tracking: if you expect search-dependent behavior but never see a search item appear, that is a signal the model judged the query as not needing current information — which may be correct, or may indicate your prompt needs to be more explicit about wanting current data. You will use this same inspection pattern in Lesson 4 when extracting citation details from a search-backed response, so it is worth getting comfortable with the shape of response.output now.

Note: The exact item type string(s) used to represent a search step, and the fields available on that item, can change between API versions. Print the raw output structure for your SDK version and confirm the field names against current documentation before building production logic that depends on them.

By default, giving the model the web search tool makes it available, not mandatory. In most cases, letting the model decide is the correct behavior — it is the entire point of tool calling that the model reasons about when a tool is useful. But there are cases where you specifically want to guarantee a search happens on every call, such as a "check current price" feature where an ungrounded answer is never acceptable regardless of how the model interprets the question.

Two practical strategies handle this without needing an explicit "force" flag (which built-in tools in the Responses API generally do not expose the way custom function tools sometimes do):

  1. Make the current-information need explicit in the prompt. Instead of "What is Bitcoin worth?" write "Search the web for Bitcoin's current price in USD right now and report the figure and the time it was retrieved." Explicit language about wanting live data strongly biases the model toward invoking the tool.
  2. Verify after the fact and retry or fail closed if no search occurred. Since you can inspect response.output for a search item, your application logic can check for one and, if absent, either re-issue the request with stronger wording or return an explicit "could not verify current data" message rather than silently serving an unsearched answer.

The second strategy is the more robust one for production systems, because prompt wording alone is a soft signal — it improves the odds the model searches but does not guarantee it. Lesson 3 goes further into shaping search behavior for specific application needs, including domain restrictions and result counts.

Common Mistakes

Assuming a search happened just because the tool was enabled, which causes silent staleness. Enabling tools=[{"type": "web_search"}] only makes the capability available; the model still decides per-request whether to use it. Always check response.output for a search-related item when your application's correctness depends on fresh data, rather than trusting that the tool being present means it fired.

Reading only output_text and never inspecting output, which causes you to miss what the model actually did to produce that text. This is fine for a quick demo, but for anything where you need to log, audit, or cite sources, you need the structured output list, not just the flattened string.

Treating a fresh-looking answer as always correct, which happens because a synthesized answer reads confidently regardless of whether the underlying search results were relevant or authoritative. A search having occurred is not the same as the answer being accurate — Lesson 7 covers handling conflicting or low-quality sources in depth.

Best Practices

Log the output list, not just output_text, in any application where correctness matters. Persisting the structured output (or at least whether a search item appeared) gives you an audit trail for debugging wrong answers later, without needing to reproduce the exact query against a constantly changing web.

Write prompts that state the need for current information explicitly when your feature depends on freshness, rather than relying on the model to infer that intent from an ambiguous question.

Treat the web search tool definition as a versioned dependency. Because built-in tool schemas can change between API releases, pin the SDK version you test against and re-verify tool behavior when you upgrade, rather than assuming the shape of tools=[{"type": "web_search"}] is permanently fixed.

0 Comments

Reviewed before they appear

No comments yet.

OpenAI SDK
Introduction to the OpenAI SDK Setting Up Python Creating an API Key Your First Call — client.responses.create() and response.output_text Understanding Billing, Credits, and What a Request Costs Why Responses Replaced Chat Completions Anatomy of a Request: model, input, and instructions Anatomy of a Response: The Typed output Array, Not Just Text Roles: User, Assistant, and Developer/System Choosing a Model, and Reading the Models Page Instead of Memorizing Names Instructions vs. Input Writing Prompts That Get Consistent Results Few-Shot Examples Reasoning Models and the reasoning Parameter Debugging a Prompt That Misbehaves Why Streaming Matters for User Experience stream=True and Iterating Over Events Handling the Event Types You Actually Care About Background Mode for Long-Running Jobs Project — Add Live Streaming to Your Chatbot The Problem With Parsing Free Text JSON Schema and Strict Mode Pydantic Models With the SDK's Parse Helpers Handling Refusals and Validation Failures Project — A Resume-to-JSON Extractor Working With input_image input_file, PDFs, and the Files API Image Generation Speech-to-Text and Text-to-Speech Project: A PDF Question-Answering Script What Function Calling Is Defining a Tool Schema The Full Loop Multiple Tools Errors, Timeouts, and Untrusted Arguments Project: A Weather Assistant Web Search File Search and Vector Stores Code Interpreter Remote MCP Servers and Connectors Project: A Research Assistant What an Embedding Is, Without the Maths Generating and Storing Embeddings Similarity Search From Scratch Hosted Vector Stores vs. Rolling Your Own A Small RAG App Over a Folder of Notes Agents vs. a Single API Call — When You Need One pip install openai Giving Agents Tools Handoffs and Multi-Agent Triage Guardrails and Approvals Tracing and Observing What Your Agent Did A Multi-Agent Support Desk Error Codes and What Each One Means Retries, Timeouts, and Backoff Rate Limits and Spend Limits Prompt Caching and Cost Optimisation The Batch API for Bulk Work Async Clients and Concurrency Moderation and Safety Best Practices Designing the App Backend With FastAPI Streaming to a Simple Frontend Deploying and a Cost/Safety Checklist Why Web Search Is Useful for Current Information Using the Web Search Tool with the Responses API Configuring Search Behavior for Application Use Cases Understanding Citations and Source Attribution Where to Go Next Building a Research Assistant with Web Search Combining Web Search with Structured Outputs Handling Conflicting or Low-Quality Web Sources Reducing Unsupported Claims with Grounded Generation Testing Freshness-Sensitive AI Answers Production Considerations for Web-Grounded Applications Understanding File Search and Retrieval-Augmented Generation Creating and Organizing Vector Stores Uploading Documents for Retrieval Connecting Vector Stores to Responses API Requests Designing Document Metadata and Filtering Strategies Building a PDF Question-Answering Application Improving Retrieval Quality With Better Document Preparation Handling Missing Evidence and Retrieval Failures Combining File Search With Web Search Building a Production Knowledge-Base Assistant What the Code Interpreter Tool Is Designed For Running Python-Based Analysis Through the OpenAI SDK Uploading Datasets for Analysis Analyzing CSV and Spreadsheet Data Generating Charts and Data Summaries Handling Generated Files and Downloadable Artifacts Building a Data-Analysis Assistant Combining Code Execution with Structured Outputs Validating Generated Calculations and Results Security and Sandbox Considerations for Code Execution Understanding Multimodal Input with the OpenAI SDK Sending Images to a Model Image Analysis from URLs and Uploaded Files Extracting Text and Information from Screenshots Building an Image-Question-Answering Application Combining Image Input with Structured Output Analyzing Multiple Images in One Request Handling Image Quality and Input Limitations Designing Multimodal Prompts for Reliable Results Building a Practical Vision-Powered Python Application Understanding Speech-to-Text and Text-to-Speech Workflows Transcribing Audio with the OpenAI SDK Working with Uploaded Audio Files Handling Timestamps and Transcription Metadata Building a Meeting Transcription Workflow Generating Spoken Responses from Text Handling Long Audio and Processing Failures Combining Audio with Text and Tool Calling Building an End-to-End Python Voice Application What Embeddings Are and When to Use Them Generating Embeddings With the OpenAI API Preparing Text for Embedding Comparing Vectors With Cosine Similarity Building a Simple Semantic Search Engine in Python Storing Embeddings in a Database Metadata Filtering for Semantic Search Chunking Strategies for Better Retrieval Evaluating Semantic Search Quality Building a Document Similarity Application When Batch Processing Makes Sense Designing Large-Volume AI Processing Pipelines Using Asynchronous Python with the OpenAI SDK Running Concurrent Requests Safely Controlling Concurrency and Avoiding Rate Limits Tracking Batch Job Progress Handling Partial Failures in Bulk Workloads Retrying Failed Items Without Duplicating Successful Work Designing Resumable AI Processing Jobs Building a Production Batch-Processing Pipeline Batch Processing Makes Sense Large-Scale AI Processing Pipelines Async Python with OpenAI SDK Safe Concurrent Requests Concurrency & Rate Limits Batch Progress Tracking Partial Failure Handling Safe Retry Handling Resumable AI Jobs Production Batch Pipeline System–User Data Separation Reusable App Instructions Prompt Templates & Variables Extraction & Classification Prompts Summarization & Transformation Prompts Explicit Output Requirements Prompt Version Management Prompt Testing & Evaluation Reusable Python Prompt Library API Key Security Secure API Key Storage Secure Secret Management Prompt Injection Prevention Trusted vs. Untrusted Content Tool Argument Validation Sensitive Data Handling Secure Logging AI Action Authorization Production AI Security Checklist Why AI Applications Need Evaluation Beyond Unit Tests Unit Testing OpenAI SDK Integration Code Mocking API Responses in Python Tests Testing Structured Outputs Against Schemas Testing Tool-Calling Workflows Building a Small Evaluation Dataset Measuring Accuracy, Consistency, and Failure Rates Regression Testing Prompts and Model Changes Human Evaluation Versus Automated Evaluation Creating a Repeatable Evaluation Pipeline AI Request Monitoring Token Cost Management Usage Metrics Design Reducing Model Calls Prompt & Context Optimization Model Selection & Optimization AI Caching Strategies Interactive Latency Optimization Usage Dashboards & Budget Alerts Performance & Cost Checklist Every API Call Starts Fresh Fixing API Statelessness Server-Side Conversation Memory Limits of Response Chaining What We're Building Conversation Memory Challenges Preparing an OpenAI SDK Application for Deployment Environment-Specific Configuration for Development and Production Deploying a Python AI Service with Docker Container Health Checks and Startup Configuration Managing Secrets in Cloud Deployments Background Workers for Long-Running AI Tasks Queues and Asynchronous Job Architectures Scaling AI Workloads Horizontally Monitoring Production Incidents and Failures Production Deployment Checklist for OpenAI SDK Applications Reusable OpenAI Service Classes AI Client Dependency Injection Typed AI Responses Python Configuration Management AI Request Decorators Centralized AI Error Handling Clean SDK Abstractions Reusable OpenAI Utilities Internal AI Python Libraries SDK Integration Maintenance Production AI Chatbot Document Q&A System Web Research Assistant Customer Support Agent AI Data Analysis Assistant Image Analysis App Meeting Transcription & Summary Semantic Document Search Multi-Tool AI Agent Production OpenAI SDK App Why "It Looked Fine When I Tested It" Isn't Enough Timing Note Status Note Pre-Decision Status Note Current Availability Note
Ask about this post
AI Ask about this post

Ask questions about Using the Web Search Tool with the Responses API and get answers drawn from it.

Signed-in readers only.