Web Search, X Search, and Web Fetch Tools

Learn to use web_search, x_search, and web_fetch for querying the web, X content, or fetching URLs. Configure providers and understand caching and execution details.

Read this when

  • You want to enable or configure web_search
  • You want to enable or configure x_search
  • You need to choose a search provider
  • You want to understand auto-detection and provider selection

web_search queries the web through the provider you have set up and delivers results in a standardized format, with caching per query lasting 15 minutes (adjustable). OpenClaw additionally includes x_search for X (formerly Twitter) content and web_fetch for simple URL retrieval. web_fetch executes entirely on your machine; web_search goes through xAI Responses when Grok serves as the provider, and x_search relies on xAI Responses in all cases.

Info

web_search functions as a basic HTTP tool, not a full browser. For pages heavy on JavaScript or requiring authentication, turn to the Web Browser. When you need one particular URL, opt for Web Fetch.

Quick start

Choose a provider

Choose a provider and finish any necessary configuration. Certain providers work without keys, while others demand an API key. Check the individual provider pages listed below for specifics.

Configure

openclaw configure --section web

This holds the provider along with any required credentials. For providers backed by an API, you have the alternative of setting the provider's env var (such as BRAVE_API_KEY) and bypassing this step.

Configuration can also happen through conversation with OpenClaw: mention configure web search in openclaw setup or within the Control UI's Settings → Ask OpenClaw chat. The hosted workflow manages both provider selection and credential entry, with API keys hidden in the browser, and the terminal chat transfers control to the masked wizard using open search wizard.

Use it

await web_search({ query: "OpenClaw plugin SDK" });

For X posts:

await x_search({ query: "dinner recipes" });

Choosing a provider

  • Brave Search, Offers structured results with snippets. Includes llm-context mode and country/language filters. A free tier exists.

  • Codex Hosted Search, Delivers AI-generated grounded answers via your Codex app-server account.

  • DuckDuckGo, No key required. Works without an API key. Integration is unofficial and HTML-based.

  • Exa, Combines neural and keyword search with content extraction, including highlights, text, and summaries.

  • Firecrawl, Provides structured results. Works best alongside firecrawl_search and firecrawl_scrape for thorough extraction.

  • Gemini, Produces AI-generated answers with citations grounded in Google Search.

  • Grok, Produces AI-generated answers with citations grounded in xAI web search.

  • Kimi, Produces AI-generated answers with citations via Moonshot web search; ungrounded chat fallbacks fail explicitly.

  • MiniMax Search, Supplies structured results through the MiniMax Token Plan search API.

  • Ollama Web Search, Performs searches via a signed-in local Ollama host or the hosted Ollama API.

  • Parallel, Paid Parallel Search API (PARALLEL_API_KEY); offers higher rate limits and objective tuning.

  • Parallel Search (Free), Key-free opt-in. Parallel's free Search MCP, featuring LLM-optimized dense excerpts and no API key.

  • Perplexity, Gives structured results with content extraction controls and domain filtering.

  • SearXNG, Self-hosted meta-search. No API key needed. Pulls together Google, Bing, DuckDuckGo, and others.

  • Tavily, Delivers structured results with search depth, topic filtering, and tavily_extract for URL extraction.

Provider comparison

ProviderResult styleFiltersAPI key
BraveStructured snippetsCountry, language, time, llm-context modeBRAVE_API_KEY
Codex Hosted SearchAI-synthesized + source URLsDomains, context size, user locationNone; uses Codex/OpenAI sign-in
DuckDuckGoStructured snippets--None (key-free)
ExaStructured + extractedNeural/keyword mode, date, content extractionEXA_API_KEY
FirecrawlStructured snippetsVia firecrawl_search toolFIRECRAWL_API_KEY
GeminiAI-synthesized + citations--GEMINI_API_KEY
GrokAI-synthesized + citations--xAI OAuth, XAI_API_KEY, or plugins.entries.xai.config.webSearch.apiKey
KimiAI-synthesized + citations; fails on ungrounded chat fallbacks--KIMI_API_KEY / MOONSHOT_API_KEY
MiniMax SearchStructured snippetsRegion (global / cn)MINIMAX_CODE_PLAN_KEY / MINIMAX_CODING_API_KEY / MINIMAX_OAUTH_TOKEN
Ollama Web SearchStructured snippets--None for signed-in local hosts; OLLAMA_API_KEY for direct https://ollama.com search
ParallelDense excerpts ranked for LLM context--PARALLEL_API_KEY (paid)
Parallel Search (Free)Dense excerpts ranked for LLM context--None (free Search MCP)
PerplexityStructured snippetsCountry, language, time, domains, content limitsPERPLEXITY_API_KEY / OPENROUTER_API_KEY
SearXNGStructured snippetsCategories, languageNone (self-hosted)
TavilyStructured snippetsVia tavily_search toolTAVILY_API_KEY

Result shape

At the core tool boundary, web_search standardizes every bundled and external plugin provider. Whatever the source, callers get one of these fixed shapes:

type WebSearchOutput =
  | {
      kind: "error";
      provider: string;
      error: "provider_error";
      message: string;
      docs?: string;
    }
  | {
      kind: "results";
      provider: string;
      query: string;
      count: number;
      tookMs?: number;
      results: Array<{
        title: string;
        url: string;
        snippet?: string;
        published?: string;
        siteName?: string;
      }>;
      externalContent: {
        untrusted: true;
        source: "web_search";
        wrapped: true;
        provider: string;
      };
      cached?: true;
    }
  | {
      kind: "answer";
      provider: string;
      query: string;
      tookMs?: number;
      content: string;
      citations?: Array<{ url: string; title?: string }>;
      externalContent: {
        untrusted: true;
        source: "web_search";
        wrapped: true;
        provider: string;
      };
      cached?: true;
    }
  | {
      kind: "raw";
      provider: string;
      data: unknown;
    };

For structured providers, kind: "results" applies; for synthesized ones, kind: "answer" is used. External plugin providers whose payloads fit neither shape are forwarded unchanged as kind: "raw" to preserve compatibility. Fields specific to a provider, such as raw scores, excerpts, related searches, inline-citation offsets, model ids, or session metadata, are not carried over on normalized branches. When a provider's richer response matters to your workflow, reach for that provider's dedicated tool instead.

The boundary itself guarantees externalContent.wrapped: true as a trust marker: provider prose (title, snippet, siteName, content, citation titles, error message) has any pre-existing envelope lines removed and is re-wrapped exactly once at the core boundary, so no provider metadata can forge the marker. query always holds the requested query, citation and result URLs must parse as http(s), published must follow ISO-date shape, URLs are emitted canonicalized, and a payload containing an error key is always flagged as kind: "error", with the raw provider code kept inside the wrapped message. Raw passthrough payloads retain whatever markers the provider set.

For Perplexity, Tavily, and xAI HTTP errors, the status code and bounded diagnostics are preserved, while reflected request credentials are redacted. Before sharing diagnostics, review them; redaction does not eliminate every type of sensitive content.

Auto-detection

In docs and setup flows, provider lists appear alphabetically. Auto-detection follows a separate, fixed precedence order and only selects a provider that requires a credential (requiresCredential !== false) when one is found configured. If no provider is set, OpenClaw evaluates providers in this sequence and picks the first one that is ready:

API-backed providers come first:

  1. Brave -- BRAVE_API_KEY or plugins.entries.brave.config.webSearch.apiKey (order 10)
  2. MiniMax Search -- MINIMAX_CODE_PLAN_KEY / MINIMAX_CODING_API_KEY / MINIMAX_OAUTH_TOKEN / MINIMAX_API_KEY or plugins.entries.minimax.config.webSearch.apiKey (order 15)
  3. Gemini -- plugins.entries.google.config.webSearch.apiKey, GEMINI_API_KEY, or models.providers.google.apiKey (order 20)
  4. Grok -- xAI OAuth, XAI_API_KEY, or plugins.entries.xai.config.webSearch.apiKey (order 30)
  5. Kimi -- KIMI_API_KEY / MOONSHOT_API_KEY or plugins.entries.moonshot.config.webSearch.apiKey (order 40)
  6. Perplexity -- PERPLEXITY_API_KEY / OPENROUTER_API_KEY or plugins.entries.perplexity.config.webSearch.apiKey (order 50)
  7. Firecrawl -- FIRECRAWL_API_KEY or plugins.entries.firecrawl.config.webSearch.apiKey (order 60)
  8. Exa -- EXA_API_KEY or plugins.entries.exa.config.webSearch.apiKey; optional plugins.entries.exa.config.webSearch.baseUrl overrides the Exa endpoint (order 65)
  9. Tavily -- TAVILY_API_KEY or plugins.entries.tavily.config.webSearch.apiKey (order 70)
  10. Parallel -- paid Parallel Search API via PARALLEL_API_KEY or plugins.entries.parallel.config.webSearch.apiKey; optional plugins.entries.parallel.config.webSearch.baseUrl overrides the endpoint (order 75)

Following those, endpoint-configured providers:

  1. SearXNG -- SEARXNG_BASE_URL or plugins.entries.searxng.config.webSearch.baseUrl (order 200)

Providers without keys, such as Parallel Search (Free), DuckDuckGo, Ollama Web Search, and Codex Hosted Search, never win auto-detection despite carrying an internal order value. They activate only when chosen explicitly via tools.web.search.provider or with openclaw configure --section web. OpenClaw will not route managed web_search queries to a key-free provider solely because no API-backed provider is set up.

OpenAI Responses models break the pattern: while tools.web.search.provider remains empty, they rely on OpenAI's built-in web search rather than the managed providers listed above (details below). Assign tools.web.search.provider to parallel-free (or any other provider) to send them through the managed route instead.

Note

SecretRef objects are accepted in every provider key field. Plugin-scoped SecretRefs under plugins.entries.<plugin>.config.webSearch.apiKey get resolved for the installed API-backed web search providers, covering Brave, Exa, Firecrawl, Gemini, Grok, Kimi, MiniMax, Parallel, Perplexity, and Tavily, regardless of whether the provider is chosen explicitly via tools.web.search.provider or picked through auto-detection. In auto-detect mode, OpenClaw resolves only the selected provider's key, leaving non-selected SecretRefs dormant, so multiple providers can stay configured without incurring resolution overhead for unused ones.

When OpenClaw web search is enabled and no managed provider is pinned, direct OpenAI Responses models (api: "openai-responses", provider openai, no base URL or an official OpenAI API base URL) automatically use OpenAI's hosted web_search tool. This behavior belongs to the bundled OpenAI plugin and does not extend to OpenAI-compatible proxy base URLs or Azure routes. To keep the managed web_search tool for OpenAI models, set tools.web.search.provider to another provider such as brave; alternatively, set tools.web.search.enabled: false to turn off both managed search and native OpenAI search.

With web search enabled and no managed provider chosen, the Codex app-server runtime automatically relies on Codex's hosted web_search tool. Native hosted search and OpenClaw's managed web_search dynamic tool cannot coexist, so managed search has no way around native domain restrictions. OpenClaw falls back to the managed tool when hosted search is unavailable, explicitly disabled, or replaced by a selected managed provider. Codex's standalone web.run extension stays disabled (features.standalone_web_search: false) because production app-server traffic rejects its user-defined web namespace.

  • Configure native search through tools.web.search.openaiCodex
  • Assign tools.web.search.provider: "codex" to enable Codex Hosted Search as the managed web_search provider for any parent model. Every call triggers a limited, short-lived Codex app-server turn and errors out if Codex fails to return a hosted webSearch item.
  • mode: "cached" serves as the default setting, but Codex converts it to live external access for unrestricted app-server turns; set "live" to explicitly request live access
  • Point tools.web.search.provider at a managed provider like brave to rely on OpenClaw's managed web_search instead
  • Configure tools.web.search.openaiCodex.enabled: false to disable Codex-hosted search; other managed providers stay accessible
  • Limiting the Codex native tool surface also preserves managed web_search availability
  • When allowedDomains is enabled, it constrains both hosted web_search and managed web_fetch on turns where native hosted search is active. Turns that use a managed search provider remain unaffected. Automatic managed search fallback also fails closed if hosted search becomes unavailable.
  • Tool-disabled LLM-only runs turn off both native and managed search
  • tools.web.search.enabled: false deactivates managed and native search alike

Persistent effective Codex search-policy modifications launch a new bound thread, preventing an already loaded app-server thread from holding onto outdated hosted-search access. Transient per-turn constraints rely on a temporary restricted thread and keep the current binding intact for later resumption.

Direct OpenAI ChatGPT Responses traffic can also tap into OpenAI's hosted web_search tool. That separate route stays opt-in through tools.web.search.openaiCodex.enabled: true and applies only to compatible openai/* models using api: "openai-chatgpt-responses".

{
  tools: {
    web: {
      search: {
        enabled: true,
        // Optional: use Codex Hosted Search from non-Codex parent models too.
        provider: "codex",
        openaiCodex: {
          enabled: true,
          mode: "cached",
          allowedDomains: ["example.com"],
          contextSize: "high",
          userLocation: {
            country: "US",
            city: "New York",
            timezone: "America/New_York",
          },
        },
      },
    },
  },
}

For runtimes and providers lacking native Codex search support, Codex can fall back to the managed web_search option via OpenClaw's dynamic tool namespace. Choose an explicit managed provider when you require OpenClaw's provider-specific network controls over Codex-hosted search.

Picking provider: "codex" activates the bundled codex plugin and applies the same tools.web.search.openaiCodex restrictions described earlier. Authenticate the Codex app-server first with openclaw models auth login --provider openai. The parent agent can run on any model or runtime; only the bounded search worker executes through Codex.

Network safety

Managed HTTP web_search provider calls follow OpenClaw's guarded fetch path, limited to the current provider's own hostname. For that hostname only, OpenClaw permits Surge, Clash, and sing-box fake-IP DNS responses in 198.18.0.0/15 and fc00::/7. Other private, loopback, link-local, and metadata destinations stay blocked. Codex Hosted Search stands apart: its bounded worker hands network access to the Codex app-server's hosted web_search tool.

This automatic allowance does not extend to arbitrary web_fetch URLs. For web_fetch, turn on tools.web.fetch.ssrfPolicy.allowRfc2544BenchmarkRange and tools.web.fetch.ssrfPolicy.allowIpv6UniqueLocalRange explicitly only when your trusted proxy owns those synthetic ranges.

Config

{
  tools: {
    web: {
      search: {
        enabled: true, // default: true
        provider: "brave", // or omit for auto-detection
        maxResults: 5,
        timeoutSeconds: 30,
        cacheTtlMinutes: 15,
      },
    },
  },
}

tools.web.search.cacheTtlMinutes governs OpenClaw's local search-result caches. Set it to 0 to skip reads and writes, even for queries already cached. A shorter positive TTL limits reuse by entry age; a longer TTL does not stretch an entry's original expiry. Provider-side caching operates independently.

Provider-specific settings (API keys, base URLs, modes) sit 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. Check the provider pages for examples. Grok can also reuse an xAI OAuth auth profile from openclaw models auth login --provider xai --method oauth; API-key config remains the fallback.

tools.web.search.provider gets checked against the web-search provider ids declared by bundled and installed plugin manifests. A typo like "brvae" fails config validation rather than silently dropping 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 emits a warning so you can reinstall the plugin or run openclaw doctor --fix to clear the stale config.

web_fetch fallback provider selection works separately:

  • pick it with tools.web.fetch.provider
  • or leave that field empty and let OpenClaw auto-detect the first ready web-fetch provider from configured credentials
  • non-sandboxed web_fetch can use installed plugin providers that declare contracts.webFetchProviders; sandboxed fetches allow bundled providers and verified official plugin installs, but exclude third-party external plugins
  • the official Firecrawl plugin is the only bundled webFetchProviders contributor today, configured under plugins.entries.firecrawl.config.webFetch.*

When you pick Kimi during openclaw onboard or openclaw configure --section web, OpenClaw can also request:

  • the Moonshot API region (https://api.moonshot.ai/v1 or https://api.moonshot.cn/v1)
  • the default Kimi web-search model (defaults to kimi-k2.6)

For x_search, set plugins.entries.xai.config.xSearch.*. The same xAI auth profile used for chat applies here, or you can use the XAI_API_KEY / plugin web-search credential that Grok web search relies on. openclaw doctor --fix automatically migrates legacy tools.web.x_search.* configuration. When you pick Grok during openclaw onboard or openclaw configure --section web, OpenClaw additionally offers optional x_search setup with the same credential immediately after Grok setup finishes. That follow-up step lives inside the Grok path itself, not as a separate top-level web-search provider option. Choosing any other provider means OpenClaw skips the x_search prompt entirely.

Storing API keys

Config file

Execute openclaw configure --section web or assign the key directly:

{
  plugins: {
    entries: {
      brave: {
        config: {
          webSearch: {
            apiKey: "YOUR_KEY", // pragma: allowlist secret
          },
        },
      },
    },
  },
}

Environment variable

In the Gateway process environment, set the provider env var:

export BRAVE_API_KEY="YOUR_KEY"

For a gateway install, place it in ~/.openclaw/.env. Check Env vars.

Tool parameters

ParameterDescription
querySearch query (required)
countResults to return (1-10, default: 5)
country2-letter ISO country code (e.g. "US", "DE")
languageISO 639-1 language code (e.g. "en", "de")
search_langSearch-language code (Brave only)
freshnessTime filter: day, week, month, or year
date_afterResults after this date (YYYY-MM-DD)
date_beforeResults before this date (YYYY-MM-DD)
ui_langUI language code (Brave only)
domain_filterDomain allowlist/denylist array (Perplexity only)
max_tokensTotal content token budget, native Perplexity Search API only
max_tokens_per_pagePer-page extraction token limit, native Perplexity Search API only

Warning

Provider support varies across parameters. Brave's llm-context mode rejects ui_lang; date_before likewise requires date_after, since Brave custom freshness ranges demand both start and end dates. Gemini, Grok, and Kimi each return a single synthesized answer with citations. They accept count for shared-tool compatibility, yet it does not alter the grounded answer format. For Gemini, day freshness acts as a recency hint; wider freshness values and explicit dates set Google Search grounding time ranges. Perplexity follows the same pattern through the Sonar/OpenRouter compatibility path (plugins.entries.perplexity.config.webSearch.baseUrl / model or OPENROUTER_API_KEY); that route also drops max_tokens and max_tokens_per_page support. SearXNG accepts http:// only for trusted private-network or loopback hosts; public SearXNG endpoints must rely on https://. Firecrawl and Tavily support only query and count via web_search, so advanced options require their dedicated tools.

x_search searches X (formerly Twitter) posts through xAI and delivers AI-synthesized answers with citations. Natural-language queries and optional structured filters are both accepted. Rather than keeping it permanently registered, OpenClaw builds the built-in xAI x_search tool per request, so it stays active only for the turn that invokes it.

Warning

x_search executes on xAI's servers. xAI charges $5 per 1,000 tool calls, plus the model's input and output tokens.

Note

According to xAI documentation, x_search supports keyword search, semantic search, user search, and thread fetch. For per-post engagement stats like reposts, replies, bookmarks, or views, a targeted lookup for the exact post URL or status ID works better. Broad keyword searches may locate the right post but yield less complete per-post metadata. A solid approach: find the post first, then issue a second x_search query aimed at that specific post.

x_search config

When enabled is not provided, x_search appears only if the active model's provider happens to be xai and xAI credentials are available. If the active model uses a recognized provider other than xAI, you can enable cross-provider usage by setting plugins.entries.xai.config.xSearch.enabled to true. Should the provider for the active model be absent or fail to resolve, the tool remains hidden. Setting enabled to false turns it off across all providers. xAI credentials are mandatory in every case.

{
  plugins: {
    entries: {
      xai: {
        config: {
          xSearch: {
            enabled: true, // required for a known non-xAI model provider
            model: "grok-4.3",
            baseUrl: "https://api.x.ai/v1", // optional, overrides webSearch.baseUrl
            inlineCitations: false,
            maxTurns: 2,
            timeoutSeconds: 30,
            cacheTtlMinutes: 15,
          },
          webSearch: {
            apiKey: "xai-...", // optional if an xAI auth profile or XAI_API_KEY is set
            baseUrl: "https://api.x.ai/v1", // optional shared xAI Responses base URL
          },
        },
      },
    },
  },
}

With plugins.entries.xai.config.xSearch.baseUrl in place, x_search sends requests to <baseUrl>/responses. When that field is absent, the destination defaults to plugins.entries.xai.config.webSearch.baseUrl, and then to the public xAI endpoint at https://api.x.ai/v1.

plugins.entries.xai.config.xSearch.cacheTtlMinutes governs OpenClaw's local x_search result cache. Assigning it 0 disables both reading from and writing to the cache. A reduced TTL curbs how often existing entries get reused, while a longer TTL never stretches their original expiration.

x_search parameters

ParameterDescription
querySearch query (required)
allowed_x_handlesCap results to no more than 20 X handles
excluded_x_handlesOmit up to 20 X handles
from_dateInclude only posts dated on or after this date (YYYY-MM-DD)
to_dateInclude only posts dated on or before this date (YYYY-MM-DD)
enable_image_understandingAllow xAI to examine images attached to relevant posts
enable_video_understandingAllow xAI to examine videos attached to relevant posts

allowed_x_handles and excluded_x_handles cannot be used together.

x_search example

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",
});

Examples

// 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"],
});

Tool profiles

When working with tool profiles or allowlists, include 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 Fetch -- retrieve a URL and pull out readable content
  • Web Browser -- complete browser automation for sites heavy on JavaScript
  • Grok Search -- Grok serving as the web_search provider
  • Ollama Web Search -- web search with no API key via your Ollama host
3,474 words · updated Sep 1, 2026