Search grounding

Build answers that stay current. Add one tool to your request and return accurate, cited responses grounded in the live web — no custom retrieval pipeline, search index, or RAG infrastructure to build or maintain.

Muse Image searches differently

This page covers the web_search tool for text models such as Muse Spark. Muse Image does its own web and image search automatically while generating, with no web_search tool to add. See Automatic grounding.

How it works

Add web_search to tools in a Responses API request. The model evaluates the query and decides whether to search. When it does, the response includes:

  • web_search_call: An output item confirming a search was performed.
  • url_citation: Annotations on output_text blocks that tie specific spans to their source URLs.
  • results (opt-in): The raw hits behind a web_search_call, returned when you include include: ["web_search_call.results"]. See Inspect the raw search results.

You send the question; the model handles search and synthesis.

Basic usage

Call client.responses.create() with tools=[{"type": "web_search"}]:

python
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.meta.ai/v1",
api_key=os.environ["MODEL_API_KEY"],
)
response = client.responses.create(
model="muse-spark-1.3",
input="Who won the most recent Formula 1 race?",
tools=[
{
"type": "web_search",
},
],
)
print(response.model_dump_json(indent=2))

Example response

json
{
"id": "resp_123",
"object": "response",
"created_at": 1778250764,
"status": "completed",
"model": "muse-spark-1.3",
"output": [
{
"id": "ws_789",
"type": "web_search_call",
"status": "completed"
},
{
"id": "msg_b4d6e9c2ff37410a",
"type": "message",
"status": "completed",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "**Muse Spark** was announced by Meta on **Wednesday, April 8, 2026**.\n\n- It was unveiled as the first AI model from Meta's new \"Muse\" family, developed by Meta Superintelligence Labs \n- Meta described it as a \"small and fast by design\" multimodal model built for real-time reasoning across WhatsApp, Instagram, Facebook, and Meta's smart glasses \n\nThe announcement came in a blog post on April 8, kicking off the week of April 6–10 that TechTarget covered in its roundup published April 10, 2026 .",
"annotations": [
{
"type": "url_citation",
"url": "https://www.techtarget.com/searchcio/feature/Weekly-news-roundup-Claude-Mythos-concerns-Muse-Spark-debut-and-US-infrastructure-disruption",
"title": "Weekly news roundup: Claude Mythos concerns, Muse Spark debut and U.S. infrastructure disruption | TechTarget",
"start_index": 38,
"end_index": 76
},
{
"type": "url_citation",
"url": "https://www.thehindubusinessline.com/info-tech/meta-launches-muse-spark-1.1-ai-bets-big-on-superintelligence-push/article70840923.ece",
"title": "Meta unveils Muse Spark AI model to compete in superintelligence race",
"start_index": 175,
"end_index": 225
},
{
"type": "url_citation",
"url": "https://www.techtarget.com/searchcio/feature/Weekly-news-roundup-Claude-Mythos-concerns-Muse-Spark-debut-and-US-infrastructure-disruption",
"title": "Weekly news roundup: Claude Mythos concerns, Muse Spark debut and U.S. infrastructure disruption | TechTarget",
"start_index": 380,
"end_index": 460
}
]
}
]
}
],
...
}

The output array holds both search metadata and the final answer. Iterate it to pull web_search_call items and message items.

Inspect the raw search results

By default a web_search_call only reports that it ran (id, type, status). To see the sources it retrieved, add include: ["web_search_call.results"] to the request:

json
{
"model": "muse-spark-1.3",
"input": "What was the score of the most recent Formula 1 race? Cite your source.",
"tools": [{"type": "web_search"}],
"include": ["web_search_call.results"]
}

Each web_search_call then includes a results array, one entry per retrieved source:

json
{
"id": "ws_789",
"type": "web_search_call",
"status": "completed",
"results": [
{
"type": "text_result",
"title": "2026 British Grand Prix",
"url": "https://en.wikipedia.org/wiki/2026_British_Grand_Prix",
"snippet": "Leclerc took his ninth Formula One victory, his first at the British Grand Prix..."
}
]
}
FieldDescription
typeResult kind; text_result for a web page.
titleTitle of the retrieved page.
urlSource URL.
snippetExtracted text the model saw from the page.

The results list is every source the model considered; the url_citation annotations are the subset it actually cited.

Working with citations

When the model grounds an answer, it attaches url_citation annotations to output_text blocks. Each annotation includes:

FieldDescription
urlSource URL the model cited
titlePage title of the source
start_indexCharacter offset where the cited passage begins in text
end_indexCharacter offset where the cited passage ends in text

Use those fields to render inline citations, build footnotes, or link users to sources:

Python (OpenAI SDK)
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.meta.ai/v1",
api_key=os.environ["MODEL_API_KEY"],
)
response = client.responses.create(
model="muse-spark-1.3",
input=[{"role": "user", "content": "What is the current population of Tokyo?"}],
tools=[{"type": "web_search"}],
)
for item in response.output:
if item.type == "message":
for block in item.content:
if block.type == "output_text":
print(block.text)
print()
if block.annotations:
print("Sources:")
for ann in block.annotations:
if ann.type == "url_citation":
cited_text = block.text[ann.start_index:ann.end_index]
print(f" - \"{cited_text}\"")
print(f" {ann.title}: {ann.url}")

Controlling search context

search_context_size controls how much retrieved content reaches the model. More context helps on broader questions but increases latency and token usage.

python
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.meta.ai/v1",
api_key=os.environ["MODEL_API_KEY"],
)
response = client.responses.create(
model="muse-spark-1.3",
input="Summarize recent developments in fusion energy research.",
tools=[
{
"type": "web_search",
"search_context_size": "high",
},
],
)
print(response.model_dump_json(indent=2))

Valid values:

ValueBehavior
"low"Minimal context. Fastest responses, lowest token usage.
"medium"Balanced context. Good default for most queries.
"high"Maximum context. Best for complex queries that benefit from more sources.

Localize results with user location

Pass an approximate user_location on the tool to bias search toward a locale — useful for "near me" and other location-sensitive queries. Every field is optional; supply only what you know. When set, type must be "approximate" (its default when omitted).

python
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.meta.ai/v1",
api_key=os.environ["MODEL_API_KEY"],
)
response = client.responses.create(
model="muse-spark-1.3",
input="What are the best-rated coffee shops near me?",
tools=[
{
"type": "web_search",
"user_location": {
"type": "approximate",
"country": "GB",
"region": "London",
"city": "London",
"timezone": "Europe/London",
},
},
],
)
print(response.model_dump_json(indent=2))
FieldDescription
typeLocation approximation type. Optional; defaults to "approximate", the only supported value.
countryTwo-letter ISO 3166-1(opens in new tab) code, such as GB.
regionRegion as free text, such as California.
cityCity as free text, such as San Francisco.
timezoneIANA time zone(opens in new tab) name, such as America/Los_Angeles.

Streaming

Stream search-grounded answers with client.responses.stream(). The stream emits search-call events first, then the answer tokens:

Python (OpenAI SDK)
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.meta.ai/v1",
api_key=os.environ["MODEL_API_KEY"],
)
with client.responses.stream(
model="muse-spark-1.3",
input=[{"role": "user", "content": "What are the latest developments in AI regulation?"}],
tools=[{"type": "web_search"}],
) as stream:
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
print()
response = stream.get_final_response()
for item in response.output:
if item.type == "message":
for block in item.content:
if block.annotations:
print("\nSources:")
for ann in block.annotations:
if ann.type == "url_citation":
print(f" - [{ann.title}]({ann.url})")

Constraints

  • Responses API only: Search grounding is not available through the Chat Completions API.
  • Can be combined with developer-defined tools: You can use web_search alongside developer-defined function tools in the same request. When you do, your function tools must not reuse a name reserved by the web_search internals (currently browser.search, browser.open, and browser.find). These reserved names are injected server-side and may change over time. A function tool whose name collides with one returns HTTP 400 (type: invalid_request_error, param: tools). The restriction applies only when web_search is in the same request; these names are otherwise valid function names.
  • The model decides whether to search: Enabling web_search does not guarantee a search on every request. The model evaluates the query and skips the search when it can answer confidently from its training data. Simple factual questions such as "What is the capital of France?" typically do not trigger a search.
  • Replaying web_search_call items in multi-turn input: When you build conversation history manually (via the input array rather than previous_response_id) and include prior web_search_call items, the id field is optional. If omitted or null, the server auto-assigns a unique ID before validation. You do not need to store or replay server-assigned web_search_call IDs from earlier turns.

Limitations

Search grounding is reliable for factual lookups and recent-events questions, but keep these limits in mind as you build:

  • Quality is still improving: Answer quality and source selection continue to improve. Treat a search-grounded answer as a strong starting point rather than a final authority, and verify anything you depend on.
  • Coverage is incomplete: Not every web source can be retrieved, so an answer may miss relevant pages or omit sources it would otherwise cite. Check the returned url_citations and the raw results to see what the model actually saw.
  • Best for focused questions: Single-fact and recent-events queries are the most reliable. Complex, multi-hop research that chains many sources into one answer is less dependable today; break these into narrower requests where you can.
  • Search isn't guaranteed: The model decides whether to search (see Constraints), so enabling web_search does not force a search on every request.

Next steps