Web Search
Use web search with litellm
| Feature | Details |
|---|---|
| Supported Endpoints | - /chat/completions - /responses - /images/generations (Gemini image models only) |
| Supported Providers | openai, xai, vertex_ai, anthropic, gemini, perplexity |
| LiteLLM Cost Tracking | ✅ Supported |
| LiteLLM Version | v1.71.0+ |
Which Search Engine is Used?
Each provider uses their own search backend:
| Provider | Search Engine | Notes |
|---|---|---|
OpenAI (gpt-5-search-api, gpt-4o-search-preview, gpt-4o-mini-search-preview) | OpenAI's internal search | Real-time web data |
xAI (grok-3) | xAI's search + X/Twitter | Real-time social media data |
Google AI/Vertex (gemini-2.0-flash) | Google Search | Uses actual Google search results |
Anthropic (claude-3-5-sonnet) | Anthropic's web search | Real-time web data |
| Perplexity | Perplexity's search engine | AI-powered search and reasoning |
web_search_optionsFor OpenAI, only dedicated search models support the web_search_options parameter:
gpt-4o-search-previewgpt-4o-mini-search-previewgpt-5-search-api
Regular models like gpt-6.1-sol and gpt-6-luna do not support web_search_options
web_search_options parameter is optionalSearch models (like gpt-4o-search-preview) automatically search the web even without the web_search_options parameter.
Use web_search_options when you need to:
- Adjust
search_context_size("low","medium","high") - Specify
user_locationfor localized results
Anthropic Web Search Models: Claude models that support web search: claude-3-5-sonnet-latest, claude-3-5-sonnet-20241022, claude-3-5-haiku-latest, claude-3-5-haiku-20241022, claude-3-7-sonnet-20250219
OpenAI Web Search: Two Approaches
OpenAI offers two distinct ways to use web search depending on the endpoint and model:
| Approach | Endpoint | Models | How to enable |
|---|---|---|---|
| Search Models | /chat/completions | gpt-5-search-api, gpt-4o-search-preview, gpt-4o-mini-search-preview | Pass web_search_options parameter |
| Web Search Tool | /responses | gpt-5, gpt-4.1, gpt-4o, and other regular models | Pass web_search_preview tool |
Search models like gpt-5-search-api automatically search the web even without the web_search_options parameter. Use web_search_options to set search_context_size ("low", "medium", "high") or specify user_location for localized results.
/chat/completions (litellm.completion)
Quick Start
- SDK
- PROXY
from litellm import completion
response = completion(
model="openai/gpt-5-search-api",
messages=[
{
"role": "user",
"content": "What was a positive news story from today?",
}
],
web_search_options={
"search_context_size": "medium" # Options: "low", "medium", "high"
}
)
- Setup config.yaml
model_list:
# OpenAI search models
- model_name: gpt-5-search-api
litellm_params:
model: openai/gpt-5-search-api
api_key: os.environ/OPENAI_API_KEY
- model_name: gpt-4o-search-preview
litellm_params:
model: openai/gpt-4o-search-preview
api_key: os.environ/OPENAI_API_KEY
# xAI
- model_name: grok-3
litellm_params:
model: xai/grok-3
api_key: os.environ/XAI_API_KEY
# Anthropic
- model_name: claude-sonnet-5
litellm_params:
model: anthropic/claude-sonnet-5
api_key: os.environ/ANTHROPIC_API_KEY
# VertexAI
- model_name: gemini-2-flash
litellm_params:
model: gemini-3.8-flash
vertex_project: your-project-id
vertex_location: us-central1
# Google AI Studio
- model_name: gemini-2-flash-studio
litellm_params:
model: gemini/gemini-3.8-flash
api_key: os.environ/GOOGLE_API_KEY
- Start the proxy
litellm --config /path/to/config.yaml
- Test it!
from openai import OpenAI
# Point to your proxy server
client = OpenAI(
api_key="sk-<your-litellm-api-key>",
base_url="http://0.0.0.0:4000"
)
response = client.chat.completions.create(
model="gpt-5-search-api", # or any other web search enabled model
messages=[
{
"role": "user",
"content": "What was a positive news story from today?"
}
],
extra_body={
"web_search_options": {
"search_context_size": "medium"
}
}
)
Search context size
- SDK
- PROXY
OpenAI (using web_search_options)
from litellm import completion
# Customize search context size
response = completion(
model="openai/gpt-5-search-api",
messages=[
{
"role": "user",
"content": "What was a positive news story from today?",
}
],
web_search_options={
"search_context_size": "low" # Options: "low", "medium" (default), "high"
}
)
xAI (using web_search_options)
xAI no longer runs web search on its own /chat/completions, so when web_search_options is set LiteLLM sends the request to xAI's Responses API with a web_search tool and converts the result back to a chat completion. xAI does not support search_context_size, so it is dropped
from litellm import completion
response = completion(
model="xai/grok-3",
messages=[
{
"role": "user",
"content": "What was a positive news story from today?",
}
],
web_search_options={} # search_context_size is ignored for xAI
)
Anthropic (using web_search_options)
from litellm import completion
# Customize search context size for Anthropic
response = completion(
model="anthropic/claude-sonnet-5",
messages=[
{
"role": "user",
"content": "What was a positive news story from today?",
}
],
web_search_options={
"search_context_size": "medium", # Options: "low", "medium" (default), "high"
"user_location": {
"type": "approximate",
"approximate": {
"city": "San Francisco",
},
}
}
)
VertexAI/Gemini (using web_search_options)
from litellm import completion
# Customize search context size for Gemini
response = completion(
model="gemini-3.8-flash",
messages=[
{
"role": "user",
"content": "What was a positive news story from today?",
}
],
web_search_options={
"search_context_size": "low" # Options: "low", "medium" (default), "high"
}
)
Gemini image generation (using web_search_options on /images/generations)
from litellm import image_generation
response = image_generation(
model="gemini/gemini-3.1-flash-image-preview",
prompt="Generate an image of the latest iPhone design",
web_search_options={},
)
Works with vertex_ai/gemini-3.1-flash-image-preview and other Gemini image models. See Vertex AI Image Generation and Google AI Studio Image Generation.
from openai import OpenAI
# Point to your proxy server
client = OpenAI(
api_key="sk-<your-litellm-api-key>",
base_url="http://0.0.0.0:4000"
)
# Customize search context size
response = client.chat.completions.create(
model="grok-3", # works with any web search enabled model
messages=[
{
"role": "user",
"content": "What was a positive news story from today?"
}
],
web_search_options={
"search_context_size": "low" # Options: "low", "medium" (default), "high"
}
)
/responses (litellm.responses)
Use the web_search_preview tool with models like gpt-6.1-sol, gpt-6-luna, etc.
Search-dedicated models like gpt-5-search-api and gpt-4o-search-preview do not support the /responses endpoint. Use them with /chat/completions + web_search_options instead (see above).
Quick Start
- SDK
- PROXY
from litellm import responses
response = responses(
model="openai/gpt-6.1-sol",
input="What is the capital of France?",
tools=[{
"type": "web_search_preview" # enables web search with default medium context size
}]
)
- Setup config.yaml
model_list:
- model_name: gpt-6.1-sol
litellm_params:
model: openai/gpt-6.1-sol
api_key: os.environ/OPENAI_API_KEY
- model_name: gpt-6-luna
litellm_params:
model: openai/gpt-6-luna
api_key: os.environ/OPENAI_API_KEY
- Start the proxy
litellm --config /path/to/config.yaml
- Test it!
from openai import OpenAI
# Point to your proxy server
client = OpenAI(
api_key="sk-<your-litellm-api-key>",
base_url="http://0.0.0.0:4000"
)
response = client.responses.create(
model="gpt-6.1-sol",
tools=[{
"type": "web_search_preview"
}],
input="What is the capital of France?",
)
print(response.output_text)
Search context size
- SDK
- PROXY
from litellm import responses
# Customize search context size
response = responses(
model="openai/gpt-6.1-sol",
input="What is the capital of France?",
tools=[{
"type": "web_search_preview",
"search_context_size": "low" # Options: "low", "medium" (default), "high"
}]
)
from openai import OpenAI
# Point to your proxy server
client = OpenAI(
api_key="sk-<your-litellm-api-key>",
base_url="http://0.0.0.0:4000"
)
# Customize search context size
response = client.responses.create(
model="gpt-6.1-sol",
tools=[{
"type": "web_search_preview",
"search_context_size": "low" # Options: "low", "medium" (default), "high"
}],
input="What is the capital of France?",
)
print(response.output_text)
Configuring Web Search in config.yaml
You can set default web search options directly in your proxy config file:
- Default Web Search
- Custom Search Context
model_list:
# Enable web search by default for all requests to this model
- model_name: grok-3
litellm_params:
model: xai/grok-3
api_key: os.environ/XAI_API_KEY
web_search_options: {} # Enables web search with default settings
Advanced
You can configure LiteLLM's router to optionally drop models that do not support WebSearch, for example
- model_name: gpt-6.1-sol
litellm_params:
model: openai/gpt-6.1-sol
- model_name: gpt-6.1-sol
litellm_params:
model: azure/gpt-6.1-sol
api_base: "x.openai.azure.com/"
api_version: 2025-03-01-preview
model_info:
supports_web_search: False <---- KEY CHANGE!
In this example, LiteLLM will still route LLM requests to both deployments, but for WebSearch, will solely route to OpenAI.
model_list:
# Set custom web search context size
- model_name: grok-3
litellm_params:
model: xai/grok-3
api_key: os.environ/XAI_API_KEY
web_search_options:
search_context_size: "high" # Options: "low", "medium", "high"
# OpenAI search model with custom context size
- model_name: gpt-5-search-api
litellm_params:
model: openai/gpt-5-search-api
api_key: os.environ/OPENAI_API_KEY
web_search_options:
search_context_size: "low"
# Gemini with medium context (default)
- model_name: gemini-2-flash
litellm_params:
model: gemini-3.8-flash
vertex_project: your-project-id
vertex_location: us-central1
web_search_options:
search_context_size: "medium"
Note: When web_search_options is set in the config, it applies to all requests to that model. Users can still override these settings by passing web_search_options in their API requests.
Checking if a model supports web search
- SDK
- PROXY
Use litellm.supports_web_search(model="model_name") -> returns True if model can perform web searches
# Check OpenAI models
assert litellm.supports_web_search(model="openai/gpt-5-search-api") == True
assert litellm.supports_web_search(model="openai/gpt-4o-search-preview") == True
# Check xAI models
assert litellm.supports_web_search(model="xai/grok-3") == True
# Check Anthropic models
assert litellm.supports_web_search(model="anthropic/claude-sonnet-5") == True
# Check VertexAI models
assert litellm.supports_web_search(model="gemini-3.8-flash") == True
# Check Google AI Studio models
assert litellm.supports_web_search(model="gemini/gemini-3.8-flash") == True
- Define models in config.yaml
model_list:
# OpenAI
- model_name: gpt-5-search-api
litellm_params:
model: openai/gpt-5-search-api
api_key: os.environ/OPENAI_API_KEY
model_info:
supports_web_search: True
- model_name: gpt-4o-search-preview
litellm_params:
model: openai/gpt-4o-search-preview
api_key: os.environ/OPENAI_API_KEY
model_info:
supports_web_search: True
# xAI
- model_name: grok-3
litellm_params:
model: xai/grok-3
api_key: os.environ/XAI_API_KEY
model_info:
supports_web_search: True
# Anthropic
- model_name: claude-sonnet-5
litellm_params:
model: anthropic/claude-sonnet-5
api_key: os.environ/ANTHROPIC_API_KEY
model_info:
supports_web_search: True
# VertexAI
- model_name: gemini-2-flash
litellm_params:
model: gemini-3.8-flash
vertex_project: your-project-id
vertex_location: us-central1
model_info:
supports_web_search: True
# Google AI Studio
- model_name: gemini-2-flash-studio
litellm_params:
model: gemini/gemini-3.8-flash
api_key: os.environ/GOOGLE_API_KEY
model_info:
supports_web_search: True
- Run proxy server
litellm --config config.yaml
- Call
/model_group/infoto check if a model supports web search
curl -X 'GET' \
'http://localhost:4000/model_group/info' \
-H 'accept: application/json' \
-H "x-api-key: $LITELLM_API_KEY"
Expected Response
{
"data": [
{
"model_group": "gpt-5-search-api",
"providers": ["openai"],
"max_tokens": 128000,
"supports_web_search": true
},
{
"model_group": "gpt-4o-search-preview",
"providers": ["openai"],
"max_tokens": 128000,
"supports_web_search": true
},
{
"model_group": "grok-3",
"providers": ["xai"],
"max_tokens": 131072,
"supports_web_search": true
},
{
"model_group": "gemini-2-flash",
"providers": ["vertex_ai"],
"max_tokens": 8192,
"supports_web_search": true
}
]
}
Web Search Cost Tracking
LiteLLM tracks web search costs automatically based on provider-specific billing models. The cost is added on top of the standard token-based pricing.
How providers charge for web search
| Provider | Billing Unit | How it works |
|---|---|---|
| Gemini 3.x (3-flash, 3-pro, 3.1-*) | Per search query | Each internal search query is billed individually. One prompt may trigger multiple queries. |
| Gemini 2.x (2.0-flash, 2.5-flash, 2.5-pro) | Per grounded prompt | Flat fee per API call that uses grounding, regardless of how many queries are executed internally. |
| OpenAI (gpt-4o-search, gpt-5-search) | Per search context size | Cost varies by search_context_size (low, medium, high). |
| Anthropic (Claude with web search) | Per search request | Fixed cost per web search tool invocation. |
| Perplexity (sonar, sonar-pro) | Per search context size | Cost varies by search_context_size. |
Pricing configuration
Web search costs are defined in model_prices_and_context_window.json using two fields:
search_context_cost_per_query: the cost per billable unit (per search context size tier).web_search_billing_unit(on Gemini models):"per_query"(each search query is billed individually) or"per_prompt"(default; flat fee per API call that uses search).
{
"gemini/gemini-3.8-flash": {
"web_search_billing_unit": "per_query",
"search_context_cost_per_query": {
"search_context_size_low": 0.014,
"search_context_size_medium": 0.014,
"search_context_size_high": 0.014
}
},
"gemini/gemini-2.5-flash": {
"search_context_cost_per_query": {
"search_context_size_low": 0.035,
"search_context_size_medium": 0.035,
"search_context_size_high": 0.035
}
}
}
Models without web_search_billing_unit default to "per_prompt": one flat charge per API call that uses web search, regardless of how many internal queries the model executes.
You can override these in your proxy config using model_info:
model_list:
- model_name: gemini-3.8-flash
litellm_params:
model: gemini/gemini-3.8-flash
model_info:
web_search_billing_unit: per_query
search_context_cost_per_query:
search_context_size_low: 0.014
search_context_size_medium: 0.014
search_context_size_high: 0.014
Google's free grounding allowance
Google gives every billing account a free allowance of Google Search grounding before the per-query price applies: 5,000 queries per month shared across the Gemini 3 models, 1,500 grounded prompts per day shared across Gemini 2.0 Flash, 2.5 Flash and 2.5 Flash-Lite, and 10,000 grounded prompts per day for Gemini 2.5 Pro. By default LiteLLM bills grounding from the first query, so logged grounding spend is an upper bound on the Google invoice.
The cost map describes each allowance with search_free_requests on the Gemini rows. count is the free units per period, period is "day" or "month", and models with the same shared_group draw from one pool, counted in the row's web_search_billing_unit.
{
"gemini/gemini-3.8-flash": {
"web_search_billing_unit": "per_query",
"search_free_requests": {"count": 5000, "period": "month", "shared_group": "gemini-3"}
}
}
The field does nothing on its own. To have the proxy log grounding at $0 until the shared allowance is used up, opt in with apply_search_free_tier. The proxy keeps one counter per shared_group and UTC period in its Redis, the same Redis it uses for budgets, so every proxy instance draws from the same pool and a restart does not reset it. A request that crosses the allowance is split: the queries still inside it are free and the rest are billed at search_context_cost_per_query. The spend row keeps the full price in metadata.cost_breakdown.original_cost and the freed amount in discount_amount.
litellm_settings:
apply_search_free_tier: true # default false
cache: true
cache_params:
type: redis
host: os.environ/REDIS_HOST
port: os.environ/REDIS_PORT
Two things to know before turning it on. The allowance is per Google billing account and LiteLLM only sees its own traffic, so if anything else on the account also grounds, LiteLLM frees queries that Google billed and the logged spend becomes a lower bound inside the allowance window; leave the setting off if other workloads share the account. Google does not publish the time zone its allowance resets in, so LiteLLM resets on the UTC calendar day and month. Without Redis, or when the counter cannot be reached, the request is billed at full price and a warning is logged. The x-litellm-response-cost header and logging callbacks still report the pre-allowance cost; only the spend row, budgets and spend counters see the reduced spend.
How LiteLLM tracks search usage
The number of web search requests is stored in usage.prompt_tokens_details.web_search_requests. LiteLLM extracts this from each provider's response:
- Gemini: Extracted from
groundingMetadata.webSearchQueriesin the response. For Gemini 2.x, clamped to 1 (per-prompt billing). - OpenAI: Reported directly in the usage metadata.
- Anthropic: Reported via
server_tool_use.web_search_requests. - xAI: Reported in
usage.server_side_tool_usage_details.web_search_callsrather thanweb_search_requests.num_sources_usedis not read. When xAI returnsusage.cost, LiteLLM uses it as the response cost, which already includes the search charge
response = litellm.completion(
model="gemini/gemini-3.8-flash",
messages=[{"role": "user", "content": "Latest tech news?"}],
web_search_options={"search_context_size": "medium"},
)
# Check web search usage
print(response.usage.prompt_tokens_details.web_search_requests) # e.g., 3
# Get total cost (includes token cost + web search cost)
cost = litellm.completion_cost(completion_response=response)
print(f"Total cost: ${cost}")