This skill routes search queries to the best available provider, manages daily API quota per agent, logs all searches, and degrades gracefully through web scraping when APIs are unavailable. It supports two execution strategies: scheduled agents that prioritise API calls for reliable results, and a general agent that preserves API quota by trying web scraping first. All quota state is persisted to a shared JSON file so multiple agents can coordinate without overrunning daily limits.
Use this skill whenever an agent needs to search the web for information — news, research, stock data, general queries, or any topic requiring up-to-date results.
This skill exposes three tools:
ToolPurposesmart_searchExecute a web search and return resultssearch_quota_statusCheck remaining quota for an agent or the full systemsearch_mark_agent_completeRelease unused quota to the shared pool when an agent finishes
smart_search#Executes a search query using the best available provider for the given agent.
Parameters:
ParameterTypeRequiredDescriptionquerystringYesThe search query. Max 1000 characters. No control characters.agent_idstringYesLowercase letters, numbers, and hyphens only (e.g. stock-analysis). Use general for manual/chat searches.force_providerstringNoOverride routing. Accepts "gemini" or "brave" only.
Return shape:
json
{
"results": "<string or array>",
"provider_used": "gemini | brave | web_fetch",
"queries_remaining": 12,
"quota_source": "agent_allocation | shared_pool | provider_direct | none",
"fallback_used": false,
"warning": null,
"web_fetch_engine": null,
"error": null
}
On failure results is null and error contains the reason.
search_quota_status#Returns quota information for a specific agent or the full quota file.
Parameters:
ParameterTypeRequiredDescriptionagent_idstringNoIf omitted, returns the full quota object.
Return shape (single agent):
json
{
"agent_id": "stock-analysis",
"gemini": { "allocated": 15, "used": 3, "remaining": 12 },
"brave": { "allocated": 0, "used": 0, "remaining": 0 },
"completed": false
}
search_mark_agent_complete#Marks an agent as done for the day and releases any unused allocation to the shared pool for other agents to use.
Parameters:
ParameterTypeRequiredDescriptionagent_idstringYesThe agent to mark complete.
Return shape:
json
{
"agent_id": "tech-news",
"released": { "gemini": 7, "brave": 0 },
"message": "Agent marked complete. 7 Gemini calls released to shared pool."
}
agent_id except general)#Used by automated agents with a known workload. Prioritises API quality; uses web scraping only as a last resort.
TierProviderCondition1Primary API (Gemini or Brave)Agent has allocation remaining2Fallback API (opposite provider)Primary exhausted or failed3DuckDuckGo web_fetchBoth APIs unavailable — blocked for finance queries4ErrorAll providers exhausted
agent_id: "general")#Used for manual chat or ad-hoc queries. Preserves API quota by trying web scraping first. Finance queries are not blocked from web_fetch in this strategy.
TierProviderCondition1Google web_fetchAlways attempted first2Bing web_fetchGoogle failed3API (Gemini or Brave)Both web_fetch failed — quota consumed only here4ErrorAll providers exhausted
Routing is automatic based on query keywords. force_provider overrides all routing.
Routes to Gemini:
Keywordstock, share price, nse, bse, wse, nyse, nasdaqearnings, ipo, dividend, funding round, acquisitionmerger, valuation, competitor, market sharebreaking, just announced, today, hours agospearfox, fifthspear
Routes to Brave:
Keywordspace, astronomy, nasa, esa, cosmos
If no keywords match, the query routes to the configured default_provider (defaults to brave). Gemini always wins for finance queries regardless of other keywords.
Keyword lists are configurable in openclaw.json via finance_keywords and brave_keywords arrays.
EngineUsed byRoleGoogleStrategy B (general)Tier 1 — always tried firstBingStrategy B (general)Tier 2 — tried if Google failsDuckDuckGoStrategy A (scheduled)Last resort — blocked for finance queries
All engines enforce a 2-second minimum delay between consecutive calls to avoid rate-limiting. Responses are capped at 10,000 characters to protect the model context window. Prompt injection patterns are stripped from all results before they are returned.
Each provider has a daily limit set in openclaw.json. Each agent has its own allocation drawn from that pool. Quota is tracked separately per provider ( gemini_used, brave_used).
Deduction flow for a single search call:
Config owns the ceilings (daily limits, per-agent allocations). The quota file owns the live counters. Changes to openclaw.json take effect on the next call without restart.
General agent quota is only consumed when both web_fetch attempts fail.
All allocations, limits, and behaviour flags live in openclaw.json under skills.smart-search. Changes take effect on the next skill invocation with no restart required.
json
{
"skills": {
"smart-search": {
"gemini_model": "gemini-2.0-flash",
"default_provider": "brave",
"strict_agents": false,
"retry_max_attempts": 3,
"retry_base_delay": 500,
"finance_keywords": ["stock", "earnings"],
"brave_keywords": ["space", "astronomy"],
"providers": {
"gemini": { "daily_limit": 1500 },
"brave": { "daily_limit": 66 }
},
"agent_allocations": {
"stock-analysis": { "gemini": 15, "brave": 0 },
"general": { "gemini": 20, "brave": 5 }
}
}
}
}
KeyDefaultEffectgemini_model``gemini-2.0-flashGemini model used for all API callsdefault_provider``braveFallback provider when no keywords matchstrict_agents``falseWhen true, rejects agent IDs not defined in configretry_max_attempts``3Maximum API retry attempts per callretry_base_delay``500Base delay in ms for exponential backofffinance_keywordsBuilt-in listKeywords that force Gemini routingbrave_keywordsBuilt-in listKeywords that force Brave routing
API keys are read from the env block in openclaw.json and held in memory only for the duration of the call. They are never logged to any file, never included in any error message, and never returned in any response object. Keys are not written to disk by this skill.
For manual or chat-initiated searches, OpenClaw must pass agent_id: "general". The skill automatically uses web_fetch first for this agent, so API quota is only consumed if both Google and Bing scraping fail. This is an OpenClaw configuration responsibility — the skill does not infer the agent from context.
Each day's searches are written to a .jsonl file in the logs/ subdirectory alongside the quota file:
text
~/.openclaw/workspace/shared/logs/search-YYYY-MM-DD.jsonl
Each line is a JSON object with: timestamp, agent_id, query, provider_used, force_provider, quota_source, queries_remaining, success, response_summary (capped at 500 chars), error, fallback_used, web_fetch_engine.
Log files older than 30 days are deleted automatically based on the filename date, not the filesystem modification time.
ErrorCauseResolutionquery is required and must be a non-empty string.Missing or blank queryPass a non-empty stringquery exceeds maximum length of 1000 characters.Query too longShorten the queryquery contains invalid control characters.Null bytes or control chars in queryStrip control characters before callingagent_id is required and must be a non-empty string.Missing agent_idPass a valid agent_idagent_id must contain only lowercase letters, numbers, and hyphens.Invalid characters in agent_idUse only [a-z0-9-]``Unknown agent: "...". Add it to openclaw.json to use it.strict_agents is true, agent not in configAdd agent to config or disable strict_agentsInvalid force_provider: "...". Use "gemini" or "brave".Unknown provider nameUse gemini or brave onlyPerplexity is not yet implemented.force_provider set to perplexityUse gemini or brave``GEMINI_API_KEY is not configured in openclaw.json.Missing API keyAdd key to openclaw.json env blockBrave Search is not configured. Add BRAVE_API_KEY to openclaw.json to enable it.Missing API keyAdd key to openclaw.json env blockGemini model ... is unavailable.Model name incorrect or deprecatedUpdate gemini_model in configGemini API error: 4xxBad request or auth failureCheck API key and queryBrave API error: 4xxBad request or auth failureCheck API keyGemini returned an empty response.API responded but returned no textRetry or check queryBrave returned no results.API returned empty results arrayTry a different queryFinance and time-sensitive queries require Gemini or Brave API.Finance query hit the web_fetch fallback blockEnsure API quota is available for finance queriesAll search providers exhausted or unavailable.All tiers failedCheck API keys, quota, and network... circuit is open. Try again later.Provider hit 3 consecutive failuresWait 60 seconds for circuit to resetQuota file is locked. Try again.Concurrent write contentionRetry the callCannot read openclaw.json. Check OPENCLAW_CONFIG_PATH.Config file missing or unreadableCheck file path and permissionsInvalid JSON input.stdin payload was not valid JSONEnsure the harness sends valid JSONUnknown tool: ...Tool name not recognisedUse smart_search, search_quota_status, or search_mark_agent_complete``Agent ... not found in quota.agent_id not in quota fileCall smart_search first to register the agentAgent ... is already marked complete for today.Double-complete callNo action needed; agent is already released
json
{ "tool": "smart_search", "args": { "query": "today AAPL stock price", "agent_id": "general" } }
Strategy B: Google web_fetch is tried first. Succeeds. Zero API quota consumed.
json
{
"results": "<html content from Google>",
"provider_used": "web_fetch",
"queries_remaining": 20,
"quota_source": "none",
"fallback_used": true,
"warning": "Result retrieved via web scraping. Quality may be lower than API results.",
"web_fetch_engine": "google"
}
json
{ "tool": "smart_search", "args": { "query": "today AAPL stock price", "agent_id": "general" } }
Strategy B: Google fails, Bing fails. Gemini API is called (finance keyword routes to Gemini). Quota consumed.
json
{
"results": "Apple (AAPL) is trading at $189.42...",
"provider_used": "gemini",
"queries_remaining": 19,
"quota_source": "agent_allocation",
"fallback_used": false,
"warning": null,
"web_fetch_engine": null
}
json
{ "tool": "smart_search", "args": { "query": "best noise-cancelling headphones 2025", "agent_id": "general" } }
Strategy B: No finance keywords. Google web_fetch tried first. Succeeds. Zero API quota consumed.
json
{
"results": "<html content from Google>",
"provider_used": "web_fetch",
"queries_remaining": 20,
"quota_source": "none",
"fallback_used": true,
"warning": "Result retrieved via web scraping. Quality may be lower than API results.",
"web_fetch_engine": "google"
}
json
{ "tool": "smart_search", "args": { "query": "NSE market open today", "agent_id": "stock-analysis" } }
Strategy A: Finance keyword routes to Gemini. Agent has 12 remaining. API call succeeds.
json
{
"results": "The NSE opened at 22,450 points today...",
"provider_used": "gemini",
"queries_remaining": 11,
"quota_source": "agent_allocation",
"fallback_used": false,
"warning": null,
"web_fetch_engine": null
}
json
{ "tool": "smart_search", "args": { "query": "NSE market open today", "agent_id": "stock-analysis" } }
Strategy A: Gemini exhausted, Brave exhausted. Finance keyword blocks DuckDuckGo fallback.
json
{
"results": null,
"provider_used": null,
"queries_remaining": 0,
"quota_source": "none",
"fallback_used": false,
"warning": null,
"web_fetch_engine": null,
"error": "Finance and time-sensitive queries require Gemini or Brave API. web_fetch fallback is not permitted for this query type."
}
json
{ "tool": "smart_search", "args": { "query": "latest AI research papers", "agent_id": "tech-news" } }
Strategy A: Gemini exhausted, Brave exhausted. No finance keywords — DuckDuckGo web_fetch is permitted.
json
{
"results": "<html content from DuckDuckGo>",
"provider_used": "web_fetch",
"queries_remaining": 0,
"quota_source": "none",
"fallback_used": true,
"warning": "Result retrieved via web scraping. Quality may be lower than API results.",
"web_fetch_engine": "duckduckgo"
}