Configure and control web results
On this page
Lesson updated November 2, 2026 based on OpenAI documentation: the Code Interpreter tool requires a
containerparameter (added to all examples), input text parts are of typeinput_text, and web search uses theweb_searchtool (web_search_previewis now a legacy tool without domain filters). Generated files are retrieved using thefichiers_generesandtelechargerfunctions from Lesson 10. Sources: Code Interpreter · Web search.
Configure and control web results
Enabling Web Search takes one line; getting reliable answers requires three settings that this lesson details. The first decides how much web content gets injected into the context, and it directly impacts your bill. The second steers the queries the model formulates itself—because it writes them, not you. The third concerns citations; without them, a sourced answer is just an affirmative statement.
The search_context_size parameter
The search_context_size parameter controls how much web content gets injected into the model’s context. It changes neither the number of searches nor their quality: it determines how much text, once pages are found, actually enters the prompt. This is a trade-off between exhaustiveness and cost, and you should tune it by task type, not set it once and forget it.
from openai import OpenAI
client = OpenAI()
# Minimal context: fast, concise answers
response = client.responses.create(
model="gpt-5.6-terra",
input="What is France's GDP in 2025?",
tools=[{
"type": "web_search",
"search_context_size": "low"
}]
)
# Medium context: good compromise (default)
response = client.responses.create(
model="gpt-5.6-terra",
input="Summarize AI market trends in Europe",
tools=[{
"type": "web_search",
"search_context_size": "medium"
}]
)
# Large context: in-depth analysis
response = client.responses.create(
model="gpt-5.6-terra",
input="Compare AI strategies in France, Germany, and the Netherlands",
tools=[{
"type": "web_search",
"search_context_size": "high"
}]
)
Impact on costs and latency
The rule of thumb is simple: bump it up one notch only when the answer you got concretely disappointed you. A date, a figure, or a factual confirmation needs nothing more than low; using high for that is like paying for a full dossier to read one line.
| Size | Context tokens | Latency | Cost | Usage |
|---|---|---|---|---|
low | Minimal | Fast | Low | Simple facts, figures |
medium | Moderate | Standard | Medium | Summaries, comparisons |
high | Maximal | Slower | High | In-depth analysis |
Guide search queries via the prompt
Here’s the point most people discover too late: the model formulates its own search queries. You write “what are the recent regulatory changes?” and it translates that into keywords you never see. Result quality therefore depends on a reformulation over which you have only indirect control—the system prompt. Name the authoritative sources in your field and the period you care about: that moves the needle most.
response = client.responses.create(
model="gpt-5.6-terra",
instructions=(
"You are a market analyst specializing in European technology. "
"When you perform web searches, prioritize institutional sources "
"(European Commission, INSEE, Eurostat) and specialized media "
"(Les Echos, La Tribune, TechCrunch). "
"Ignore personal blogs and forums."
),
input="What is the state of AI startup funding in France?",
tools=[{
"type": "web_search",
"search_context_size": "high"
}]
)
Extract and structure citations
Citations arrive as annotations attached to the response, not in the text itself. This separation is good news: it lets you display sources however you want, verify them, or reject an answer that has none. In any application where the answer carries weight—legal, medical, financial—treating missing annotations as an error is safer than trusting the text.
def extract_sources(response) -> list[dict]:
"""Extract all URL citations from a Web Search response."""
sources = []
for item in response.output:
if item.type == "message":
for block in item.content:
if hasattr(block, "annotations"):
for ann in block.annotations:
if ann.type == "url_citation":
sources.append({
"url": ann.url,
"title": ann.title,
"excerpt_start": ann.start_index,
"excerpt_end": ann.end_index
})
return sources
# Usage
response = client.responses.create(
model="gpt-5.6-terra",
input="What are the new GDPR rules for AI in 2026?",
tools=[{"type": "web_search"}]
)
sources = extract_sources(response)
for s in sources:
print(f" {s['title']}: {s['url']}")
Pattern: search with cross-validation
A single source is still a single source, even if a model found it. For information that drives a decision, explicitly ask for confirmation from multiple independent sources and have the model flag disagreements rather than silently arbitrating them: a disagreement between two sources is itself information, often more useful than averaging the two.
def validated_search(question: str, min_sources: int = 3) -> dict:
"""Web search requiring multiple sources."""
response = client.responses.create(
model="gpt-5.6-terra",
instructions=(
f"To answer this question, you must find at least "
f"{min_sources} independent sources that confirm the information. "
"If sources contradict each other, flag the discrepancies. "
"Cite each source explicitly."
),
input=question,
tools=[{
"type": "web_search",
"search_context_size": "high"
}]
)
sources = extract_sources(response)
return {
"answer": response.output_text,
"sources": sources,
"reliability": "high" if len(sources) >= min_sources else "verify"
}
Handle no results
There’s one case people forget to handle: the search returns nothing. The risk isn’t the lack of an answer—it’s that, lacking results, the model answers from its internal knowledge without signaling it, giving you plausible but outdated information. Ask it to say so explicitly, and plan for reformulation.
def search_with_fallback(question: str) -> dict:
"""Web search with failure detection and reformulation."""
# First attempt
response = client.responses.create(
model="gpt-5.6-terra",
input=question,
tools=[{"type": "web_search"}]
)
sources = extract_sources(response)
if len(sources) == 0:
# Automatic reformulation
response = client.responses.create(
model="gpt-5.6-terra",
input=f"Rephrase this question for a more effective web search, "
f"then search: {question}",
tools=[{"type": "web_search", "search_context_size": "high"}]
)
sources = extract_sources(response)
return {
"answer": response.output_text,
"sources": sources
}
Web Search with streaming
A web search takes a few seconds, sometimes more if the model chains several queries. With streaming, the user sees the answer build up instead of staring at a loading indicator—and for this kind of latency, the difference in perception is huge for near-zero implementation cost.
stream = client.responses.create(
model="gpt-5.6-terra",
input="What are this week's tech news headlines?",
tools=[{"type": "web_search"}],
stream=True
)
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
elif event.type == "response.completed":
# Extract sources at the end
sources = extract_sources(event.response)
print(f"\n\n--- {len(sources)} sources consulted ---")
Filter domains and list sources
The web_search tool accepts a filters parameter that restricts the search to a list of allowed domains (up to 100) or excludes a list (also up to 100). Write domains without http:// or https://. Filtering exists only with web_search in the Responses API: the legacy web_search_preview tool ignores it.
response = client.responses.create(
model="gpt-5.6-terra",
tools=[{
"type": "web_search",
"filters": {"allowed_domains": ["service-public.fr", "legifrance.gouv.fr"]},
}],
include=["web_search_call.action.sources"],
input="What are the price display requirements for retail stores in France?",
)
print(response.output_text)
The include=["web_search_call.action.sources"] option returns the full list of URLs consulted, often longer than the list of citations. Citations themselves arrive as url_citation annotations (URL, title, position in text): OpenAI’s documentation asks that you display them visibly and clickably in your interface.
Key takeaways
search_context_sizecontrols the volume of web data injectedfiltersrestricts or excludes domains (max 100);includelists all consulted sources- The system prompt guides the model toward the right sources
- Extract
url_citationannotations to trace sources - Implement cross-validation for critical information
- Handle missing results with automatic reformulation