> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arouter.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Search

> Ground any model's responses with real-time web data. Use the web plugin or :online suffix — or migrate to Server Tools for model-driven search.

<Warning>
  The `web` plugin and `:online` model suffix are **deprecated**. They continue to work, but new integrations should use [Server Tools](/en/guides/features/server-tools/overview) instead. Server Tools give the model control over when to search, return structured citations, and support more engines.
</Warning>

ARouter supports web search grounding for any model — not just models with built-in search. Activate it by appending `:online` to the model slug or by passing the `web` plugin in your request body.

## Quick Start

Append `:online` to any model slug to enable web search:

```json theme={null}
{"model": "openai/gpt-5.4:online"}
```

Or use the `plugins` array explicitly:

```json theme={null}
{
  "model": "openai/gpt-5.4",
  "plugins": [{"id": "web"}]
}
```

Both approaches are equivalent.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import OpenAI from "openai";

    const client = new OpenAI({
      baseURL: "https://api.arouter.ai/v1",
      apiKey: "lr_live_xxxx",
    });

    const response = await client.chat.completions.create({
      model: "openai/gpt-5.4:online",
      messages: [{ role: "user", content: "What happened in tech news today?" }],
    });

    console.log(response.choices[0].message.content);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from openai import OpenAI

    client = OpenAI(
        base_url="https://api.arouter.ai/v1",
        api_key="lr_live_xxxx",
    )

    response = client.chat.completions.create(
        model="openai/gpt-5.4:online",
        messages=[{"role": "user", "content": "What happened in tech news today?"}],
    )

    print(response.choices[0].message.content)
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl https://api.arouter.ai/v1/chat/completions \
      -H "Authorization: Bearer lr_live_xxxx" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "openai/gpt-5.4:online",
        "messages": [{"role": "user", "content": "What happened in tech news today?"}]
      }'
    ```
  </Tab>
</Tabs>

***

## Parsing Web Search Results

Web search results are standardized across all providers and returned in the `annotations` field of the response message:

```json theme={null}
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "Here's the latest news I found: ...",
        "annotations": [
          {
            "type": "url_citation",
            "url_citation": {
              "url": "https://www.example.com/article",
              "title": "Article title",
              "content": "Excerpt from the article",
              "start_index": 100,
              "end_index": 200
            }
          }
        ]
      }
    }
  ]
}
```

***

## Customizing the Web Plugin

Pass a `plugins` array to configure web search behavior:

```json theme={null}
{
  "model": "openai/gpt-5.4",
  "messages": [{"role": "user", "content": "Latest AI research"}],
  "plugins": [
    {
      "id": "web",
      "max_results": 3,
      "search_prompt": "A web search was conducted. Use these results to answer:"
    }
  ]
}
```

| Parameter         | Type       | Default   | Description                                         |
| ----------------- | ---------- | --------- | --------------------------------------------------- |
| `id`              | `string`   | —         | Must be `"web"`                                     |
| `max_results`     | `number`   | `5`       | Maximum number of search results to retrieve        |
| `search_prompt`   | `string`   | See below | Prompt injected before search results               |
| `engine`          | `string`   | auto      | Search engine: `"native"`, `"exa"`, or `"parallel"` |
| `include_domains` | `string[]` | —         | Only return results from these domains              |
| `exclude_domains` | `string[]` | —         | Exclude results from these domains                  |

**Default search prompt:**

```
A web search was conducted on {date}. Incorporate the following web search results into your response.

IMPORTANT: Cite them using markdown links named using the domain of the source.
Example: [nytimes.com](https://nytimes.com/some-page).
```

***

## Domain Filtering

Restrict which domains appear in search results:

```json theme={null}
{
  "model": "openai/gpt-5.4",
  "plugins": [
    {
      "id": "web",
      "include_domains": ["arxiv.org", "*.openai.com"],
      "exclude_domains": ["reddit.com"]
    }
  ]
}
```

Both `include_domains` and `exclude_domains` accept domain strings with optional wildcards (`*.substack.com`) and path filtering (`openai.com/blog`).

### Engine Compatibility

| Engine             | `include_domains` | `exclude_domains`                     |
| ------------------ | ----------------- | ------------------------------------- |
| Exa                | Yes               | Yes                                   |
| Parallel           | Yes               | Yes (mutually exclusive with include) |
| Native (Anthropic) | Yes               | Yes (mutually exclusive)              |
| Native (OpenAI)    | Yes               | Ignored                               |
| Native (xAI)       | Yes               | Yes (mutually exclusive, max 5 each)  |

***

## Engine Selection

ARouter supports multiple search engines via the `engine` parameter:

| Engine       | Description                                                                |
| ------------ | -------------------------------------------------------------------------- |
| (unset)      | Use native search if the provider supports it, otherwise fall back to Exa  |
| `"native"`   | Always use the model provider's built-in search (may error if unsupported) |
| `"exa"`      | Use Exa's neural+keyword hybrid search                                     |
| `"parallel"` | Use Parallel's search API                                                  |

### Default Behavior

* OpenAI, Anthropic, xAI models → native search by default
* All other models → Exa search

```json theme={null}
{
  "model": "google/gemini-2.5-flash:online",
  "plugins": [{"id": "web", "engine": "exa", "max_results": 3}]
}
```

***

## Search Context Size

For models that support native search, control how much search context is retrieved:

```json theme={null}
{
  "model": "openai/gpt-5.4:online",
  "messages": [{"role": "user", "content": "Latest quantum computing research"}],
  "web_search_options": {
    "search_context_size": "high"
  }
}
```

| Value      | Description                                     |
| ---------- | ----------------------------------------------- |
| `"low"`    | Minimal search context — fast and cheap         |
| `"medium"` | Moderate context — suitable for most queries    |
| `"high"`   | Extensive context — ideal for detailed research |

***

## Pricing

Web search incurs additional charges beyond normal LLM token costs.

### Exa Search

* \*\*$4 per 1,000 results** (default `max_results: 5` = $0.02 per request maximum)
* Charged from your ARouter credits

### Native Search

Pricing depends on the provider and search context size. Refer to each provider's documentation:

* [OpenAI web search pricing](https://platform.openai.com/docs/pricing)
* [Anthropic search pricing](https://docs.anthropic.com)
* [xAI pricing](https://docs.x.ai/docs/models)

### Parallel Search

* **\$4 per 1,000 results** (same rate as Exa)
* Charged from your ARouter credits

<Note>
  Web search charges apply even when used with `:free` model variants.
</Note>

***

## Combining with Free Models

You can combine `:online` with `:free`:

```json theme={null}
{"model": "meta-llama/llama-4-maverick:free:online"}
```

The LLM inference is free but web search still incurs search costs.

***

## xAI X Search Filters

When using xAI models with web search, ARouter automatically includes X/Twitter search alongside web search. Control X search behavior with the `x_search_filter` parameter:

```json theme={null}
{
  "model": "x-ai/grok-4.20:online",
  "messages": [{"role": "user", "content": "What are people saying about AI?"}],
  "x_search_filter": {
    "allowed_x_handles": ["sama", "karpathy"],
    "from_date": "2025-01-01",
    "to_date": "2025-12-31"
  }
}
```

| Parameter                    | Type       | Description                                    |
| ---------------------------- | ---------- | ---------------------------------------------- |
| `allowed_x_handles`          | `string[]` | Only include posts from these handles (max 10) |
| `excluded_x_handles`         | `string[]` | Exclude posts from these handles (max 10)      |
| `from_date`                  | `string`   | Start date (ISO 8601, e.g. `"2025-01-01"`)     |
| `to_date`                    | `string`   | End date (ISO 8601)                            |
| `enable_image_understanding` | `boolean`  | Analyze images in posts                        |
| `enable_video_understanding` | `boolean`  | Analyze videos in posts                        |

<Note>
  `allowed_x_handles` and `excluded_x_handles` are mutually exclusive. If both are set, the filter is silently dropped.
</Note>

***

## Practical Examples

### Research Assistant

```python theme={null}
response = client.chat.completions.create(
    model="anthropic/claude-sonnet-4.6:online",
    messages=[{
        "role": "user",
        "content": "What are the latest developments in fusion energy research?"
    }],
    extra_body={
        "plugins": [{
            "id": "web",
            "max_results": 5,
            "include_domains": ["nature.com", "science.org", "arxiv.org"]
        }]
    }
)
```

### News Summarization

```python theme={null}
response = client.chat.completions.create(
    model="openai/gpt-5.4:online",
    messages=[{
        "role": "user",
        "content": "Summarize today's top 3 technology news stories"
    }],
    extra_body={
        "plugins": [{
            "id": "web",
            "max_results": 10,
            "exclude_domains": ["reddit.com", "twitter.com"]
        }]
    }
)
```

## Migrating to Server Tools

The recommended replacement for the `web` plugin and `:online` suffix is the `openrouter:web_search` Server Tool. Server Tools are invoked by the model itself when it determines a search is needed, rather than being forced on every request.

See [Server Tools — Web Search](/en/guides/features/server-tools/overview) for the migration guide and full configuration reference.

See [Plugins Overview](/en/guides/features/plugins-overview) for the full list of available plugins.
