> Fetch clean Markdown by appending `.md` to any page URL under https://signalwire.com/docs or requesting it with the HTTP header `Accept: text/markdown`. The root index at https://signalwire.com/docs/llms.txt lists the available documentation indexes. # WebSearchSkill > Search the web via Google Custom Search, scrape results, and quality-filter the output. [add-skill]: /docs/server-sdks/reference/typescript/agents/agent-base/add-skill Search the web using the Google Custom Search JSON API. The skill fetches more results than requested, scrapes each page, scores the extracted content, and returns only the highest-quality matches to the AI. **Class:** `WebSearchSkill` **Tools:** `web_search` (configurable via `tool_name`) **Env vars:** `GOOGLE_SEARCH_API_KEY`, `GOOGLE_SEARCH_ENGINE_ID` (legacy `GOOGLE_SEARCH_CX` is still accepted) **Multi-instance:** yes — instance key combines `search_engine_id` and `tool_name`. **`api_key`** `string` — required Google Custom Search API key. Falls back to the `GOOGLE_SEARCH_API_KEY` environment variable. --- **`search_engine_id`** `string` — required Google Custom Search Engine ID. Falls back to the `GOOGLE_SEARCH_ENGINE_ID` environment variable (or the legacy `GOOGLE_SEARCH_CX`). --- **`tool_name`** `string` Custom tool name for this Web Search instance (useful when registering multiple instances at once). --- **`num_results`** `integer` — default: 3 Number of high-quality results to return (range `1-10`). --- **`delay`** `number` — default: 0.5 Delay between scraping pages in seconds (minimum `0`). --- **`max_content_length`** `integer` — default: 32768 Maximum total response size in characters (minimum `1000`). --- **`oversample_factor`** `number` — default: 2.5 How many extra results to fetch for quality filtering — e.g. `2.5` fetches 2.5× the requested `num_results` before scoring. Range `1.0-3.5`. --- **`min_quality_score`** `number` — default: 0.3 Minimum quality score (0–1) required to include a result. --- **`no_results_message`** `string` Message returned when no quality results are found. Use `{query}` as a placeholder for the original search term. --- **`safe_search`** `string` — default: medium Safe-search level. One of `"off"`, `"medium"`, `"high"`. --- **`per_page_timeout`** `number` — default: 2.0 Maximum seconds to wait on a single page scrape. Minimum `0.1`. --- **`overall_deadline`** `number` — default: 10.0 Wall-clock budget in seconds for the entire tool call. In-flight scrapes are abandoned past this point so the response beats the kernel webhook timeout. Minimum `1.0`. --- **`parallel_scrape`** `boolean` — default: true Scrape all candidate pages concurrently (raced against the deadline) instead of sequentially. --- **`snippets_only`** `boolean` — default: false Skip page scraping entirely and return Google CSE snippets only. The fastest mode (sub-second). --- **`response_prefix`** `string` Optional text prepended (separated by a blank line) to every non-empty search result. --- **`response_postfix`** `string` Optional text appended (separated by a blank line) to every non-empty search result. --- ## Example ```typescript {6-10} import { AgentBase, WebSearchSkill } from '@signalwire/sdk'; const agent = new AgentBase({ name: 'assistant', route: '/assistant' }); agent.setPromptText('You are a helpful assistant.'); await agent.addSkill(new WebSearchSkill({ num_results: 3, safe_search: 'high', min_quality_score: 0.4, })); agent.run(); ``` > Search the web via Google Custom Search, scrape results, and quality-filter the output.