---
title: Web Search
product: vercel
url: /docs/ai-gateway/models-and-providers/web-search
canonical_url: "https://vercel.com/docs/ai-gateway/models-and-providers/web-search"
last_updated: 2026-06-30
type: conceptual
prerequisites:
  - /docs/ai-gateway/models-and-providers
  - /docs/ai-gateway
related:
  - /docs/ai-gateway/sdks-and-apis/anthropic-messages-api/advanced
summary: Enable AI models to search the web for current information using built-in tools through AI Gateway.
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

# Web Search

AI Gateway provides built-in web search capabilities that allow AI models to access current information from the web. This is useful when you need up-to-date information that may not be in the model's training data.

AI Gateway supports two types of web search:

- **Search for all providers**: Use [Perplexity Search](#using-perplexity-search), [Exa Search](#using-exa-search), or [Parallel Search](#using-parallel-search) with any model regardless of provider. This gives you consistent web search behavior across different models.
- **Provider-specific search**: Use native web search tools from [Anthropic](#anthropic-web-search), [OpenAI](#openai-web-search), or [Google](#google-web-search). These tools are optimized for their respective providers and may offer [additional features](#provider-specific-search).

## Using Perplexity Search

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](https://docs.perplexity.ai/guides/search-quickstart).

> **💡 Note:** Perplexity web search requests are charged at $5 per 1,000 requests. See
> [Perplexity's pricing](https://docs.perplexity.ai/getting-started/pricing) for
> more details.

#### streamText

```typescript filename="perplexity-web-search.ts" {10-12}
import { gateway, streamText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const result = streamText({
    model: 'openai/gpt-5.5', // Works with any model, not just Perplexity
    prompt,
    tools: {
      perplexity_search: gateway.tools.perplexitySearch(),
    },
  });

  for await (const part of result.fullStream) {
    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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="perplexity-web-search.ts" {10-12}
import { gateway, generateText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const { text } = await generateText({
    model: 'openai/gpt-5.5', // Works with any model, not just Perplexity
    prompt,
    tools: {
      perplexity_search: gateway.tools.perplexitySearch(),
    },
  });

  return Response.json({ text });
}
```

### Perplexity parameters

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'`.

#### streamText

```typescript filename="perplexity-web-search-params.ts" {10-20}
import { gateway, streamText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const result = streamText({
    model: 'openai/gpt-5.5',
    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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="perplexity-web-search-params.ts" {10-20}
import { gateway, generateText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const { text } = await generateText({
    model: 'openai/gpt-5.5',
    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 });
}
```

## Using Exa Search

The `exaSearch` tool can be used with any model regardless of the model provider or creator. [Exa](https://exa.ai/) 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](https://exa.ai/docs/reference/search-api-guide-for-coding-agents).

> **💡 Note:** 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.

#### streamText

```typescript filename="exa-web-search.ts" {10-12}
import { gateway, streamText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const result = streamText({
    model: 'openai/gpt-5.5', // Works with any model
    prompt,
    tools: {
      exa_search: gateway.tools.exaSearch(),
    },
  });

  for await (const part of result.fullStream) {
    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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="exa-web-search.ts" {10-12}
import { gateway, generateText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const { text } = await generateText({
    model: 'openai/gpt-5.5', // Works with any model
    prompt,
    tools: {
      exa_search: gateway.tools.exaSearch(),
    },
  });

  return Response.json({ text });
}
```

### Exa parameters

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`.

#### streamText

```typescript filename="exa-web-search-params.ts" {10-21}
import { gateway, streamText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const result = streamText({
    model: 'openai/gpt-5.5',
    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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="exa-web-search-params.ts" {10-21}
import { gateway, generateText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const { text } = await generateText({
    model: 'openai/gpt-5.5',
    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](https://exa.ai/docs/reference/search-api-guide-for-coding-agents).

## Using Parallel Search

The `parallelSearch` tool can be used with any model regardless of the model provider or creator. [Parallel AI](https://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](https://docs.parallel.ai/search/search-quickstart).

> **💡 Note:** 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.

#### streamText

```typescript filename="parallel-web-search.ts" {10-12}
import { gateway, streamText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const result = streamText({
    model: 'anthropic/claude-opus-4.8', // Works with any model
    prompt,
    tools: {
      parallel_search: gateway.tools.parallelSearch(),
    },
  });

  for await (const part of result.fullStream) {
    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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="parallel-web-search.ts" {10-12}
import { gateway, generateText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const { text } = await generateText({
    model: 'anthropic/claude-opus-4.8', // Works with any model
    prompt,
    tools: {
      parallel_search: gateway.tools.parallelSearch(),
    },
  });

  return Response.json({ text });
}
```

### Parallel parameters

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.

#### streamText

```typescript filename="parallel-web-search-params.ts" {10-22}
import { gateway, streamText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const result = streamText({
    model: 'anthropic/claude-opus-4.8',
    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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="parallel-web-search-params.ts" {10-22}
import { gateway, generateText } from 'ai';

export async function POST(request: Request) {
  const { prompt } = await request.json();

  const { text } = await generateText({
    model: 'anthropic/claude-opus-4.8',
    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](https://docs.parallel.ai/search/search-quickstart).

## Provider-specific search

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

> **💡 Note:** Pricing for provider-specific web search tools depends on the model you use.
> See the Web Search price column on the [model detail
> pages](https://vercel.com/ai-gateway/models) for exact pricing.

### Anthropic web search

For Anthropic models, you can use the native [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/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.

#### streamText

```typescript filename="anthropic-web-search.ts" {10-12}
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-4.8',
    prompt,
    tools: {
      web_search: anthropic.tools.webSearch_20250305(),
    },
  });

  return result.toDataStreamResponse();
}
```

#### generateText

```typescript filename="anthropic-web-search.ts" {10-12}
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-4.8',
    prompt,
    tools: {
      web_search: anthropic.tools.webSearch_20250305(),
    },
  });

  return Response.json({ text });
}
```

#### Anthropic parameters

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.

#### streamText

```typescript filename="anthropic-web-search-params.ts" {10-23}
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-4.8',
    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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="anthropic-web-search-params.ts" {10-23}
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-4.8',
    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](/docs/ai-gateway/sdks-and-apis/anthropic-messages-api/advanced#web-search) documentation.

### OpenAI web search

For OpenAI models, you can use the native [web search tool](https://platform.openai.com/docs/guides/tools-web-search) provided by the `@ai-sdk/openai` package. Import `openai` from `@ai-sdk/openai` and pass `openai.tools.webSearch({})` to the `tools` parameter.

#### streamText

```typescript filename="openai-web-search.ts" {10-12}
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-5.5',
    prompt,
    tools: {
      web_search: openai.tools.webSearch({}),
    },
  });

  return result.toDataStreamResponse();
}
```

#### generateText

```typescript filename="openai-web-search.ts" {10-12}
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-5.5',
    prompt,
    tools: {
      web_search: openai.tools.webSearch({}),
    },
  });

  return Response.json({ text });
}
```

### Google web search

For Google Gemini models, you can use [Grounding with Google Search](https://docs.cloud.google.com/vertex-ai/generative-ai/docs/grounding/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.

#### Google Vertex

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](#enterprise-web-search) below.

#### streamText

```typescript filename="google-vertex-web-search.ts" {10-12}
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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="google-vertex-web-search.ts" {10-12}
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 });
}
```

#### Enterprise web search

For users who need zero data retention, you can use [Enterprise Web Grounding](https://docs.cloud.google.com/vertex-ai/generative-ai/docs/grounding/web-grounding-enterprise) instead. Pass `vertex.tools.enterpriseWebSearch({})` to the `tools` parameter.

> **💡 Note:** 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.

#### streamText

```typescript filename="enterprise-web-grounding.ts" {10-12}
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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="enterprise-web-grounding.ts" {10-12}
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 });
}
```

#### Google AI Studio

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

#### streamText

```typescript filename="google-ai-studio-web-search.ts" {10-12}
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.toDataStreamResponse();
}
```

#### generateText

```typescript filename="google-ai-studio-web-search.ts" {10-12}
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 });
}
```


---

[View full sitemap](/docs/sitemap)
