Skip to content
Docs

AI Gateway Web Search

AI Gateway provides built-in search tools that let AI models access current web information and source-grounded data. Use them when you need information that may not be in the model's training data.

AI Gateway supports two types of web search:

For pages the model already has a URL for, Browserbase Fetch retrieves the contents directly instead of searching.

Search tool definitions differ by API and provider. The TypeScript AI SDK example uses AI Gateway's Perplexity tool. The Python beta example uses Anthropic's native web search tool. Chat Completions uses its server-tool format. Messages uses Anthropic's native web search tool. Responses uses OpenAI's native web search with an OpenAI model. Don't copy a tool definition between these formats. The Python beta example supports Anthropic's native web search. Gateway's Perplexity search helper is not supported in this beta agent loop.

For provider-specific search, follow the Anthropic, OpenAI, and Google sections. Use the Responses tool reference for function tools. Native search tool availability depends on the selected provider and model.

See the AI SDK provider-executed tools reference for SDK configuration and usage.

search.ts
import { generateText, gateway, isStepCount } from 'ai';
 
const { text } = await generateText({
  tools: { search: gateway.tools.perplexitySearch({ maxResults: 1 }) },
  stopWhen: isStepCount(3),
  model: "anthropic/claude-sonnet-5",
  prompt: "Search the web for Vercel AI Gateway and summarize it in one sentence.",
});
 
console.log(text);
search_ai.py
import asyncio
import ai
from ai.providers.anthropic import tools as anthropic_tools
 
async def main():
    model = ai.get_model("anthropic/claude-sonnet-5")
    messages = [ai.user_message("Search the web for Vercel AI Gateway and summarize it in one sentence.")]
    agent = ai.Agent(tools=[anthropic_tools.web_search(max_uses=1)])
    async with agent.run(model, messages) as stream:
        async for event in stream:
            if isinstance(event, ai.events.TextDelta):
                print(event.chunk, end="", flush=True)
    print()
 
asyncio.run(main())
search-chat.ts
const response = await fetch(
  'https://ai-gateway.vercel.sh/v1/chat/completions',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.AI_GATEWAY_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'anthropic/claude-sonnet-5',
      messages: [
        {
          role: 'user',
          content:
            'Search the web for Vercel AI Gateway and summarize it in one sentence.',
        },
      ],
      tools: [
        {
          type: 'vercel:perplexity_search',
          config: {
            query: 'Vercel AI Gateway',
            max_results: 1,
          },
        },
      ],
    }),
  },
);
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
search_chat.py
import os
from openai import OpenAI
 
client = OpenAI(
    api_key=os.environ["AI_GATEWAY_API_KEY"],
    base_url="https://ai-gateway.vercel.sh/v1",
)
 
response = client.chat.completions.create(
    tools=[{"type": "vercel:perplexity_search", "config": {"query": "Vercel AI Gateway", "max_results": 1}}],
    model="anthropic/claude-sonnet-5",
    messages=[{"role": "user", "content": "Search the web for Vercel AI Gateway and summarize it in one sentence."}],
)
 
print(response.choices[0].message.content)
search-chat.sh
curl --fail-with-body https://ai-gateway.vercel.sh/v1/chat/completions \
  -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "anthropic/claude-sonnet-5",
  "messages": [
    {
      "role": "user",
      "content": "Search the web for Vercel AI Gateway and summarize it in one sentence."
    }
  ],
  "tools": [
    {
      "type": "vercel:perplexity_search",
      "config": {
        "query": "Vercel AI Gateway",
        "max_results": 1
      }
    }
  ]
}'
search-messages.ts
import Anthropic from '@anthropic-ai/sdk';
 
const client = new Anthropic({
  apiKey: process.env.AI_GATEWAY_API_KEY,
  baseURL: 'https://ai-gateway.vercel.sh',
});
 
const response = await client.messages.create({
  tools: [
    {
      type: 'web_search_20250305',
      name: 'web_search',
      max_uses: 1,
    },
  ],
  model: 'anthropic/claude-sonnet-5',
  messages: [
    {
      role: 'user',
      content:
        'Search the web for Vercel AI Gateway and summarize it in one sentence.',
    },
  ],
  max_tokens: 1024,
});
 
for (const block of response.content) {
  if (block.type === 'text') console.log(block.text);
}
search_messages.py
import os
from anthropic import Anthropic
 
client = Anthropic(
    api_key=os.environ["AI_GATEWAY_API_KEY"],
    base_url="https://ai-gateway.vercel.sh",
)
 
response = client.messages.create(
    tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 1}],
    model="anthropic/claude-sonnet-5",
    messages=[{"role": "user", "content": "Search the web for Vercel AI Gateway and summarize it in one sentence."}],
    max_tokens=1024,
)
 
for block in response.content:
    if block.type == "text":
        print(block.text)
search-messages.sh
curl --fail-with-body https://ai-gateway.vercel.sh/v1/messages \
  -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
  "model": "anthropic/claude-sonnet-5",
  "messages": [
    {
      "role": "user",
      "content": "Search the web for Vercel AI Gateway and summarize it in one sentence."
    }
  ],
  "max_tokens": 1024,
  "tools": [
    {
      "type": "web_search_20250305",
      "name": "web_search",
      "max_uses": 1
    }
  ]
}'
search-responses.ts
import OpenAI from 'openai';
 
const client = new OpenAI({
  apiKey: process.env.AI_GATEWAY_API_KEY,
  baseURL: 'https://ai-gateway.vercel.sh/v1',
});
 
const response = await client.responses.create({
  tools: [{ "type": "web_search", "search_context_size": "low" }],
  model: "openai/gpt-5.4-mini",
  input: "Search the web for Vercel AI Gateway and summarize it in one sentence.",
});
 
console.log(response.output_text);
search_responses.py
import os
from openai import OpenAI
 
client = OpenAI(
    api_key=os.environ["AI_GATEWAY_API_KEY"],
    base_url="https://ai-gateway.vercel.sh/v1",
)
 
response = client.responses.create(
    tools=[{"type": "web_search", "search_context_size": "low"}],
    model="openai/gpt-5.4-mini",
    input="Search the web for Vercel AI Gateway and summarize it in one sentence.",
)
 
print(response.output_text)
search-responses.sh
curl --fail-with-body https://ai-gateway.vercel.sh/v1/responses \
  -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "openai/gpt-5.4-mini",
  "input": "Search the web for Vercel AI Gateway and summarize it in one sentence.",
  "tools": [
    {
      "type": "web_search",
      "search_context_size": "low"
    }
  ]
}'

The perplexitySearch tool can be used with any model regardless of the model provider or creator. This makes it a flexible option when you want consistent web search behavior across different models, or when you want to use web search with a model whose provider doesn't offer native web search capabilities.

To use Perplexity Search, import gateway from ai and pass gateway.tools.perplexitySearch() to the tools parameter. When the model needs current information, it calls the tool and AI Gateway routes the request to Perplexity's search API.

Perplexity web search requests are charged at $5 per 1,000 requests. See Perplexity's pricing for more details.

perplexity-web-search.ts
import { gateway, streamText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'openai/gpt-6-astra', // Works with any model, not just Perplexity
    prompt,
    tools: {
      perplexity_search: gateway.tools.perplexitySearch(),
    },
  });
 
  for await (const part of result.stream) {
    if (part.type === 'text-delta') {
      process.stdout.write(part.text);
    } else if (part.type === 'tool-call') {
      console.log('Tool call:', part.toolName);
    } else if (part.type === 'tool-result') {
      console.log('Search results received');
    }
  }
 
  return result.toUIMessageStreamResponse();
}
perplexity-web-search.ts
import { gateway, generateText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'openai/gpt-6-astra', // Works with any model, not just Perplexity
    prompt,
    tools: {
      perplexity_search: gateway.tools.perplexitySearch(),
    },
  });
 
  return Response.json({ text });
}

You can configure the perplexitySearch tool with these parameters:

  • maxResults: Number of results to return (1-20). Defaults to 10.
  • maxTokens: Total token budget across all results. Defaults to 25,000, max 1,000,000.
  • maxTokensPerPage: Tokens extracted per webpage. Defaults to 2,048.
  • country: ISO 3166-1 alpha-2 country code (e.g., 'US', 'GB') for regional results.
  • searchLanguageFilter: ISO 639-1 language codes (e.g., ['en', 'fr']). Max 10 codes.
  • searchDomainFilter: Domains to include (e.g., ['reuters.com']) or exclude with - prefix (e.g., ['-reddit.com']). Max 20 domains. Cannot mix allowlist and denylist.
  • searchRecencyFilter: Filter by content recency. Values: 'day', 'week', 'month', or 'year'.
perplexity-web-search-params.ts
import { gateway, streamText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'openai/gpt-6-astra',
    prompt,
    tools: {
      perplexity_search: gateway.tools.perplexitySearch({
        maxResults: 5,
        maxTokens: 50000,
        maxTokensPerPage: 2048,
        country: 'US',
        searchLanguageFilter: ['en'],
        searchDomainFilter: ['reuters.com', 'bbc.com', 'nytimes.com'],
        searchRecencyFilter: 'week',
      }),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
perplexity-web-search-params.ts
import { gateway, generateText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'openai/gpt-6-astra',
    prompt,
    tools: {
      perplexity_search: gateway.tools.perplexitySearch({
        maxResults: 5,
        maxTokens: 50000,
        maxTokensPerPage: 2048,
        country: 'US',
        searchLanguageFilter: ['en'],
        searchDomainFilter: ['reuters.com', 'bbc.com', 'nytimes.com'],
        searchRecencyFilter: 'week',
      }),
    },
  });
 
  return Response.json({ text });
}

The exaSearch tool can be used with any model regardless of the model provider or creator. Exa returns web results and extracted content for agent workflows that need current information, domain filters, date filters, and token-efficient excerpts.

To use Exa Search, import gateway from ai and pass gateway.tools.exaSearch() to the tools parameter. When the model needs current information, it calls the tool and AI Gateway routes the request to Exa's Search API.

Exa web search requests are charged at $7 per 1,000 requests. Each request includes up to 10 results. Additional requested results beyond 10 are charged at $1 per 1,000 additional results.

exa-web-search.ts
import { gateway, streamText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'openai/gpt-6-astra', // Works with any model
    prompt,
    tools: {
      exa_search: gateway.tools.exaSearch(),
    },
  });
 
  for await (const part of result.stream) {
    if (part.type === 'text-delta') {
      process.stdout.write(part.text);
    } else if (part.type === 'tool-call') {
      console.log('Tool call:', part.toolName);
    } else if (part.type === 'tool-result') {
      console.log('Search results received');
    }
  }
 
  return result.toUIMessageStreamResponse();
}
exa-web-search.ts
import { gateway, generateText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'openai/gpt-6-astra', // Works with any model
    prompt,
    tools: {
      exa_search: gateway.tools.exaSearch(),
    },
  });
 
  return Response.json({ text });
}

You can configure the exaSearch tool with these parameters:

  • type: Search mode. Values: 'auto' (default), 'fast', or 'instant'.
  • numResults: Maximum number of results to return (1-100). Defaults to 10.
  • category: Content category. Values: 'company', 'people', 'research paper', 'news', 'personal site', or 'financial report'.
  • userLocation: Two-letter ISO country code, such as 'US', for location-aware search.
  • includeDomains: List of domains to restrict search results to.
  • excludeDomains: List of domains to exclude from search results.
  • startPublishedDate: Only return results published after this ISO 8601 date.
  • endPublishedDate: Only return results published before this ISO 8601 date.
  • contents: Controls extracted page content and freshness.
    • text: Return page text. You can set maxCharacters, includeHtmlTags, verbosity, includeSections, and excludeSections.
    • highlights: Return concise excerpts. You can set query and maxCharacters.
    • maxAgeHours: Maximum age of cached content in hours.
    • livecrawlTimeout: Timeout for live crawling in milliseconds.
    • subpages: Number of related subpages to crawl.
    • subpageTarget: Target page or pages for subpage crawling.
    • extras: Extract links or image links from pages with links and imageLinks.
exa-web-search-params.ts
import { gateway, streamText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'openai/gpt-6-astra',
    prompt,
    tools: {
      exa_search: gateway.tools.exaSearch({
        type: 'fast',
        numResults: 5,
        category: 'news',
        includeDomains: ['reuters.com', 'bbc.com', 'nytimes.com'],
        contents: {
          highlights: true,
          maxAgeHours: 24,
        },
      }),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
exa-web-search-params.ts
import { gateway, generateText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'openai/gpt-6-astra',
    prompt,
    tools: {
      exa_search: gateway.tools.exaSearch({
        type: 'fast',
        numResults: 5,
        category: 'news',
        includeDomains: ['reuters.com', 'bbc.com', 'nytimes.com'],
        contents: {
          highlights: true,
          maxAgeHours: 24,
        },
      }),
    },
  });
 
  return Response.json({ text });
}

This initial AI Gateway integration supports Exa's standard Search modes and content extraction controls. Deep synthesis modes and generated summaries are not exposed yet because they have separate pricing.

For more details on search parameters and API options, see the Exa Search API documentation.

The takoSearch tool searches the web and Tako's curated, real-time knowledge graph in one call. It returns token-efficient web excerpts and knowledge graph results backed by structured data, source attribution, and embed-ready visualizations. Use it when your agent needs access to authoritative, real-time finance, sports, weather, macroeconomics, and politics data or results from the web. Set sources.data.includeContents to return the raw structured data that backs the data results.

To use Tako Search, import gateway from ai and pass gateway.tools.takoSearch() to tools:

tako-search.ts
import { gateway, generateText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'openai/gpt-6-astra',
    prompt,
    tools: {
      tako_search: gateway.tools.takoSearch({
        effort: 'fast',
        sources: {
          data: { count: 2 },
          web: { count: 2 },
        },
      }),
    },
  });
 
  return Response.json({ text });
}

Tako Search costs $7 per 1,000 instant or fast requests and $12 per 1,000 deep requests. sources.data.includeContents adds variable export surcharges based on the requested rows and underlying data source. Guide agents to search without it first, then inspect each card's content.export_pricing before exporting. See Contents pricing for details.

You can configure the takoSearch tool with these parameters:

  • effort: Retrieval depth. Values: 'instant' (cached, lowest latency), 'fast' (default), or 'deep' (broader retrieval with reranking, billed at the higher request rate).
  • sources: Omit it to search both curated data and live web. When set, only the source keys present are searched.
  • sources.web: Configure web results.
    • count: Maximum web results (1-20).
    • includeDomains / excludeDomains: Only return, or drop, results from these bare domains. Up to 20 each.
    • publishedAfter / publishedBefore: Keep results published on or after, or on or before, this YYYY-MM-DD date.
    • highlights: Return query-relevant passages as each result's snippet instead of the opening text of the page. Defaults to true.
    • snippetMaxChars: Character cap on each result's snippet. Maximum 20,000.
    • includeContents: Inline each page's extracted full text. articleContentMaxChars caps it, defaulting to 30,000 (maximum 1,000,000).
  • sources.data: Configure knowledge graph results.
    • count: Maximum data results (1-20). Defaults to 5. The free row allowance is per result, so raising count raises both the rows you get and the baseline you pay.
    • includeContents: Inline each card's underlying rows in content.dataset as typed, unit-labeled columns. This is the parameter that adds row charges.
    • maxRows: Row cap per result. Omit it and each result returns the free allowance only (20 rows) with truncated: true. You pay for rows actually returned, so a value above the series length costs the series length. Each card reports its own ceiling in content.export_pricing.max_rows_ceiling.
    • contentFormat: Serialization for inlined card data. Values: 'json_compact' (default), 'json_records', 'csv', or 'card_json'.
    • nodeIds: Data Graph node IDs to prioritize. Up to 20.
    • strict: Only return cards matching nodeIds. Requires at least one nodeIds value.
  • includeRelated: Number of related search suggestions to return (1-20).
  • location: End-user { latitude, longitude } coordinates for localized results.
  • countryCode: ISO 3166-1 alpha-2 country code, such as 'US'.
  • locale: BCP-47 locale, such as 'en-US'.
  • timezone: IANA timezone, such as 'America/New_York'.

AI Gateway applies options you set in takoSearch() as developer defaults, overriding model-generated values.

For the complete input and output schema, see the AI SDK AI Gateway reference. For search behavior, data cards, and Tako-specific options, see the Tako Search documentation.

The browserbaseSearch tool can be used with any model regardless of the model provider or creator. Browserbase provides fast, structured web discovery through its Search API, returning result titles, URLs, and metadata without opening a browser session. Use it when you want the model to find pages, then pair it with Browserbase Fetch to read one.

To use Browserbase Search, import gateway from ai and pass gateway.tools.browserbaseSearch() to tools:

Browserbase Search costs $7 per 1,000 requests, regardless of how many results you request.

browserbase-search.ts
import { gateway, streamText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'openai/gpt-5.6-sol', // Works with any model
    prompt,
    tools: {
      browserbase_search: gateway.tools.browserbaseSearch({ numResults: 3 }),
    },
  });
 
  for await (const part of result.fullStream) {
    if (part.type === 'text-delta') {
      process.stdout.write(part.text);
    } else if (part.type === 'tool-result') {
      console.log('Search results received');
    }
  }
 
  return result.toUIMessageStreamResponse();
}
browserbase-search.ts
import { gateway, generateText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'openai/gpt-5.6-sol', // Works with any model
    prompt,
    tools: {
      browserbase_search: gateway.tools.browserbaseSearch({ numResults: 3 }),
    },
  });
 
  return Response.json({ text });
}

You can configure the browserbaseSearch tool with this parameter:

  • numResults: Maximum number of results to return (1-25). Defaults to 10.

The model supplies the query, which must be between 1 and 200 characters.

AI Gateway applies options you set in browserbaseSearch() as developer defaults, overriding model-generated values.

For the complete input and output schema, see the AI SDK AI Gateway reference. For search behavior and Browserbase-specific options, see the Browserbase Search documentation.

The browserbaseFetch tool retrieves the contents of a URL the model already has, rather than searching for one. It uses Browserbase's Fetch API, a lightweight option for pages that do not need JavaScript execution or browser interaction, and can return the raw response body, Markdown, or structured JSON extracted against a JSON Schema you supply. Pair it with Browserbase Search when the model needs to find the page first.

To use Browserbase Fetch, import gateway from ai and pass gateway.tools.browserbaseFetch() to tools:

Browserbase Fetch is priced per option. A plain raw fetch costs $1 per 1,000 requests. Routing through proxies adds $3 per 1,000, and extraction (markdown or json format) adds another $3 per 1,000, so a proxied extraction costs $7 per 1,000. The surcharges are billed from the options actually sent upstream and reported in the gateway usage metadata.

browserbase-fetch.ts
import { gateway, streamText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'openai/gpt-5.6-sol', // Works with any model
    prompt,
    tools: {
      browserbase_fetch: gateway.tools.browserbaseFetch({ format: 'markdown' }),
    },
  });
 
  for await (const part of result.fullStream) {
    if (part.type === 'text-delta') {
      process.stdout.write(part.text);
    } else if (part.type === 'tool-result') {
      console.log('Page contents received');
    }
  }
 
  return result.toUIMessageStreamResponse();
}
browserbase-fetch.ts
import { gateway, generateText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'openai/gpt-5.6-sol', // Works with any model
    prompt,
    tools: {
      browserbase_fetch: gateway.tools.browserbaseFetch({
        allowRedirects: true,
        format: 'json',
        schema: {
          type: 'object',
          properties: {
            title: { type: 'string' },
            summary: { type: 'string' },
          },
          required: ['title', 'summary'],
        },
      }),
    },
  });
 
  return Response.json({ text });
}

You can configure the browserbaseFetch tool with these parameters:

  • format: Output format for the fetched content. Values: 'raw' (the upstream response body unchanged), 'markdown' (the page converted to Markdown), or 'json' (structured extraction, which requires schema). Browserbase defaults to 'raw' when the request omits format. 'markdown' and 'json' run extraction and are billed above the base rate.
  • schema: JSON Schema describing the object to extract. Only valid when format is 'json'. Omitting schema with 'json' passes AI Gateway validation but causes an error from Browserbase.
  • allowRedirects: Follow HTTP redirects. Defaults to false.
  • proxies: Route the request through Browserbase's proxy network. Defaults to false. Billed above the base rate.
  • allowInsecureSsl: Skip TLS certificate verification. Defaults to false.

The model supplies the url, which must be an absolute http or https URL. It can also supply format, allow_redirects, and proxies unless you set them.

Because format and proxies each affect the price, a model that selects proxies: true together with markdown or json output raises the cost of a call from $1 to $7 per 1,000. Set either option in browserbaseFetch() to pin it and cap the per-call cost.

schema and allowInsecureSsl are developer configuration only. A model cannot set them, so a prompt-injected value is discarded before the fetch runs.

AI Gateway applies options you set in browserbaseFetch() as developer defaults, overriding model-generated values.

If Browserbase extraction is unavailable or its quota is exhausted, 'markdown' and 'json' requests can return configuration_error with status code 403 or

  1. Retry with format: 'raw' and omit schema.

For the complete input and output schema, see the AI SDK AI Gateway reference. For fetch behavior and Browserbase-specific options, see the Browserbase Fetch documentation.

Use AI Gateway server tools from the OpenAI-compatible Chat Completions API. AI Gateway executes the search, adds the results to the model context, and returns the final answer in the same response.

Choose the search provider by adding one server tool to tools:

Tool typeSearch providerRequired config field
vercel:browserbase_searchBrowserbasequery
vercel:exa_searchExaquery
vercel:parallel_searchParallelobjective
vercel:perplexity_searchPerplexityquery
vercel:tako_searchTakoquery

Browserbase Fetch is also available as a server tool. Add vercel:browserbase_fetch with a url config field to fetch a page instead of searching for one.

Put static tool settings in config. Use snake case for config keys. AI Gateway uses these values as developer defaults and overrides model-generated values.

For example, this request requires an Exa search before the model answers:

curl https://ai-gateway.vercel.sh/v1/chat/completions \
  -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-6-astra",
    "messages": [
      {
        "role": "user",
        "content": "Summarize Vercel AI Gateway in one sentence."
      }
    ],
    "tools": [
      {
        "type": "vercel:exa_search",
        "config": {
          "query": "Vercel AI Gateway",
          "type": "instant",
          "num_results": 1
        }
      }
    ],
    "tool_choice": "required",
    "max_tokens": 128
  }'

Use tool_choice: "auto" to let the model decide whether to search. Use "required" when the request must make an initial search. Do not use a named function tool_choice for server tools.

AI Gateway executes server tools internally. The final Chat Completions response has finish_reason: "stop" and does not include client-facing tool_calls or raw search results. Inspect choices[0].message.provider_metadata.gateway.gatewayToolCalls for successful search-call counts and the gateway metadata for aggregate cost.

Use distinct names for your own function tools. Do not define a client function named browserbase_fetch, browserbase_search, exa_search, parallel_search, perplexity_search, or tako_search in a request that includes the corresponding AI Gateway server tool.

The parallelSearch tool can be used with any model regardless of the model provider or creator. Parallel AI provides LLM-optimized web search that extracts relevant excerpts from web pages, making it ideal for research tasks and information retrieval.

To use Parallel Search, import gateway from ai and pass gateway.tools.parallelSearch() to the tools parameter. When the model needs current information, it calls the tool and AI Gateway routes the request to Parallel's search API.

Parallel web search requests are charged at $5 per 1,000 requests (includes up to 10 results per request). Additional results beyond 10 are charged at $1 per 1,000 additional results.

parallel-web-search.ts
import { gateway, streamText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'anthropic/claude-opus-5', // Works with any model
    prompt,
    tools: {
      parallel_search: gateway.tools.parallelSearch(),
    },
  });
 
  for await (const part of result.stream) {
    if (part.type === 'text-delta') {
      process.stdout.write(part.text);
    } else if (part.type === 'tool-call') {
      console.log('Tool call:', part.toolName);
    } else if (part.type === 'tool-result') {
      console.log('Search results received');
    }
  }
 
  return result.toUIMessageStreamResponse();
}
parallel-web-search.ts
import { gateway, generateText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'anthropic/claude-opus-5', // Works with any model
    prompt,
    tools: {
      parallel_search: gateway.tools.parallelSearch(),
    },
  });
 
  return Response.json({ text });
}

You can configure the parallelSearch tool with these parameters:

  • mode: Search mode preset. Values: 'one-shot' (comprehensive results with longer excerpts, default) or 'agentic' (concise, token-efficient results for multi-step workflows).
  • maxResults: Maximum number of results to return (1-20). Defaults to 10.
  • searchQueries: Optional list of keyword search queries to supplement the objective.
  • sourcePolicy: Controls which domains and date ranges to include or exclude.
    • includeDomains: List of domains to restrict search results to (e.g., ['arxiv.org', 'nature.com']).
    • excludeDomains: List of domains to exclude from search results.
    • afterDate: Only return results published after this date (format: YYYY-MM-DD).
  • excerpts: Controls result excerpt length.
    • maxCharsPerResult: Maximum characters per result excerpt.
    • maxCharsTotal: Maximum total characters across all result excerpts.
  • fetchPolicy: Controls content freshness.
    • maxAgeSeconds: Maximum age of cached content in seconds for time-sensitive queries.
parallel-web-search-params.ts
import { gateway, streamText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'anthropic/claude-opus-5',
    prompt,
    tools: {
      parallel_search: gateway.tools.parallelSearch({
        mode: 'one-shot',
        maxResults: 5,
        sourcePolicy: {
          includeDomains: ['arxiv.org', 'nature.com', 'science.org'],
          afterDate: '2025-01-01',
        },
        excerpts: {
          maxCharsPerResult: 5000,
        },
      }),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
parallel-web-search-params.ts
import { gateway, generateText } from 'ai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'anthropic/claude-opus-5',
    prompt,
    tools: {
      parallel_search: gateway.tools.parallelSearch({
        mode: 'one-shot',
        maxResults: 15,
        sourcePolicy: {
          includeDomains: ['arxiv.org', 'nature.com', 'science.org'],
          afterDate: '2025-01-01',
        },
        excerpts: {
          maxCharsPerResult: 5000,
        },
      }),
    },
  });
 
  return Response.json({ text });
}

For more details on search parameters and API options, see the Parallel AI Search documentation.

Use native web search tools from Anthropic, OpenAI, Google, or SpaceXAI. These tools are optimized for their respective providers and may offer additional features.

Pricing for provider-specific web search tools depends on the model you use. See the Web Search price column on the model detail pages for exact pricing.

For Anthropic models, you can use the native web search tool provided by the @ai-sdk/anthropic package. Import anthropic from @ai-sdk/anthropic and pass anthropic.tools.webSearch_20250305() to the tools parameter. The tool returns source information including titles and URLs, which you can access through the source event type in the stream.

anthropic-web-search.ts
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'anthropic/claude-opus-5',
    prompt,
    tools: {
      web_search: anthropic.tools.webSearch_20250305(),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
anthropic-web-search.ts
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'anthropic/claude-opus-5',
    prompt,
    tools: {
      web_search: anthropic.tools.webSearch_20250305(),
    },
  });
 
  return Response.json({ text });
}

The following parameters are supported:

  • maxUses: Maximum number of web searches Claude can perform during the conversation.
  • allowedDomains: Optional list of domains Claude is allowed to search. If provided, searches will be restricted to these domains.
  • blockedDomains: Optional list of domains Claude should avoid when searching.
  • userLocation: Optional user location information to provide geographically relevant search results.
anthropic-web-search-params.ts
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'anthropic/claude-opus-5',
    prompt,
    tools: {
      web_search: anthropic.tools.webSearch_20250305({
        maxUses: 3,
        allowedDomains: ['techcrunch.com', 'wired.com'],
        blockedDomains: ['example-spam-site.com'],
        userLocation: {
          type: 'approximate',
          country: 'US',
          region: 'California',
          city: 'San Francisco',
          timezone: 'America/Los_Angeles',
        },
      }),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
anthropic-web-search-params.ts
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'anthropic/claude-opus-5',
    prompt,
    tools: {
      web_search: anthropic.tools.webSearch_20250305({
        maxUses: 3,
        allowedDomains: ['techcrunch.com', 'wired.com'],
        blockedDomains: ['example-spam-site.com'],
        userLocation: {
          type: 'approximate',
          country: 'US',
          region: 'California',
          city: 'San Francisco',
          timezone: 'America/Los_Angeles',
        },
      }),
    },
  });
 
  return Response.json({ text });
}

For more details on using the Anthropic Messages API directly, see the Anthropic advanced features documentation.

For OpenAI models, you can use the native web search tool provided by the @ai-sdk/openai package. Import openai from @ai-sdk/openai and pass openai.tools.webSearch({}) to the tools parameter.

openai-web-search.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'openai/gpt-6-astra',
    prompt,
    tools: {
      web_search: openai.tools.webSearch({}),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
openai-web-search.ts
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'openai/gpt-6-astra',
    prompt,
    tools: {
      web_search: openai.tools.webSearch({}),
    },
  });
 
  return Response.json({ text });
}

For Google Gemini models, you can use Grounding with Google Search. Google offers two providers: Google Vertex and Google AI Studio. Choose the one that matches your setup. The Google Search tool returns source information including titles and URLs, which you can access through the source event type in the stream.

Import vertex from @ai-sdk/google-vertex and pass vertex.tools.googleSearch({}) to the tools parameter. For users who need zero data retention, see Enterprise web search below.

google-vertex-web-search.ts
import { streamText } from 'ai';
import { vertex } from '@ai-sdk/google-vertex';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'google/gemini-3.1-pro-preview',
    prompt,
    tools: {
      google_search: vertex.tools.googleSearch({}),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
google-vertex-web-search.ts
import { generateText } from 'ai';
import { vertex } from '@ai-sdk/google-vertex';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'google/gemini-3.1-pro-preview',
    prompt,
    tools: {
      google_search: vertex.tools.googleSearch({}),
    },
  });
 
  return Response.json({ text });
}

For users who need zero data retention, you can use Enterprise Web Grounding instead. Pass vertex.tools.enterpriseWebSearch({}) to the tools parameter.

Enterprise web search uses indexed content that is a subset of the full web. Use Google search for more up-to-date and comprehensive results.

enterprise-web-grounding.ts
import { streamText } from 'ai';
import { vertex } from '@ai-sdk/google-vertex';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'google/gemini-3.1-pro-preview',
    prompt,
    tools: {
      enterprise_web_search: vertex.tools.enterpriseWebSearch({}),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
enterprise-web-grounding.ts
import { generateText } from 'ai';
import { vertex } from '@ai-sdk/google-vertex';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'google/gemini-3.1-pro-preview',
    prompt,
    tools: {
      enterprise_web_search: vertex.tools.enterpriseWebSearch({}),
    },
  });
 
  return Response.json({ text });
}

Import google from @ai-sdk/google and pass google.tools.googleSearch({}) to the tools parameter.

google-ai-studio-web-search.ts
import { streamText } from 'ai';
import { google } from '@ai-sdk/google';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'google/gemini-3.1-pro-preview',
    prompt,
    tools: {
      google_search: google.tools.googleSearch({}),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
google-ai-studio-web-search.ts
import { generateText } from 'ai';
import { google } from '@ai-sdk/google';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'google/gemini-3.1-pro-preview',
    prompt,
    tools: {
      google_search: google.tools.googleSearch({}),
    },
  });
 
  return Response.json({ text });
}

For SpaceXAI Grok models, you can use the native web search tool provided by the @ai-sdk/xai package. Import xai from @ai-sdk/xai and pass xai.tools.webSearch({}) to the tools parameter. The tool returns source information including titles and URLs, which you can access through the source event type in the stream.

xai-web-search.ts
import { streamText } from 'ai';
import { xai } from '@ai-sdk/xai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'spacexai/grok-4.20-non-reasoning',
    prompt,
    tools: {
      web_search: xai.tools.webSearch({}),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
xai-web-search.ts
import { generateText } from 'ai';
import { xai } from '@ai-sdk/xai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'spacexai/grok-4.20-non-reasoning',
    prompt,
    tools: {
      web_search: xai.tools.webSearch({}),
    },
  });
 
  return Response.json({ text });
}

The following parameters are supported:

  • allowedDomains: Optional list of domains to restrict searches to (max 5). Cannot be combined with excludedDomains.
  • excludedDomains: Optional list of domains to exclude from searches (max 5). Cannot be combined with allowedDomains.
  • enableImageSearch: Let the model use image search as a separate mode.
  • enableImageUnderstanding: Let the model analyze images found during the search.
xai-web-search-params.ts
import { streamText } from 'ai';
import { xai } from '@ai-sdk/xai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const result = streamText({
    model: 'spacexai/grok-4.20-non-reasoning',
    prompt,
    tools: {
      web_search: xai.tools.webSearch({
        allowedDomains: ['arxiv.org', 'openai.com'],
        enableImageUnderstanding: true,
      }),
    },
  });
 
  return result.toUIMessageStreamResponse();
}
xai-web-search-params.ts
import { generateText } from 'ai';
import { xai } from '@ai-sdk/xai';
 
export async function POST(request: Request) {
  const { prompt } = await request.json();
 
  const { text } = await generateText({
    model: 'spacexai/grok-4.20-non-reasoning',
    prompt,
    tools: {
      web_search: xai.tools.webSearch({
        allowedDomains: ['arxiv.org', 'openai.com'],
        enableImageUnderstanding: true,
      }),
    },
  });
 
  return Response.json({ text });
}
Last updated September 8, 2026

Was this helpful?

supported.