docs/tools/web.md
The web_search tool searches the web using your configured provider and
returns results. Results are cached by query for 15 minutes (configurable).
OpenClaw also includes x_search for X (formerly Twitter) posts and
web_fetch for lightweight URL fetching. In this phase, web_fetch stays
local while web_search and x_search can use xAI Responses under the hood.
```javascript
await web_search({ query: "OpenClaw plugin SDK" });
```
For X posts, use:
```javascript
await x_search({ query: "dinner recipes" });
```
| Provider | Result style | Filters | API key |
|---|---|---|---|
| Brave | Structured snippets | Country, language, time, llm-context mode | BRAVE_API_KEY |
| DuckDuckGo | Structured snippets | -- | None (key-free) |
| Exa | Structured + extracted | Neural/keyword mode, date, content extraction | EXA_API_KEY |
| Firecrawl | Structured snippets | Via firecrawl_search tool | FIRECRAWL_API_KEY |
| Gemini | AI-synthesized + citations | -- | GEMINI_API_KEY |
| Grok | AI-synthesized + citations | -- | XAI_API_KEY |
| Kimi | AI-synthesized + citations; fails on ungrounded chat fallbacks | -- | KIMI_API_KEY / MOONSHOT_API_KEY |
| MiniMax Search | Structured snippets | Region (global / cn) | MINIMAX_CODE_PLAN_KEY / MINIMAX_CODING_API_KEY / MINIMAX_OAUTH_TOKEN |
| Ollama Web Search | Structured snippets | -- | None for signed-in local hosts; OLLAMA_API_KEY for direct https://ollama.com search |
| Perplexity | Structured snippets | Country, language, time, domains, content limits | PERPLEXITY_API_KEY / OPENROUTER_API_KEY |
| SearXNG | Structured snippets | Categories, language | None (self-hosted) |
| Tavily | Structured snippets | Via tavily_search tool | TAVILY_API_KEY |
Direct OpenAI Responses models use OpenAI's hosted web_search tool automatically when OpenClaw web search is enabled and no managed provider is pinned. This is provider-owned behavior in the bundled OpenAI plugin and only applies to native OpenAI API traffic, not OpenAI-compatible proxy base URLs or Azure routes. Set tools.web.search.provider to another provider such as brave to keep the managed web_search tool for OpenAI models, or set tools.web.search.enabled: false to disable both managed search and native OpenAI search.
Codex-capable models can optionally use the provider-native Responses web_search tool instead of OpenClaw's managed web_search function.
tools.web.search.openaiCodexopenai-codex/* or providers using api: "openai-codex-responses")web_search still applies to non-Codex modelsmode: "cached" is the default and recommended settingtools.web.search.enabled: false disables both managed and native search{
tools: {
web: {
search: {
enabled: true,
openaiCodex: {
enabled: true,
mode: "cached",
allowedDomains: ["example.com"],
contextSize: "high",
userLocation: {
country: "US",
city: "New York",
timezone: "America/New_York",
},
},
},
},
},
}
If native Codex search is enabled but the current model is not Codex-capable, OpenClaw keeps the normal managed web_search behavior.
Managed web_search provider calls use OpenClaw's guarded fetch path. For
trusted provider API hosts, OpenClaw allows Surge, Clash, and sing-box fake-IP
DNS answers in 198.18.0.0/15 and fc00::/7 only for that provider hostname.
Other private, loopback, link-local, and metadata destinations remain blocked.
This automatic allowance does not apply to arbitrary web_fetch URLs. For
web_fetch, enable tools.web.fetch.ssrfPolicy.allowRfc2544BenchmarkRange and
tools.web.fetch.ssrfPolicy.allowIpv6UniqueLocalRange explicitly only when your
trusted proxy owns those synthetic ranges.
Provider lists in docs and setup flows are alphabetical. Auto-detection keeps a separate precedence order.
If no provider is set, OpenClaw checks providers in this order and uses the
first one that is ready:
API-backed providers first:
BRAVE_API_KEY or plugins.entries.brave.config.webSearch.apiKey (order 10)MINIMAX_CODE_PLAN_KEY / MINIMAX_CODING_API_KEY / MINIMAX_OAUTH_TOKEN / MINIMAX_API_KEY or plugins.entries.minimax.config.webSearch.apiKey (order 15)plugins.entries.google.config.webSearch.apiKey, GEMINI_API_KEY, or models.providers.google.apiKey (order 20)XAI_API_KEY or plugins.entries.xai.config.webSearch.apiKey (order 30)KIMI_API_KEY / MOONSHOT_API_KEY or plugins.entries.moonshot.config.webSearch.apiKey (order 40)PERPLEXITY_API_KEY / OPENROUTER_API_KEY or plugins.entries.perplexity.config.webSearch.apiKey (order 50)FIRECRAWL_API_KEY or plugins.entries.firecrawl.config.webSearch.apiKey (order 60)EXA_API_KEY or plugins.entries.exa.config.webSearch.apiKey; optional plugins.entries.exa.config.webSearch.baseUrl overrides the Exa endpoint (order 65)TAVILY_API_KEY or plugins.entries.tavily.config.webSearch.apiKey (order 70)Key-free fallbacks after that:
ollama signin; can reuse Ollama provider bearer auth when the host needs it, and can call direct https://ollama.com search when configured with OLLAMA_API_KEY (order 110)SEARXNG_BASE_URL or plugins.entries.searxng.config.webSearch.baseUrl (order 200)If no provider is detected, it falls back to Brave (you will get a missing-key error prompting you to configure one).
<Note> All provider key fields support SecretRef objects. Plugin-scoped SecretRefs under `plugins.entries.<plugin>.config.webSearch.apiKey` are resolved for the bundled API-backed web search providers, including Brave, Exa, Firecrawl, Gemini, Grok, Kimi, MiniMax, Perplexity, and Tavily, whether the provider is picked explicitly via `tools.web.search.provider` or selected through auto-detect. In auto-detect mode, OpenClaw resolves only the selected provider key -- non-selected SecretRefs stay inactive, so you can keep multiple providers configured without paying resolution cost for the ones you are not using. </Note>{
tools: {
web: {
search: {
enabled: true, // default: true
provider: "brave", // or omit for auto-detection
maxResults: 5,
timeoutSeconds: 30,
cacheTtlMinutes: 15,
},
},
},
}
Provider-specific config (API keys, base URLs, modes) lives under
plugins.entries.<plugin>.config.webSearch.*. Gemini can also reuse
models.providers.google.apiKey and models.providers.google.baseUrl as lower-priority
fallbacks after its dedicated web-search config and GEMINI_API_KEY. See the
provider pages for examples.
tools.web.search.provider is validated against the web-search provider ids
declared by bundled and installed plugin manifests. A typo such as "brvae"
fails config validation instead of silently falling back to auto-detection. If a
configured provider only has stale plugin evidence, such as a leftover
plugins.entries.<plugin> block after uninstalling a third-party plugin,
OpenClaw keeps startup resilient and reports a warning so you can reinstall the
plugin or run openclaw doctor --fix to clean up the stale config.
web_fetch fallback provider selection is separate:
tools.web.fetch.providerweb_fetch can use installed plugin providers that declare
contracts.webFetchProviders; sandboxed fetches stay bundled-onlyplugins.entries.firecrawl.config.webFetch.*When you choose Kimi during openclaw onboard or
openclaw configure --section web, OpenClaw can also ask for:
https://api.moonshot.ai/v1 or https://api.moonshot.cn/v1)kimi-k2.6)For x_search, configure plugins.entries.xai.config.xSearch.*. It uses the
same XAI_API_KEY fallback as Grok web search.
Legacy tools.web.x_search.* config is auto-migrated by openclaw doctor --fix.
When you choose Grok during openclaw onboard or openclaw configure --section web,
OpenClaw can also offer optional x_search setup with the same key.
This is a separate follow-up step inside the Grok path, not a separate top-level
web-search provider choice. If you pick another provider, OpenClaw does not
show the x_search prompt.
```json5
{
plugins: {
entries: {
brave: {
config: {
webSearch: {
apiKey: "YOUR_KEY", // pragma: allowlist secret
},
},
},
},
},
}
```
```bash
export BRAVE_API_KEY="YOUR_KEY"
```
For a gateway install, put it in `~/.openclaw/.env`.
See [Env vars](/help/faq#env-vars-and-env-loading).
| Parameter | Description |
|---|---|
query | Search query (required) |
count | Results to return (1-10, default: 5) |
country | 2-letter ISO country code (e.g. "US", "DE") |
language | ISO 639-1 language code (e.g. "en", "de") |
search_lang | Search-language code (Brave only) |
freshness | Time filter: day, week, month, or year |
date_after | Results after this date (YYYY-MM-DD) |
date_before | Results before this date (YYYY-MM-DD) |
ui_lang | UI language code (Brave only) |
domain_filter | Domain allowlist/denylist array (Perplexity only) |
max_tokens | Total content budget, default 25000 (Perplexity only) |
max_tokens_per_page | Per-page token limit, default 2048 (Perplexity only) |
x_search queries X (formerly Twitter) posts using xAI and returns
AI-synthesized answers with citations. It accepts natural-language queries and
optional structured filters. OpenClaw only enables the built-in xAI x_search
tool on the request that serves this tool call.
{
plugins: {
entries: {
xai: {
config: {
xSearch: {
enabled: true,
model: "grok-4-1-fast-non-reasoning",
baseUrl: "https://api.x.ai/v1", // optional, overrides webSearch.baseUrl
inlineCitations: false,
maxTurns: 2,
timeoutSeconds: 30,
cacheTtlMinutes: 15,
},
webSearch: {
apiKey: "xai-...", // optional if XAI_API_KEY is set
baseUrl: "https://api.x.ai/v1", // optional shared xAI Responses base URL
},
},
},
},
},
}
x_search posts to <baseUrl>/responses when
plugins.entries.xai.config.xSearch.baseUrl is set. If that field is omitted,
it falls back to plugins.entries.xai.config.webSearch.baseUrl, then the
legacy tools.web.search.grok.baseUrl, and finally the public xAI endpoint.
| Parameter | Description |
|---|---|
query | Search query (required) |
allowed_x_handles | Restrict results to specific X handles |
excluded_x_handles | Exclude specific X handles |
from_date | Only include posts on or after this date (YYYY-MM-DD) |
to_date | Only include posts on or before this date (YYYY-MM-DD) |
enable_image_understanding | Let xAI inspect images attached to matching posts |
enable_video_understanding | Let xAI inspect videos attached to matching posts |
await x_search({
query: "dinner recipes",
allowed_x_handles: ["nytfood"],
from_date: "2026-03-01",
});
// Per-post stats: use the exact status URL or status ID when possible
await x_search({
query: "https://x.com/huntharo/status/1905678901234567890",
});
// Basic search
await web_search({ query: "OpenClaw plugin SDK" });
// German-specific search
await web_search({ query: "TV online schauen", country: "DE", language: "de" });
// Recent results (past week)
await web_search({ query: "AI developments", freshness: "week" });
// Date range
await web_search({
query: "climate research",
date_after: "2024-01-01",
date_before: "2024-06-30",
});
// Domain filtering (Perplexity only)
await web_search({
query: "product reviews",
domain_filter: ["-reddit.com", "-pinterest.com"],
});
If you use tool profiles or allowlists, add web_search, x_search, or group:web:
{
tools: {
allow: ["web_search", "x_search"],
// or: allow: ["group:web"] (includes web_search, x_search, and web_fetch)
},
}
web_search provider