Install
openclaw skills install @robbyczgw-cla/web-search-plusUnified multi-provider web search and URL extraction skill with intelligent auto-routing across Serper, Brave, Tavily, Querit, Linkup, Exa, Firecrawl, Perple...
openclaw skills install @robbyczgw-cla/web-search-plusStop choosing search providers. Let the skill do it for you.
This skill now connects you to 12 search providers and adds a companion extraction flow for pulling content from URLs. Broad web query? → Brave or Serper. Research question? → Tavily or Exa. Need citations and grounding? → Linkup. Want scrape-ready content? → Firecrawl. Prefer privacy? → SearXNG. Need low-cost Google SERP with prepaid credits? → SerpBase (explicit/fallback-only). Want an independent index as a last-resort fallback (even keyless)? → Keenable.
Read this before searching with sensitive queries.
--provider <name>) instead of relying on auto-routing, so you control exactly which third party receives the query. Self-hosted SearXNG keeps queries on infrastructure you control..cache by default, WSP_CACHE_DIR to relocate), including provider_health.json with provider error messages and provider_stats.json. Cache files are written with owner-only permissions (dir 0700, files 0600).
--no-cacheWSP_DISABLE_CACHE=1python3 scripts/search.py --clear-cache (inspect with --cache-stats)To avoid auto-activating on everyday requests, the manifest registers only narrowly scoped trigger phrases:
web search pluswsp searchsearch the web formulti-provider web searchextract url contentextract content from urlGeneric words like "search", "find", "look up", or "research" intentionally do not trigger this skill.
# Interactive setup (recommended for first run)
python3 scripts/setup.py
# Or manually
cp .env.example .env
python3 scripts/search.py -q "latest OpenClaw release"
python3 scripts/extract.py --url https://example.com
The wizard explains providers, collects keys, and sets defaults.
deep + deep-reasoningPERPLEXITY_API_KEY or KILOCODE_API_KEY--provider serpbase or add to provider_priority in config.json)KEENABLE_API_KEY or keyless against the opt-in shared public tier (WSP_KEENABLE_ALLOW_PUBLIC=1, ~1000 req/hour, no SLA, warning in result metadata). Lowest auto-routing priority — never displaces a configured keyed providerscripts/extract.py auto-falls back across (Tavily-first for reliability):
scrape.serper.dev, markdown preferred)Default priority (SerpBase excluded by design — opt-in only):
tavily → linkup → querit → exa → firecrawl → perplexity → brave → serper → you → searxng → keenable
Routing additionally applies adaptive provider performance memory: every provider call records latency/result-count/error into a rolling window (50 samples, 7-day freshness, persisted as provider_stats.json) that feeds bounded (±1.0) routing-score adjustments after 5 fresh samples — enough to break ties and nudge close calls, never enough to override a clear query-class winner (routing.adaptive_adjustments).
Examples:
python3 scripts/search.py -q "weather in Vienna today"
# generic current-web intent → Brave or Serper
python3 scripts/search.py -q "find credible sources for AI tutoring outcomes"
# citation/evidence intent → Linkup
python3 scripts/search.py -q "latest AI policy updates in Germany"
# multilingual + recency → Querit or Tavily
python3 scripts/search.py -p exa --exa-depth deep -q "LLM scaling laws research"
python3 scripts/search.py -p firecrawl -q "YC startups web scraping"
python3 scripts/search.py -p serpbase -q "best laptop 2026" # explicit SerpBase
Debug routing:
python3 scripts/search.py --explain-routing -q "your query"
python3 scripts/search.py -q "AI regulation" --freshness week
# providers with native date filters receive the mapped value; others run the
# normal search and report freshness.applied=false in metadata
python3 scripts/search.py -p serper --type news -q "quantum computing"
# Serper serves the Google news vertical natively (date/source/thumbnail);
# other providers report search_type.applied=false
python3 scripts/search.py -q "beste Kaffeehäuser Wien"
# explicit location hints (curated city/country table) set the country (at);
# set locale.language="auto" (or WSP_LOCALE_LANGUAGE=auto) to also infer the
# query language conservatively. Resolved values + sources appear in
# metadata.locale. Without configuration behavior stays exactly us/en.
Results from known Stack Overflow/GitHub/documentation mirror domains are removed (strict exact-domain/true-subdomain matching, no look-alike false positives); extend via quality.blocked_domains or rescue via quality.allowed_domains in config.json. Domain-diversity reranking caps a single domain at 2 head slots (overflow demoted, not dropped). Explicit domain intent (site: queries, --include-domains) bypasses both. Removals and demotions are reported in metadata.result_filter.
python3 scripts/extract.py --url https://example.com
python3 scripts/extract.py --url https://docs.linkup.so --provider linkup
python3 scripts/extract.py --url https://example.com --url https://example.org --include-images
python3 scripts/extract.py --url https://example.com --format html --include-raw-html
python3 scripts/extract.py --url https://example.com --provider serper # Serper webpage scraper
python3 scripts/extract.py --url https://example.com --extract-char-limit 30000
Oversized pages return a head/tail window plus an explanatory footer (default inline budget 15,000 chars; WSP_EXTRACT_CHAR_LIMIT or --extract-char-limit override). Inline base64 image data is replaced with [IMAGE: alt] placeholders before measuring content, preventing data-URI token bombs while preserving normal http(s) image links.
Research mode queries up to three providers concurrently (wall-clock cost ≈ slowest provider, not the sum), deduplicates across providers with deterministic ordering, then extracts the top sources for grounding:
python3 scripts/search.py --mode research -q "EU AI Act obligations for foundation models"
python3 scripts/search.py --mode research -q "..." --research-providers tavily linkup exa
python3 scripts/search.py --mode research -q "..." --research-extract-count 2 --research-time-budget 30
The time budget gates which providers launch and whether extraction runs; exhausted steps are reported in routing.provider_errors / routing.extraction_error instead of failing the call.
Quality reports add transparent routing/result diagnostics, including authority signals for canonical-source routing classes (canonical_domain_hits, demoted_domain_hits, canonical_top_result):
python3 scripts/search.py -q "official anthropic claude release notes" --quality-report
For those canonical classes (official vendor releases, official docs, policy PDFs, finance/IR, security advisories) results are also reranked for intent: primary sources are boosted, mirror/aggregator domains demoted (metadata.intent_rerank shows what changed).
.env.example documents supported env varsconfig.example.json includes provider priority and provider-specific defaultsconfig.json is your local runtime config"serpbase" to auto_routing.provider_priority in config.jsonWSP_KEENABLE_ALLOW_PUBLIC=1 or "keenable": {"allow_public": true}"locale": {"country": "...", "language": "..."} in config.json (or WSP_LOCALE_COUNTRY/WSP_LOCALE_LANGUAGE); --country/--language CLI flags always winRetry-After, retry at most once (waits ≤30s honored inline), and feed the provider's requested wait into the cooldown ladder; failure history older than 30 minutes decays instead of escalating cooldowns forever; missing-key configuration errors never trigger cooldownsURL SSRF protection (extraction, --similar-url):
http / https URLs are accepted10/8, 127/8, 169.254/16, 172.16/12, 192.168/16, CGNAT 100.64/10, ::1, fc00::/7, fe80::/10, IPv4-mapped IPv6, 0.0.0.0)169.254.169.254, metadata.google.internal) are always blocked--allow-private-urls or WSP_ALLOW_PRIVATE_URLS=1 (off by default; metadata endpoints stay blocked)SearXNG SSRF protection:
http / https onlySEARXNG_ALLOW_PRIVATE=1Local data:
0700; cache, provider-health, and provider-stats files are written 0600 via atomic temp-file replaceDeclared permissions (see package.json → clawhub.permissions):
*_API_KEY vars, SEARXNG_*, and WSP_* settingspython3 -m unittest discover -s tests -p 'test_*.py'
python3 scripts/search.py --explain-routing -q "find credible sources for climate change impacts"
python3 scripts/extract.py --url https://example.com --provider auto --compact