diff --git a/.github/workflows/verify-engines.yml b/.github/workflows/verify-engines.yml new file mode 100644 index 0000000..818e933 --- /dev/null +++ b/.github/workflows/verify-engines.yml @@ -0,0 +1,108 @@ +name: Verify engine table + +on: + push: + paths: + - 'skills/serpapi-web-search/SKILL.md' + - '.github/workflows/verify-engines.yml' + schedule: + - cron: '0 9 * * 1' # Monday 9am — catch stale paths from API changes + +jobs: + verify: + runs-on: ubuntu-latest + env: + SERPAPI_KEY: ${{ secrets.SERPAPI_KEY }} + steps: + - uses: actions/checkout@v4 + + - name: Verify each engine's response path + run: | + set -euo pipefail + + if [ -z "${SERPAPI_KEY:-}" ]; then + echo "::warning::SERPAPI_KEY not set (fork or missing secret). Skipping." + exit 0 + fi + + PASS=0; FAIL=0; SKIP=0 + + # Hardcoded engine→query and engine→result_key maps. + # Update these when SKILL.md engine table changes. + declare -A QUERIES=( + [google_light]="q=test" + [google]="q=test" + [google_scholar]="q=attention+is+all+you+need" + [google_maps]="q=Central+Park+New+York" + [google_maps_reviews]="data_id=0x89c2589a018531e3:0xb9df1f7387a94119" + [youtube]="search_query=hello+world" + [google_finance]="q=AAPL:NASDAQ" + [google_flights]="departure_id=JFK&arrival_id=LAX&outbound_date=$(date -d '+7 days' +%Y-%m-%d 2>/dev/null || date -v+7d +%Y-%m-%d)&type=2" + [google_hotels]="q=hotels+in+Kyoto&check_in_date=$(date -d '+14 days' +%Y-%m-%d 2>/dev/null || date -v+14d +%Y-%m-%d)&check_out_date=$(date -d '+16 days' +%Y-%m-%d 2>/dev/null || date -v+16d +%Y-%m-%d)&adults=2" + [google_shopping_light]="q=headphones" + [google_news_light]="q=technology" + [google_images_light]="q=sunset" + [google_jobs]="q=software+engineer" + [apple_app_store]="term=Notion" + [bing]="q=test" + [duckduckgo]="q=test" + ) + + declare -A RESULT_KEYS=( + [google_light]="organic_results" + [google]="organic_results" + [google_scholar]="organic_results" + [google_maps]="place_results|local_results" + [google_maps_reviews]="reviews" + [youtube]="video_results" + [google_finance]="summary" + [google_flights]="best_flights" + [google_hotels]="properties" + [google_shopping_light]="shopping_results" + [google_news_light]="news_results" + [google_images_light]="images_results" + [google_jobs]="jobs_results" + [apple_app_store]="organic_results" + [bing]="organic_results" + [duckduckgo]="organic_results" + ) + + for engine in "${!QUERIES[@]}"; do + params="${QUERIES[$engine]}" + expected_key="${RESULT_KEYS[$engine]}" + + resp=$(curl -s "https://serpapi.com/search.json?engine=${engine}&${params}&api_key=${SERPAPI_KEY}") + + # Check if result key exists and is non-empty (supports pipe-separated alternatives) + has_key=$(echo "$resp" | python3 -c " + import json, sys + d = json.load(sys.stdin) + keys = '${expected_key}'.split('|') + if any(k in d and d[k] for k in keys): + print('yes') + else: + print('no') + " 2>/dev/null || echo "error") + + if [ "$has_key" = "yes" ]; then + echo "✓ $engine → $expected_key" + PASS=$((PASS + 1)) + elif [ "$has_key" = "error" ]; then + echo "⊘ $engine → SKIP (parse error)" + SKIP=$((SKIP + 1)) + else + echo "✗ $engine → expected '$expected_key' not found" + echo " Response keys: $(echo "$resp" | python3 -c "import json,sys; print(list(json.load(sys.stdin).keys())[:10])" 2>/dev/null)" + FAIL=$((FAIL + 1)) + fi + + sleep 1 # rate limit courtesy + done + + echo "" + echo "=== Results: $PASS pass, $FAIL fail, $SKIP skip ===" + + if [ "$FAIL" -gt 0 ]; then + echo "::error::$FAIL engine(s) returned unexpected response structure" + exit 1 + fi diff --git a/.gitignore b/.gitignore index 1f33baf..c704a01 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ .sisyphus/ +*.py +__pycache__/ +.aut-traces/ +.opencode/ diff --git a/.opencode/skills/serpapi-web-search/SKILL.md b/.opencode/skills/serpapi-web-search/SKILL.md deleted file mode 100644 index 91b2f4a..0000000 --- a/.opencode/skills/serpapi-web-search/SKILL.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -name: serpapi-web-search -description: >- - Search the web using SerpApi's 100+ search engines. Use this skill whenever - the user needs current or web-sourced information: researching a topic, - checking recent news, comparing products or prices, finding local businesses, - searching images or videos, or looking up academic papers, flights, hotels, - or stocks — even if they don't explicitly ask to "search the web." Default - to google_light for speed. Supports Google, Bing, DuckDuckGo, YouTube, - Amazon, Maps, Scholar, and more. -compatibility: >- - Requires one of: a native serpapi_search tool; or the serpapi CLI (brew install serpapi/tap/serpapi-cli); or an SDK; or outbound network - access with curl. All paths require a SERPAPI_KEY. -license: MIT ---- - -## Invocation - -Use the first available method: - -**1. MCP tool** — use `serpapi_search` if available ([source](https://github.com/serpapi/serpapi-mcp)): -``` -serpapi_search(params={"engine": "google_light", "q": "query", "num": 10}, mode="compact") -``` -`mode="compact"` strips `search_metadata` and `search_parameters` — smaller context, same results. - -**2. serpapi-cli** — preferred shell fallback; optimized for AI agents ([source](https://github.com/serpapi/serpapi-cli)): -```bash -serpapi search engine=google_light q="your query" num=10 -``` -Install: `brew install serpapi/tap/serpapi-cli` -Auth: `SERPAPI_KEY` env var, `--api-key` flag, or `serpapi login`. -Exit codes: `0` success · `1` API error · `2` usage error. Errors are JSON on stderr. -For `--fields` / `--jq` filtering: see [rules/examples.md](rules/examples.md). - -**Result count:** Default to `num=10`. Use `num=3` only for narrow single-fact lookups. - -**3. SDK** — when writing code: see [rules/sdks.md](rules/sdks.md) — Python, JS, Go, Ruby, PHP, Java, .NET. - -**4. curl** — universal fallback: -```bash -curl -G "https://serpapi.com/search.json" \ - --data-urlencode "q=your query" \ - --data-urlencode "engine=google_light" \ - --data-urlencode "api_key=${SERPAPI_KEY}" -``` - -## Engine Selection - -Pick the engine that matches the user's intent: - -| Use Case | Engine | -|:---|:---| -| **General web — default for AI agents** | `google_light` ⚡ | -| Comprehensive (knowledge graph, local pack, featured snippets) | `google` | -| News | `google_news_light` | -| Images | `google_images_light` | -| Shopping / prices | `google_shopping_light` | -| Flights | `google_flights` | -| Hotels | `google_hotels` | -| Jobs | `google_jobs` | -| Alternative web | `bing` | -| Privacy-first | `duckduckgo` | -| Academic / research | `google_scholar` | -| Local / maps | `google_maps` | -| Video | `youtube` | -| **SerpApi's own crawled index** | `search_index` 🔬 | - -**For AI/LLM agents:** `google_light` is the recommended default — it has the lowest latency, smallest response payload, and returns clean organic results without the noise of the full `google` engine. Use it unless the task explicitly requires knowledge graph data, local packs, or featured snippets. - -**`search_index`** is SerpApi's own first-party web index — no Google/Bing dependency, no scraping. It is in active development and improving rapidly. Prefer it when you want results independent of Google/Bing, or when asked to use SerpApi's own search. It will be the best LLM-native search option as it matures. - -Prefer `_light` variants — they're faster and cheaper. Use the full engine only when you need knowledge graph, local pack, or featured snippets. - -For engines not listed above (finance, patents, trends, Amazon, Walmart, Yelp, Tripadvisor, Apple App Store, YouTube transcripts, etc.), read [rules/ENGINES.md](rules/ENGINES.md). - -## Error Reference - -- **401** — Invalid or missing API key. -- **429** — Monthly quota reached. Check usage: `serpapi account` or [serpapi.com/dashboard](https://serpapi.com/dashboard) · [account API](https://serpapi.com/account-api). -- **400** — Missing required parameter (`q` or `engine`). - -## Docs - -Official reference (link these when agents need deeper detail): - -| Topic | URL | -|:---|:---| -| Main API reference | [serpapi.com/search-api](https://serpapi.com/search-api) | -| All engines (online) | [serpapi.com/search-engine-apis](https://serpapi.com/search-engine-apis) | -| Locations lookup | [serpapi.com/locations-api](https://serpapi.com/locations-api) | -| Account & quota API | [serpapi.com/account-api](https://serpapi.com/account-api) | -| Search Index API (alpha) | [serpapi.com/search-index-api](https://serpapi.com/search-index-api) | -| Pricing | [serpapi.com/pricing](https://serpapi.com/pricing) | - -## Rules - -Read these files when you need more detail: - -- **Parameters** (locale, time filter, pagination, safe search): [rules/parameters.md](rules/parameters.md) -- **Response format** (result keys, JSON shape, pagination): [rules/response.md](rules/response.md) -- **Examples** (news, shopping, time-filtered, Bing): [rules/examples.md](rules/examples.md) -- **SDKs** (Python, JS, Go, Ruby, PHP, Java, .NET): [rules/sdks.md](rules/sdks.md) -- **All 100+ engines** (flights, hotels, jobs, finance, patents…): [rules/ENGINES.md](rules/ENGINES.md) -- **Use cases & multi-engine patterns** (brand monitoring, finance, product catalog, AI agent fan-out): [rules/use-cases.md](rules/use-cases.md) diff --git a/.opencode/skills/serpapi-web-search/rules/ENGINES.md b/.opencode/skills/serpapi-web-search/rules/ENGINES.md deleted file mode 100644 index c95f831..0000000 --- a/.opencode/skills/serpapi-web-search/rules/ENGINES.md +++ /dev/null @@ -1,193 +0,0 @@ -# SerpApi Search Engines Catalog - -Complete list of 133 SerpApi search engines. Use the `engine` parameter to select the desired search engine. Prefer `_light` variants — they're faster and cheaper. See [response.md](response.md) for result keys by engine. - -## Google (69 engines) - -| Engine | Description | Key Parameters | -|--------|-------------|----------------| -| google | Main Google Search results | q, location, gl, hl, google_domain | -| google_light | Fast, essential Google Search results | q, location, gl, hl | -| google_ai_mode | Google AI-powered search mode | q, gl, hl | -| google_ai_overview | Google AI Overview results | q, gl, hl | -| google_about_this_result | Google "About This Result" feature data | q, google_domain | -| google_autocomplete | Google search autocomplete suggestions | q, gl, hl | -| google_related_questions | Google "People Also Ask" questions | q, gl, hl | -| google_images | Google Images search | q, location, ijn, safe | -| google_images_light | Fast Google Images results | q, location, gl, hl | -| google_images_related_content | Related content for Google Images | image_url, gl, hl | -| google_reverse_image | Google reverse image search | image_url, gl, hl | -| google_lens | Google Lens visual search | url, image_url | -| google_news | Google News results | q, gl, hl, tbm=nws | -| google_news_light | Fast Google News results | q, gl, hl | -| google_shopping | Google Shopping product search | q, location, direct_link | -| google_shopping_light | Fast Google Shopping results | q, location, gl, hl | -| google_shopping_filters | Google Shopping filters and facets | q, tbs, gl, hl | -| google_videos | Google Videos search | q, location, gl, hl | -| google_videos_light | Fast Google Videos results | q, location, gl, hl | -| google_short_videos | Google Short Videos (Shorts/TikTok style) | q, gl, hl | -| google_local | Google Local results | q, location, gl, hl | -| google_local_services | Google Local Services results | q, location, category | -| google_maps | Google Maps search | q, ll, type | -| google_maps_reviews | Reviews for a Google Maps place | data_id, hl, sort | -| google_maps_directions | Directions from Google Maps | origin, destination | -| google_maps_photos | Photos for a Google Maps place | data_id, hl | -| google_maps_photo_meta | Metadata for Google Maps photos | data_id, photo_id | -| google_maps_posts | Posts for a Google Maps place | data_id, hl | -| google_maps_autocomplete | Google Maps autocomplete suggestions | q, ll | -| google_maps_contributor_reviews | Google Maps contributor reviews | contributor_id, gl, hl | -| google_jobs | Google for Jobs search | q, location, chips | -| google_jobs_listing | Detailed Google Jobs listing | q, j_id, htidocid | -| google_scholar | Google Scholar results | q, as_ylo, as_yhi | -| google_scholar_author | Google Scholar author profile | author_id, hl | -| google_scholar_case_law | Google Scholar case law details | case_id | -| google_scholar_cite | Google Scholar citation details | q, hl | -| google_scholar_profiles | Google Scholar user profiles | mapex, hl | -| google_patents | Google Patents results | q, patent_number, country | -| google_patents_details | Individual patent details | patent_id, hl | -| google_finance | Google Finance stock/market data | q, window | -| google_finance_markets | Google Finance market overview | market | -| google_flights | Google Flights search | departure_id, arrival_id, outbound_date, type | -| google_flights_airports | Google Flights airport information | q, hl | -| google_flights_autocomplete | Google Flights autocomplete | q, hl | -| google_flights_deals | Google Flights deal discovery (no fixed route) | departure_id, outbound_date, gl, hl | -| google_hotels | Google Hotels search | q, check_in_date, check_out_date | -| google_hotels_ads | Google Hotels advertisements | q, hl | -| google_hotels_autocomplete | Google Hotels autocomplete suggestions | q, gl, hl | -| google_hotels_photos | Google Hotels photos | q, hl | -| google_hotels_reviews | Google Hotels reviews | q, hl | -| google_hotels_properties | Google Hotels property results | q, hl | -| google_trends | Google Trends results | q, geo, date | -| google_trends_autocomplete | Google Trends autocomplete | q, hl | -| google_trends_news | Google Trends news articles | q, geo, hl | -| google_trends_trending_now | Google Trends trending searches | geo, hl | -| google_events | Google Events search | q, location, gl, hl | -| google_play | Google Play Store results | q, store, gl, hl | -| google_play_product | Google Play product details | product_id, gl | -| google_play_reviews | Google Play product reviews | product_id, gl | -| google_play_books | Google Play Books search | q, gl, hl | -| google_play_games | Google Play Games search | q, gl, hl | -| google_play_movies | Google Play Movies search | q, gl, hl | -| google_ads | Google Ads — keyword-level sponsored results (higher rate than google) 🔒 | q, location | -| google_ads_transparency_center | Google Ads Transparency Center — lookup by advertiser | q, customer_id | -| google_ads_transparency_center_ad_details | Individual ad creative details | advertiser_id, creative_id | -| google_immersive_product | Google Immersive Product results | q, product_id | -| google_forums | Google Forums results | q, gl, hl | -| google_travel | Google Travel results | q, gl, hl | -| google_travel_explore | Google Travel Explore destinations | travel_type, destination | - -## Bing (9 engines) - -| Engine | Description | Key Parameters | -|--------|-------------|----------------| -| bing | Main Bing Search results | q, location, cc, mkt | -| bing_images | Bing Images search | q, mkt, safeSearch | -| bing_news | Bing News search | q, mkt, freshness | -| bing_shopping | Bing Shopping search | q, mkt, price | -| bing_videos | Bing Videos search | q, mkt, count | -| bing_copilot | Bing Copilot results | q, cc | -| bing_maps | Bing Maps search | q, cp, lat, lon | -| bing_product | Bing Product results | product_id, mkt | -| bing_reverse_image | Bing reverse image search | image_url, cc, mkt | - -## DuckDuckGo + AI Web (5 engines) - -| Engine | Description | Key Parameters | -|--------|-------------|----------------| -| duckduckgo | Main DuckDuckGo Search results | q, kl, l | -| duckduckgo_light | Fast DuckDuckGo Search results | q, kl, l | -| duckduckgo_maps | DuckDuckGo Maps results | q, kl, l, lat, lon | -| duckduckgo_news | DuckDuckGo News results | q, kl, l, df | -| brave_ai_mode | Brave AI Mode search | q, gl, hl | - -## Yahoo, Yandex, Baidu, Naver (15 engines) - -| Engine | Description | Key Parameters | -|--------|-------------|----------------| -| yahoo | Yahoo! Search results | p, vc, pz | -| yahoo_images | Yahoo! Images results | p, pz, imgtype | -| yahoo_shopping | Yahoo! Shopping results | p, pz, sort | -| yahoo_videos | Yahoo! Videos results | p, pz, duration | -| yandex | Yandex Search results | text, lr, p | -| yandex_images | Yandex Images results | text, lr, p | -| yandex_videos | Yandex Videos search | text, lr, duration | -| baidu | Baidu Search results | wd, pn, rn | -| baidu_news | Baidu News search | word, pn, rn | -| naver | Naver Search results | query, where, start | -| naver_ai_overview | Naver AI Overview results | query | -| naver_images | Naver Images results | query, where, start | -| naver_news | Naver News results | query, where, start | -| naver_shopping | Naver Shopping results | query, where, start | -| naver_videos | Naver Videos results | query, where, start | - -## Shopping (13 engines) - -| Engine | Description | Key Parameters | -|--------|-------------|----------------| -| amazon | Amazon product search | k, page, sort | -| amazon_product | Amazon product details | asin | -| amazon_reviews | Amazon product reviews | asin | -| ebay | eBay product search | _nkw, _pgn, _sop | -| ebay_product | eBay product details | item_id, epid | -| ebay_deals | eBay deals results | q | -| walmart | Walmart product search | query, page, sort | -| walmart_product | Walmart product details | product_id | -| walmart_reviews | Walmart product reviews | product_id, page | -| walmart_product_sellers | Walmart product sellers | product_id | -| home_depot | Home Depot product search | q, page, sort | -| home_depot_product | Home Depot product details | product_id | -| home_depot_product_reviews | Home Depot product reviews | product_id, page | - -## Travel & Local (10 engines) - -| Engine | Description | Key Parameters | -|--------|-------------|----------------| -| tripadvisor | Tripadvisor results | q, location_id | -| tripadvisor_place | Tripadvisor place details | data_id | -| tripadvisor_reviews | Tripadvisor place reviews | location_id | -| yelp | Yelp business search | find_desc, find_loc | -| yelp_place | Yelp business details | place_id | -| yelp_reviews | Yelp reviews | place_id, start | -| opentable | OpenTable results | q, metroId | -| open_table_reviews | OpenTable reviews | restaurant_id | -| apple_maps | Apple Maps local search | query, location | -| apple_maps_places | Apple Maps Places details | q, gl, hl | - -## Media (9 engines) - -| Engine | Description | Key Parameters | -|--------|-------------|----------------| -| youtube | YouTube Search results | search_query, sp | -| youtube_video | YouTube video details | v, video_id | -| youtube_video_transcript | YouTube video transcript | v, lang | -| youtube_channel | YouTube channel results | channel_id, sp | -| youtube_playlist | YouTube playlist results | list, sp | -| youtube_shorts | YouTube shorts results | search_query, sp | -| apple_app_store | Apple App Store results | term, country | -| apple_product | Apple App Store product details | id, country | -| apple_reviews | Apple App Store reviews | id, country | - -## Social (2 engines) - -| Engine | Description | Key Parameters | -|--------|-------------|----------------| -| instagram_profile | Instagram public profile data | profile_id | -| facebook_profile | Facebook public profile data | profile_id | - -## SerpApi Search Index (Alpha) - -SerpApi's own first-party search index — no Google/Bing scraping. Results come directly from SerpApi's own crawled web index. Zero external API dependency, fastest response, fully private. - -| Engine | Description | Key Parameters | -|--------|-------------|----------------| -| `search_index` | SerpApi's own crawled web index — fastest, private, no scraping (actively improving; alpha) | `q` | - -CLI: `serpapi search engine=search_index q="your query"` -HTTP: `GET https://serpapi.com/search.json?engine=search_index&q=...&api_key=...` -Docs: [serpapi.com/search-index-api](https://serpapi.com/search-index-api) - -> **Note:** Alpha — ranking and coverage are actively improving. Will be the best LLM-native search option as it matures. - ---- -Full online engine catalog: [serpapi.com/search-engine-apis](https://serpapi.com/search-engine-apis) · Pricing: [serpapi.com/pricing](https://serpapi.com/pricing) · Usage covered in [SKILL.md](../SKILL.md). - diff --git a/.opencode/skills/serpapi-web-search/rules/examples.md b/.opencode/skills/serpapi-web-search/rules/examples.md deleted file mode 100644 index 14543ac..0000000 --- a/.opencode/skills/serpapi-web-search/rules/examples.md +++ /dev/null @@ -1,93 +0,0 @@ -# SerpApi Examples - -All examples use `serpapi-cli` (preferred). For curl equivalents, swap `serpapi search engine=X q=Y` with `curl -G "https://serpapi.com/search.json" --data-urlencode "q=Y" --data-urlencode "engine=X" --data-urlencode "api_key=${SERPAPI_KEY}"`. - -## Google News - -Latest news on a topic: - -```bash -serpapi search engine=google_news_light q="artificial intelligence" -``` - -Result key: `news_results` - -## Google Shopping - -Product prices and availability: - -```bash -serpapi search engine=google_shopping_light q="iphone 16 pro" -``` - -Result key: `shopping_results` - -## Bing Web Search - -Alternative web results (cross-reference or privacy): - -```bash -serpapi search engine=bing q="serpapi documentation" -``` - -Result key: `organic_results` - -## Time-Filtered Search - -Results from the past week: - -```bash -serpapi search engine=google_light q="latest AI models" tbs=qdr:w -``` - -See [parameters.md](parameters.md) for all `tbs` values. - -## Result Filtering - -Fetch only what you need — fewer tokens, faster processing: - -```bash -# Server-side: only return top 10 organic results (reduces API payload) -serpapi search --fields "organic_results[0:10]" engine=google_light q="coffee" - -# Client-side: extract title + link + snippet after receiving full response -serpapi search --jq ".organic_results[0:10]|[.[]|{title,link,snippet}]" engine=google_light q="coffee" - -# Both combined: minimum bandwidth + minimum context window tokens -serpapi search --fields "organic_results[0:10]" --jq "[.organic_results[]|{title,link,snippet}]" engine=google_light q="coffee" -``` - -## Search Index (SerpApi's Own Index) - -Query SerpApi's first-party web index — no Google/Bing dependency, direct index access: - -```bash -serpapi search engine=search_index q="serpapi documentation" - -# With field filtering -serpapi search --jq ".organic_results[0:10]|[.[]|{title,link,snippet}]" engine=search_index q="coffee" -``` - -Result key: `organic_results` (same structure as `google_light`) - -## Paginate All Results - -```bash -serpapi search engine=google q="coffee" --all-pages --max-pages 3 -``` - -## Retrieve a Cached Search - -Every SerpApi response includes a `search_metadata.id`. Retrieve it later without an extra quota cost: - -```bash -serpapi archive -``` - -## Account Usage - -Check remaining quota: - -```bash -serpapi account -``` diff --git a/.opencode/skills/serpapi-web-search/rules/parameters.md b/.opencode/skills/serpapi-web-search/rules/parameters.md deleted file mode 100644 index 4655f12..0000000 --- a/.opencode/skills/serpapi-web-search/rules/parameters.md +++ /dev/null @@ -1,59 +0,0 @@ -# SerpApi Parameters - -All parameters for `GET https://serpapi.com/search.json`. - -## Required - -| Parameter | Type | Description | -|:---|:---|:---| -| `engine` | string | The search engine to use (e.g., `google_light`). | -| `q` | string | The search query. Required for most engines. Exceptions: `youtube` uses `search_query`; `amazon` uses `k`; `instagram_profile` uses `profile_id`; `google_maps_reviews` uses `data_id`. | -| `api_key` | string | Your SerpApi API key. | - -## Pagination - -| Parameter | Type | Description | -|:---|:---|:---| -| `num` | integer | Results per page (max 100, default 10). | -| `start` | integer | Result offset. Use `start=10&num=10` for page 2. | - -## Locale Targeting - -Set these based on the user's context — they improve result relevance and ranking order. - -| Parameter | Type | Description | -|:---|:---|:---| -| `gl` | string | Country code (e.g., `us`, `uk`, `de`). Default: `us`. | -| `hl` | string | Language code (e.g., `en`, `es`, `fr`). Default: `en`. | -| `location` | string | Canonical city/region for precise geo-targeting (e.g., `Austin, Texas`). Takes precedence over `gl`. Look up valid values at [serpapi.com/locations-api](https://serpapi.com/locations-api). | - -**Example — French results from France:** -```bash -serpapi search engine=google_light q="restaurants paris" gl=fr hl=fr -``` - -## Time Filtering - -Use `tbs` to restrict results to a time range. - -| Value | Meaning | -|:---|:---| -| `qdr:d` | Past 24 hours | -| `qdr:w` | Past week | -| `qdr:m` | Past month | -| `qdr:y` | Past year | - -**Example — news from the past week:** -```bash -serpapi search engine=google_light q="AI announcements" tbs=qdr:w -``` - -## Other Parameters - -| Parameter | Type | Description | -|:---|:---|:---| -| `safe` | string | Safe search: `active` or `off`. | -| `no_cache` | string | Pass `"true"` to bypass cached results and force a live crawl. | - ---- -Full parameter reference: [serpapi.com/search-api](https://serpapi.com/search-api) · Locations lookup: [serpapi.com/locations-api](https://serpapi.com/locations-api) diff --git a/.opencode/skills/serpapi-web-search/rules/response.md b/.opencode/skills/serpapi-web-search/rules/response.md deleted file mode 100644 index f441dde..0000000 --- a/.opencode/skills/serpapi-web-search/rules/response.md +++ /dev/null @@ -1,51 +0,0 @@ -# SerpApi Response Format - -## JSON Structure - -All engines return JSON with `search_metadata` and `search_parameters` at the top level. Result arrays and `serpapi_pagination` are present when results exist; they are absent on empty results or errors. - -```json -{ - "search_metadata": { "status": "Success", "created_at": "..." }, - "search_parameters": { "engine": "google_light", "q": "..." }, - "organic_results": [ - { - "position": 1, - "title": "Example Result", - "link": "https://example.com", - "snippet": "Brief description of the result." - } - ], - "serpapi_pagination": { "next": "https://serpapi.com/search.json?..." } -} -``` - -When using `mode="compact"` with the native tool, `search_metadata` and `search_parameters` are stripped from the response. - -## Result Key by Engine - -Different engines use different top-level array keys for their results: - -| Engine Category | Result Key | -|:---|:---| -| Web (`google_light`, `google`, `bing`, `duckduckgo`) | `organic_results` | -| News (`google_news_light`, `bing_news`, `duckduckgo_news`) | `news_results` | -| Images (`google_images_light`, `google_images`) | `images_results` | -| Shopping (`google_shopping_light`, `google_shopping`) | `shopping_results` | -| Amazon, Walmart, eBay | `organic_results` | -| Jobs (`google_jobs`) | `jobs_results` | -| Maps (`google_maps`) | `local_results` | -| Maps Reviews (`google_maps_reviews`) | `reviews` | -| Videos (`google_videos_light`) | `video_results` | -| YouTube (`youtube`) | `video_results` | -| Scholar (`google_scholar`) | `organic_results` | -| Flights (`google_flights`) | `best_flights`, `other_flights` | -| Finance (`google_finance`) | `summary`, `graph`, `news_results` (multiple top-level keys) | -| Trends (`google_trends`) | `interest_over_time`, `related_queries`, `related_topics` | - -## Pagination - -Use `serpapi_pagination.next` as the URL for the next page of results. It includes all current parameters plus the correct pagination offset for the engine — pass it directly without modification. - ---- -Full response schema: [serpapi.com/search-api](https://serpapi.com/search-api) diff --git a/.opencode/skills/serpapi-web-search/rules/sdks.md b/.opencode/skills/serpapi-web-search/rules/sdks.md deleted file mode 100644 index c65c7a6..0000000 --- a/.opencode/skills/serpapi-web-search/rules/sdks.md +++ /dev/null @@ -1,86 +0,0 @@ -# SerpApi SDKs - -All official SDKs wrap the same `/search.json` endpoint. Use the SDK for your target language instead of raw HTTP. - -## Python - -```bash -pip install serpapi -``` - -```python -import serpapi - -client = serpapi.Client(api_key="your_key_here") -results = client.search({"engine": "google_light", "q": "coffee"}) -print(results["organic_results"]) -``` - -Repo: [github.com/serpapi/serpapi-python](https://github.com/serpapi/serpapi-python) - -## JavaScript / Node.js - -```bash -npm install serpapi -``` - -```js -import SerpApi from "serpapi"; - -const client = new SerpApi.Client({ apiKey: "your_key_here" }); -const results = await client.json({ engine: "google_light", q: "coffee" }); -console.log(results.organic_results); -``` - -Repo: [github.com/serpapi/serpapi-javascript](https://github.com/serpapi/serpapi-javascript) - -## Ruby - -```bash -gem install serpapi -``` - -```ruby -require "serpapi" - -client = SerpApi::Client.new(api_key: "your_key_here") -results = client.search(engine: "google_light", q: "coffee") -puts results[:organic_results] -``` - -Repo: [github.com/serpapi/serpapi-ruby](https://github.com/serpapi/serpapi-ruby) - -## Go - -```bash -go get github.com/serpapi/serpapi-golang -``` - -```go -import "github.com/serpapi/serpapi-golang" - -client := serpapi.NewClient(serpapi.ClientConfig{APIKey: "your_key_here"}) -results, _ := client.Search(map[string]string{"engine": "google_light", "q": "coffee"}) -``` - -Repo: [github.com/serpapi/serpapi-golang](https://github.com/serpapi/serpapi-golang) - -## PHP - -```bash -composer require serpapi/serpapi -``` - -Repo: [github.com/serpapi/serpapi-php](https://github.com/serpapi/serpapi-php) - -## Java - -Repo: [github.com/serpapi/serpapi-java](https://github.com/serpapi/serpapi-java) - -## .NET / C# - -Repo: [github.com/serpapi/serpapi-dotnet](https://github.com/serpapi/serpapi-dotnet) - ---- - -All SDKs accept the same parameters as the REST API. See [parameters.md](parameters.md) for the full parameter list and [serpapi.com/search-api](https://serpapi.com/search-api) for the canonical reference. diff --git a/AGENTS.md b/AGENTS.md index d0e38d3..22e1599 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,87 +1,25 @@ -# SerpApi Search Skill - -Universal web search across 100+ search engines and result types. - - + + -## Overview - -Documentation-only skill package for AI coding agents. No executable code — the deliverable is Markdown files that agents consume. Powered by [SerpApi](https://serpapi.com) REST API via `serpapi_search` MCP tool, the [serpapi CLI](https://github.com/serpapi/serpapi-cli) (preferred), official SDKs, or `curl`. - -## Structure - -``` -./ -├── AGENTS.md # This file — skill discovery + project knowledge -├── README.md # GitHub landing page, installation instructions -├── LICENSE # MIT -├── docs/ -│ └── api-key-setup.md # SERPAPI_KEY config per agent + CI/CD -└── skills/serpapi-web-search/ - ├── SKILL.md # Core skill definition (frontmatter + usage) - ├── LESSONS.md # Deep knowledge — JIT-injected by extension hooks - ├── serpapi.yaml # Network policy preset - └── rules/ - ├── ENGINES.md # Full catalog of 133 search engines - ├── examples.md # CLI examples for common search types - ├── parameters.md # All query parameters with examples - ├── response.md # Response format and result key reference - ├── use-cases.md # Multi-engine patterns, fan-out, usage formulas - └── sdks.md # SDK quickstart: Python, JS, Go, Ruby, PHP, Java, .NET -``` - -Hidden (not tracked in git): -- `.opencode/` — OpenCode plugin packaging (node_modules, duplicate SKILL.md) -- `.sisyphus/` — Build/QA evidence and plans - -## Where to Look - -| Task | Location | Notes | -|------|----------|-------| -| Add SDK quickstart | `skills/serpapi-web-search/rules/sdks.md` | Python, JS, Go, Ruby, PHP, Java, .NET | -| Edit skill behavior/examples | `skills/serpapi-web-search/SKILL.md` | Canonical agent-facing artifact | -| Add/update search engines | `skills/serpapi-web-search/rules/ENGINES.md` | 133 engines in categorized tables | -| Add use case / multi-engine pattern | `skills/serpapi-web-search/rules/use-cases.md` | Segment-to-engine mapping, fan-out, usage formulas | -| Change API key instructions | `docs/api-key-setup.md` | Per-agent setup (Claude Code, Cursor, etc.) | -| Update install instructions | `README.md` | 7 agent platforms + universal curl | -| Skill discovery metadata | `AGENTS.md` (this file) | `` XML block | - -## Conventions - -- **Default engine**: `google_light` — always recommend Light endpoints first for speed/cost. -- **API key placeholder**: Use `your_key_here` consistently (never hardcode real keys). -- **Env var name**: `SERPAPI_KEY` — standardized across all docs and examples. -- **Invocation order**: MCP tool (`serpapi_search`) → serpapi-cli → SDK (if writing code) → curl (last resort). -- **serpapi-cli agent tips**: Use `--fields` for server-side filtering (reduces API payload), `--jq` for client-side filtering. Combine both for minimum token usage. -- **curl examples**: All use `${SERPAPI_KEY}` variable reference. -- **SKILL.md frontmatter**: YAML block with `name`, `description`, `license` fields. -- **No executable code**: This repo is pure Markdown. No scripts, no tests, no build step. - -## Anti-Patterns - -- **Never** commit real API keys or hex strings that look like keys. -- **Never** reference competitor SERP scraping tools (e.g., Oxylabs, Scrapingbee) in any file. -- **Never** add executable code — this is a docs-only skill package. -- **Light vs Full**: Always default to Light engines. Only suggest full engine when user needs knowledge graph, local pack, or featured snippets. - -## Commands +## Repo map -No build/test/lint commands. Pure documentation repo. +- `README.md` — install instructions for 7 agent platforms +- `skills/serpapi-web-search/SKILL.md` — core skill: engines, parameters, examples +- `skills/serpapi-web-search/rules/` — ENGINES.md (133 engines), examples, parameters, response keys, use-cases, SDKs +- `skills/agent-usability-test/SKILL.md` — AUT methodology: protocol, scoring, fix→retest loop +- `skills/agent-usability-test/LESSONS.md` — empirical findings: isolation bugs, sample size, contamination +- `LICENSE` — MIT -```bash -# Verify no keys leaked -grep -rE "[a-f0-9]{32,}" --include="*.md" . +## Editing rules -# Validate internal links exist -grep -ohE '\(([^)]+\.md[^)]*)\)' *.md docs/*.md skills/**/*.md | tr -d '()' | sort -u -``` +- Every fact must be one agents can't derive from the `serpapi_search` tool schema + live responses. If it's in the schema, cut it. +- Never commit API keys. Placeholder: `your_key_here`. Env var: `SERPAPI_KEY`. +- Never reference competitor SERP scrapers. +- Prefer `_light` engine variants in examples (faster, cheaper). +- When adding an engine to the selection table, include its result key. -## Notes +## Line discipline -- **macOS gotcha**: `grep -P` (Perl regex) unavailable on Darwin — use `grep -E` or `sed` instead. -- **Link paths in AGENTS.md**: Must be relative to repo root (not to the file's parent). Previously had a broken `references/ENGINES.md` link — fixed to `skills/serpapi-web-search/rules/ENGINES.md`. -- **OpenCode packaging**: `.opencode/` contains a bundled copy of the skill + `@opencode-ai/plugin` dependency. This is separate from the canonical `skills/` directory. -- **Git history**: atomic commits on `master`. No branches, no CI pipeline. -- **Submissions**: Skill is publishable to agentskills.guide, SkillMD.ai, skills.rest, and ClawHub. See `.sisyphus/evidence/task-11-submissions.md` for submission details. +SKILL.md stays under 200 lines. Every addition needs a matching cut. diff --git a/README.md b/README.md index 97886e9..a5e34b2 100644 --- a/README.md +++ b/README.md @@ -1,169 +1,56 @@ -# SerpApi Search Skill +# serpapi-search-skill [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) - -Universal web search skill for AI coding agents with support for 100+ search engines. - -Search the web, news, images, shopping, videos, maps, flights, hotels, jobs, and academic databases directly from your AI agent. Powered by [SerpApi](https://serpapi.com). +Search the web from any AI agent — 100+ engines via one MCP tool. ## Quick Start -1. Get your API key from the [SerpApi Dashboard](https://serpapi.com/dashboard). -2. Set the environment variable: `export SERPAPI_KEY=your_key_here` -3. Install the skill: - ```bash - npx skills add serpapi/skills - ``` -4. Start searching! See [SKILL.md](skills/serpapi-web-search/SKILL.md) for usage. - -## What's Included - -- [SKILL.md](skills/serpapi-web-search/SKILL.md): Core skill definition — invocation, engine selection, composition patterns. -- [LESSONS.md](skills/serpapi-web-search/LESSONS.md): Deep knowledge for JIT injection — quota recovery, geo targeting, pagination, advanced patterns. -- [rules/ENGINES.md](skills/serpapi-web-search/rules/ENGINES.md): Catalog of 133 supported search engines. -- [rules/parameters.md](skills/serpapi-web-search/rules/parameters.md): All query parameters with examples. -- [rules/response.md](skills/serpapi-web-search/rules/response.md): Response format and result key reference. -- [rules/examples.md](skills/serpapi-web-search/rules/examples.md): CLI examples for common search types. -- [rules/use-cases.md](skills/serpapi-web-search/rules/use-cases.md): Multi-engine patterns, fan-out, usage estimation. -- [api-key-setup.md](docs/api-key-setup.md): Detailed configuration guide for all agents. -- [AGENTS.md](AGENTS.md): Discovery file for agent integration. -- [LICENSE](LICENSE): MIT License terms. - -## Installation - -The easiest way to install across all your agents at once: - -```bash -npx skills add serpapi/skills -``` - -This installs via the [skills CLI](https://github.com/vercel-labs/skills) and supports Claude Code, Cursor, Codex, OpenCode, Windsurf, and [40+ more agents](https://github.com/vercel-labs/skills#supported-agents). - -For agent-specific or manual installation: - -### Claude Code -```bash -git clone https://github.com/serpapi/skills.git -# Global install (available to all projects): -cp -r skills/serpapi-web-search ~/.claude/skills/ -# Project-scoped install: -cp -r skills/serpapi-web-search .claude/skills/ -``` -See [api-key-setup.md](docs/api-key-setup.md#claude-code) for MCP configuration. - -### Cursor -```bash -cp -r skills/serpapi-web-search .cursor/skills/ -``` -Or use the Remote Rules URL pointing to your repository's `SKILL.md`. - -### Codex -```bash -cp -r skills/serpapi-web-search .agents/skills/ -``` - -### Windsurf -```bash -cp -r skills/serpapi-web-search .windsurf/skills/ -``` +1. Get your key: [serpapi.com/dashboard](https://serpapi.com/dashboard) +2. Add to your MCP config (Cursor, Windsurf, Codex, Claude Desktop — same shape): -### OpenClaw -```bash -cp -r skills/serpapi-web-search ~/.openclaw/skills/ -``` - -### NemoClaw (inside sandbox) - -```bash -# 1. Install serpapi-cli inside the sandbox -go install github.com/serpapi/serpapi-cli/cmd/serpapi@latest -export SERPAPI_KEY=your_key_here - -# 2. Copy the skill into the workspace -cp -r skills/serpapi-web-search skills/serpapi-web-search - -# 3. Apply the network policy -openshell policy set skills/serpapi-web-search/serpapi.yaml - -# 4. Add to ~/.openclaw/openclaw.json -# { "skills": { "entries": { "serpapi-web-search": { "enabled": true, -# "apiKey": { "source": "env", "provider": "default", "id": "SERPAPI_KEY" } } } } } - -# 5. Make permanent -nemoclaw onboard -``` - -Or paste this into any AI assistant with access to your NemoClaw workspace: - -``` -Fetch https://raw.githubusercontent.com/serpapi/skills/main/skills/serpapi-web-search/SKILL.md -and save it to skills/serpapi-web-search/SKILL.md. - -Fetch https://raw.githubusercontent.com/serpapi/skills/main/skills/serpapi-web-search/serpapi.yaml -and save it to nemoclaw-blueprint/policies/presets/serpapi.yaml. - -Add this to ~/.openclaw/openclaw.json (home directory, not workspace): +```json { - "skills": { - "entries": { - "serpapi-web-search": { - "enabled": true, - "apiKey": { "source": "env", "provider": "default", "id": "SERPAPI_KEY" } - } + "mcpServers": { + "serpapi": { + "command": "npx", + "args": ["-y", "@serpapi/serpapi-mcp"], + "env": { "SERPAPI_KEY": "your_key_here" } } } } - -Then run: nemoclaw onboard ``` -### OpenCode +**Claude Code CLI:** ```bash -cp -r skills/serpapi-web-search .opencode/skills/ +claude mcp add serpapi -- npx -y @serpapi/serpapi-mcp ``` -OpenCode also automatically reads skills from `.claude/skills/` and `.agents/skills/`. +Set the key: `claude mcp env serpapi SERPAPI_KEY your_key_here` -### Universal (curl) -Download the skill definition directly to any directory: -```bash -curl -O https://raw.githubusercontent.com/serpapi/skills/main/skills/serpapi-web-search/SKILL.md -``` +## Verify -### serpapi CLI -If you prefer a CLI over raw curl, install the [serpapi CLI](https://github.com/serpapi/serpapi-cli): -```bash -brew install serpapi/tap/serpapi-cli -``` -Then search directly from your shell: -```bash -export SERPAPI_KEY=your_key_here -serpapi search engine=google_light q="coffee shops in Austin" -``` +Ask your agent: **"What search tools do you have?"** — expect `serpapi_search`. -## API Key Setup +## Local Execution -Configure your `SERPAPI_KEY` for secure access. Detailed instructions for environment variables, MCP settings, and CI/CD are available in [api-key-setup.md](docs/api-key-setup.md). +Replace `"args"` with `["-y", "@serpapi/serpapi-mcp", "--local"]` in the config above. -## Available Engines +## Why MCP -Search across 100+ platforms including Google, Bing, DuckDuckGo, YouTube, and Amazon. Use **Light** endpoints for faster responses and lower cost: +| Method | Agent discovery rate | +|--------|---------------------| +| MCP tool registration | ~100% | +| Skill file on disk | 0% | -- `google_light`: Fastest general web search (default). -- `google_images_light`: Optimized image search. -- `google_news_light`: Latest news results. -- `google_shopping_light`: Product pricing and availability. -- `google_videos_light`: Video search. -- `duckduckgo_light`: Privacy-focused web results. +## CI/CD -See [rules/ENGINES.md](skills/serpapi-web-search/rules/ENGINES.md) for the full list of 107 engines. +Set `SERPAPI_KEY` as a repo secret. Never commit keys. -## Links +## See Also -- [SerpApi Website](https://serpapi.com) -- [API Dashboard](https://serpapi.com/dashboard) -- [Search Playground](https://serpapi.com/playground) -- [Documentation](https://serpapi.com/search-api) +- [`skills/serpapi-web-search/SKILL.md`](skills/serpapi-web-search/SKILL.md) — engine selection, examples, parameters +- [`@serpapi/serpapi-mcp`](https://github.com/serpapi/serpapi-mcp) — MCP server source +- [`serpapi-cli`](https://github.com/serpapi/serpapi-cli) — terminal usage +- [serpapi.com/docs](https://serpapi.com/docs) — full API reference ## License -MIT License. See [LICENSE](LICENSE) for details. +MIT diff --git a/docs/api-key-setup.md b/docs/api-key-setup.md index a0cd109..12e4977 100644 --- a/docs/api-key-setup.md +++ b/docs/api-key-setup.md @@ -1,148 +1,40 @@ # API Key Setup -This guide covers how to get and set up your SerpApi API key for AI coding agents. - -## Getting Your API Key - -1. Sign up for an account at [serpapi.com](https://serpapi.com). -2. Go to the [Dashboard](https://serpapi.com/dashboard) to find your key. -3. Copy the API key for use in the configurations below. - -## Security Guidance - -* **Environment Variables**: Always store your key in an environment variable named `SERPAPI_KEY`. -* **Never Commit Keys**: Do not commit your API key to any version control system. -* **Rotate Keys**: If you suspect exposure, rotate your key immediately in the SerpApi dashboard. - -## Shell Environment - -For local development, add the key to your shell profile (`.zshrc` or `.bashrc`): +Get key at [serpapi.com/dashboard](https://serpapi.com/dashboard). Store as `SERPAPI_KEY` env var — never commit, rotate if exposed. ```bash export SERPAPI_KEY=your_key_here ``` -Reload your profile: - -```bash -source ~/.zshrc # or source ~/.bashrc -``` - -## Per-Agent Configuration - -Use the [serpapi-mcp](https://github.com/serpapi/serpapi-mcp) server for Model Context Protocol integration. - -### Claude Code - -**Option A — CLI (recommended):** - -```bash -claude mcp add serpapi -- npx -y @serpapi/serpapi-mcp -``` - -Then set your API key in the environment (see Shell Environment above) or pass it via `SERPAPI_KEY=your_key_here claude mcp add ...`. - -**Option B — Manual config:** - -Add to `~/.claude/settings.json` (global) or `.claude/settings.json` (project-scoped): +## MCP Config +Same JSON for Claude Code (`~/.claude/settings.json`), Cursor (`.cursor/mcp.json`), Windsurf (`.windsurf/mcp.json`): ```json { "mcpServers": { "serpapi": { "command": "npx", "args": ["-y", "@serpapi/serpapi-mcp"], - "env": { - "SERPAPI_KEY": "your_key_here" - } + "env": { "SERPAPI_KEY": "your_key_here" } } } } ``` -### Cursor - -Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global): - -```json -{ - "mcpServers": { - "serpapi": { - "command": "npx", - "args": ["-y", "@serpapi/serpapi-mcp"], - "env": { - "SERPAPI_KEY": "your_key_here" - } - } - } -} -``` +Claude Code CLI: `claude mcp add serpapi -- npx -y @serpapi/serpapi-mcp` -### Windsurf - -Add to `.windsurf/mcp.json`: - -```json -{ - "mcpServers": { - "serpapi": { - "command": "npx", - "args": ["-y", "@serpapi/serpapi-mcp"], - "env": { - "SERPAPI_KEY": "your_key_here" - } - } - } -} -``` - -### OpenCode - -Add to `.opencode/config.json` or rely on the shell environment variable above. - -### Codex / Other Agents - -Set the environment variable before launching the agent: - -```bash -export SERPAPI_KEY=your_key_here -``` - -## CI/CD Configuration - -### GitHub Actions - -Store your key as a [repository secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets) named `SERPAPI_KEY`, then reference it in your workflow: +## CI/CD +GitHub Actions ([store as secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets)): ```yaml -jobs: - search: - runs-on: ubuntu-latest - steps: - - name: Run agent with SerpApi - env: - SERPAPI_KEY: ${{ secrets.SERPAPI_KEY }} - run: | - echo "Agent will pick up SERPAPI_KEY from the environment" +env: + SERPAPI_KEY: ${{ secrets.SERPAPI_KEY }} ``` -### GitLab CI - -Store the key as a [CI/CD variable](https://docs.gitlab.com/ee/ci/variables/) in your project settings: - +GitLab CI: ```yaml -search-job: - script: - - echo "Agent will pick up SERPAPI_KEY from CI environment" - variables: - SERPAPI_KEY: $SERPAPI_KEY -``` - -### Generic / Docker - -Pass the key at runtime: - -```bash -docker run -e SERPAPI_KEY=your_key_here your-agent-image +variables: + SERPAPI_KEY: $SERPAPI_KEY ``` +Docker: `docker run -e SERPAPI_KEY=your_key_here your-agent-image` diff --git a/skills/agent-usability-test/LESSONS.md b/skills/agent-usability-test/LESSONS.md new file mode 100644 index 0000000..208e621 --- /dev/null +++ b/skills/agent-usability-test/LESSONS.md @@ -0,0 +1,45 @@ +# AUT Lessons + +## [tags: isolation, HOME, infrastructure] HOME=/tmp lobotomizes the agent, not just the skill (2026-07-03) + +Setting HOME=/tmp removes ALL skills, extensions, and MCP tools — not just the one under test. The WITHOUT condition becomes "agent with zero capabilities" vs WITH "fully configured agent." This isn't a controlled comparison. + +Fix: create a stripped HOME directory with agent auth config (so CLI runs) but no skills directory and no extension hooks. Verify by asking "what skills do you have?" in each condition. + +## [tags: scoring, ground-truth] Stale GTs cause systematic false negatives (2026-07-03) + +Ground truth "256" for Google Scholar citations that grew to 257,076. Agent correctly answered 257,076 → scored as FAIL. Flight price GT "$322" was correct same-day but would drift within days. + +Fix: verify GTs via the tool itself immediately before running trials. For volatile data (flights, prices), accept ±5% or re-verify within the run window. + +## [tags: parallelism, rate-limits] 8 parallel copilot calls → 67% ERROR rate (2026-07-03) + +8 concurrent `copilot -p` processes hit API rate limits. 81/120 returned non-zero exit (captured as "ERROR"). Reduced to MAX_PARALLEL=2 with 8s stagger delay → 0% errors. + +## [tags: sample-size, noise] N=3 produced +53pp lift that collapsed to +16pp at N=12 (2026-06-28) + +Sonnet 4.6 hard questions. N=3: WITH 87%, WITHOUT 27%. N=12: WITH 76%, WITHOUT 60%. The WITHOUT floor was artificially low in the small sample — Sonnet can scrape/fetch answers without the tool, just not every time. + +## [tags: difficulty, satisficing] Easy questions show 0% lift regardless of skill quality (2026-06-28) + +126 runs on easy questions (phone numbers, stock tickers). Both conditions: 100% correct. Tool discovery rate: 3.7%. Agents satisfice with web_search when it works. Only hard questions (where default tools fail) measure skill value. + +## [tags: contamination, training-data] Sonnet names SerpApi engines in 32% of WITHOUT runs (2026-06-28) + +With skill completely removed, Sonnet 4.6 says "I need google_flights but SERPAPI_KEY isn't set." It knows from training data (our docs are public). GPT-5.4 and Haiku 4.5: zero awareness without skill. Training-data contamination is model-specific and can't be fixed by better isolation. + +## [tags: auth, error-messages, haiku] Missing auth guidance caused -20pp for Haiku (2026-06-29) + +Original SKILL.md had no auth-check instructions. Haiku found the CLI, tried to call it, hit 401, burned retries, gave up. Tool actively hurt performance. Added auth-check block → zero auth failures in retest. + +## [tags: protocol, pilot] Always pilot 1 WITH + 1 WITHOUT before full matrix (2026-07-03) + +First full run (120 trials, 8 parallel, HOME=/tmp) produced garbage: 81 ERRORs, 0% discovery in WITH. Two pilots later: confirmed HOME isolation bug, rate limit threshold, and scoring format issues. Pilot costs 2 runs. Full matrix costs 240. Find infrastructure bugs at N=2, not N=240. + +## [tags: behavioral-vs-score] "Used the tool" ≠ "correct answer" (2026-07-03) + +Haiku retest: used serpapi CLI successfully in 3/3 manual trials (behavioral pass). But got wrong numbers because flight prices changed (score fail). These are independent metrics. Report both: discovery/usage rate (behavioral) AND correctness (score). Don't conflate. + +## [tags: task-design, information-asymmetry] 0pp lift means no information asymmetry, not broken docs (2026-07-03) + +Hotels and App Store both showed 0pp lift in 240-run retest. Initial diagnosis: documentation bugs (wrong JSON path, missing engine). Fixed both, retested N=12. Agents used the API correctly — still 0pp. Web search answers via Booking.com and AppBrain equally well. The task doesn't need the tool. Lesson: before attributing 0pp to doc quality, run 1 WITHOUT trial. If it passes, the task has no information asymmetry and will never show lift regardless of skill quality. Good AUT tasks: Scholar citations (no aggregator), live flight prices (not cached), Maps exact review counts (not on web). diff --git a/skills/agent-usability-test/SKILL.md b/skills/agent-usability-test/SKILL.md new file mode 100644 index 0000000..ac9e494 --- /dev/null +++ b/skills/agent-usability-test/SKILL.md @@ -0,0 +1,93 @@ +--- +name: agent-usability-test +description: >- + Test whether agents can discover and use your tool — not whether agents are + capable. The subject under test is the interface. A bad score means fix the + docs/tool, not the agent. +license: MIT +version: "1.0" +--- + +Give agent a goal. Make tool available. Don't mention the tool. Observe. +Run WITHOUT baseline. Delta = **lift** — the only metric that matters. +Lift is inversely proportional to model capability: docs matter most for weak models; strong models self-correct from API responses. + +## Failure modes + +| # | Failure | Signal | +|---|---------|--------| +| 1 | Non-discovery | Tool never called despite being available and relevant | +| 2 | Wrong selection | Agent picks suboptimal tool when multiple are available | +| 3 | Parameter cargo-culting | Agent copies doc examples instead of adapting | +| 4 | Response-schema blindness | Correct call, wrong field extracted | +| 5 | Auth/error cliff | 401/429/timeout → agent gives up instead of recovering | + +## Discovery by integration level + +``` +MCP tool registered ~100% (in agent's tool list) +System prompt hint ~50-80% (estimate) +CLI on $PATH ~30-50% (N=4) +File on disk 0% (N=12, 4 models) +``` + +Test at your deployment level. File-on-disk test for MCP-deployed tool = false negative. + +## Protocol + +1. **Hypothesize.** State expected outcome before running. Fisher's exact for N<20. +2. **Design tasks** with verifiable answers (binary: correct/incorrect). Don't encode methodology in the prompt. +3. **Verify ground truths same-day.** Query the source yourself before running trials. Stale GTs produce false negatives. +4. **Run matrix.** ≥2 models × 2 conditions (WITH/WITHOUT). Uncoached prompt: *"Answer this: [GOAL]. Cite your source."* +5. **Randomize run order.** Shuffle all (model, task, condition, trial) tuples. Sequential runs introduce temporal confounds (API rate limits, model load, price changes). +6. **Isolate WITHOUT completely.** HOME controls skill/extension loading in most agent CLIs — don't just set CWD=/tmp. Create a stripped HOME with agent auth config but no skills directory, no extensions. Tool off PATH. No env vars. Verify: ask the WITHOUT agent "what skills do you have?" — if it names your tool, isolation failed. If it names your tool despite correct isolation, that's training-data contamination — note it, don't fix it. +7. **Competition variant (FM#2):** Give ALL competing tools simultaneously. Score which gets picked. Three conditions: YOURS-ONLY, ALL-TOOLS, NONE. +8. **Observe via trace** — not self-report. Metrics: discovery rate, selection rate, efficiency (calls to correct answer), recovery rate, lift. +9. **Score binary per fact.** Automated substring or exact match. No 0-100 rubrics. No human judgment on borderline cases (define pass/fail criteria before running). +10. **Fix → Retest with control.** Fix docs, not agent. Run old-docs AND new-docs agents in same session — without a control, improvement could be model variance. + +## Adversarial conditions (tests your error messages, not agent intelligence) + +- **429 rate limit:** retry with backoff or give up? +- **Network timeout:** fall back or fail silently? +- **Malformed response:** handle unexpected JSON shape? +- **Deprecated endpoint:** find current one from error message? + +Score: binary (recovered / didn't). + +## Don't + +- Coach the agent ("use this tool") — tests reading, not behavior +- Ask agents to self-report friction +- Test one model only +- Skip the WITHOUT baseline +- Use 0-100 rubric scores +- Claim significance at N<10 +- Score with stale ground truths (verify same-day) +- Run WITHOUT from the repo directory (AGENTS.md leaks tool names) +- Run all WITH then all WITHOUT sequentially (randomize) +- Report behavioral observation ("used the tool") as score data ("correct answer") + +## Sample size + +N=1-3/cell → directional only (never publish) · N=10/cell → Fisher's exact, large effects · N=12/cell → 80% power, moderate effects · Always report N per cell, not total runs + +## Pre-flight checklist (run before committing to full matrix) + +1. [ ] GTs verified same-day via the tool itself (not from memory/training data) +2. [ ] Pilot: 1 run WITH — agent discovers and uses the tool? If not, fix infra. +3. [ ] Pilot: 1 run WITHOUT — agent has zero awareness of tool? If not, fix isolation. +4. [ ] Information asymmetry check: can web search answer this task? If yes, expect 0pp lift regardless of skill quality. Test with 1 WITHOUT run — if correct, the task is too easy. +5. [ ] Scoring function matches GT format (comma-separated numbers, decimal points, currency symbols) +6. [ ] Error rate <10% in pilot (rate limits, auth failures, timeouts) +7. [ ] Questions span ≥3 engines/capabilities (not all the same difficulty) + +## Not this + +| Approach | Tests | Subject | +|----------|-------|---------| +| WebBench/WebArena | Can agent complete web tasks? | Agent capability | +| API-Bank/ToolBench | Can agent follow API specs? | Agent tool-use skill | +| UXAgent/UXCascade | Is web UI usable for humans? | Human-facing interface | +| Search API benchmarks | Which API gives better results? | API output quality | +| **AUT** | **Can agents discover and use it?** | **Agent-facing interface** | diff --git a/skills/serpapi-web-search/LESSONS.md b/skills/serpapi-web-search/LESSONS.md index 7400ad5..bb09b29 100644 --- a/skills/serpapi-web-search/LESSONS.md +++ b/skills/serpapi-web-search/LESSONS.md @@ -1,60 +1,66 @@ # Lessons — serpapi-web-search ## [tags: quota, 429, rate-limit, fallback] Quota exhaustion recovery -- 429 means monthly limit reached. Check: `serpapi account` or dashboard. -- Immediate fallback: switch to `_light` variants (cheaper, same results for most queries). -- If already on `_light`: reduce `num` to 3, cache aggressively with `serpapi archive `. -- Cross-engine fallback order: `google_light` → `bing` → `duckduckgo` (different quota pools? No — all count against same key). -- `no_cache=true` burns an extra credit; never use it unless freshness is critical. -- Check `total_searches_left` (not `plan_searches_left`) — it includes extra_credits. - -## [tags: token, context, fields, jq, compact] Minimizing token usage in agent context -- `--fields "organic_results[0:5]"` is server-side — API returns only those fields, saving bandwidth. -- `--jq ".organic_results|[.[]|{title,link,snippet}]"` is client-side — filters after receiving full response. -- Combine both for minimum tokens: `--fields "organic_results[0:5]" --jq "[.[]|{title,link,snippet}]"`. -- MCP `mode="compact"` strips `search_metadata` + `search_parameters` (~200 tokens saved per call). -- For multi-page research, use `serpapi archive ` to retrieve previous results without re-querying. - -## [tags: location, geo, gl, hl, locale] Geographic targeting precision -- `gl=us` sets country but results may still be generic. Add `location=Austin, Texas` for city-level precision. -- `location` values must match SerpApi's canonical list: `serpapi locations q="Austin"` or [locations API](https://serpapi.com/locations-api). -- For local business queries, `google_maps` + `ll=@lat,lng,zoom` gives better results than `google_light` + `location`. -- `hl` affects language of UI chrome AND ranking weight of same-language pages. - -## [tags: pagination, all-pages, serpapi_pagination] Paginating through results -- `serpapi_pagination.next` in response contains the full URL for the next page — use it directly. -- CLI: `serpapi search --all-pages --max-pages 3` auto-paginates (concatenates all result arrays). -- Manual: increment `start` by `num` (e.g., `start=0`, `start=10`, `start=20` for pages 1-3). -- Some engines (google_maps, youtube) use cursor-based pagination — `next_page_token` instead of offset. - -## [tags: search_index, vespa, own-index] SerpApi's search_index engine -- First-party web index — no Google/Bing dependency, no scraping, no quota-per-result cost model. -- Best for: queries where you want reproducible, non-personalized results independent of Google's ranking. -- Limitations (alpha): smaller index than Google, no knowledge graph, no featured snippets. -- Same result structure as google_light: `organic_results` with `title`, `link`, `snippet`, `position`. +- 429 = monthly limit. Check: `serpapi account` → `total_searches_left` (includes extra_credits). +- Fallback order: switch to `_light` → reduce `num` to 3 → use `serpapi archive ` for re-reads. +- `no_cache=true` burns a credit; skip unless freshness critical. +- All engines share one quota pool (no per-engine pools). + +## [tags: token, context, fields, jq, compact] Minimizing token usage +- `--fields "organic_results[0:5]"` = server-side filter (API returns only those fields). +- `--jq "[.[]|{title,link,snippet}]"` = client-side transform (after full response received). +- Combine both for minimum tokens. For MCP: `mode="compact"` strips metadata (~200 tokens/call). +- `serpapi archive ` re-fetches without burning a credit. + +## [tags: location, geo, gl, hl, locale] Geographic targeting +- `gl=us` = country. Add `location=Austin, Texas` for city-level precision. +- Location values must match canonical list: `serpapi locations q="Austin"` or [locations API](https://serpapi.com/locations-api). +- For local queries: `google_maps` + `ll=@lat,lng,zoom` > `google_light` + `location`. +- `hl` affects both UI language AND ranking weight of same-language pages. + +## [tags: pagination, all-pages, serpapi_pagination] Pagination +- `serpapi_pagination.next` = full URL for next page — use directly. +- CLI: `--all-pages --max-pages 3` auto-concatenates result arrays. +- Manual: increment `start` by `num`. Some engines use `next_page_token` (Maps, YouTube). + +## [tags: search_index, own-index, first-party] SerpApi's search_index +- First-party web index — no Google/Bing dependency, no scraping. +- Best for: reproducible, non-personalized results independent of Google ranking. +- Alpha: smaller index, no knowledge graph, no featured snippets. +- Same structure as google_light: `organic_results` with `{title, link, snippet, position}`. - Supports: `q`, `num`, `start`, `safe`, `hl`, `gl`, `site:` operator. -## [tags: news, freshness, tbs, time-filter] Time-filtered search patterns -- `tbs=qdr:h` (past hour) — useful for breaking news, but may return few/no results for niche topics. -- `tbs=qdr:d` (past day) — good default for "latest" requests. -- `tbs=qdr:w` (past week) — best balance of freshness and coverage. -- For news specifically, prefer `google_news_light` over `google_light` + `tbs` — news engine has better freshness signals. -- Google Trends (`google_trends`) complements news by showing search volume spikes — use together for trend analysis. +## [tags: news, freshness, tbs, time-filter] Time-filtered search +- `tbs=qdr:h` (hour), `qdr:d` (day), `qdr:w` (week — best freshness/coverage balance). +- For "latest" requests: prefer `google_news_light` over `google_light` + `tbs` (better freshness signals). +- `google_trends` complements news with search volume spikes. ## [tags: maps, local, reviews, data_id] Local business intelligence -- Two-step pattern: (1) `google_maps q="business name city"` → get `data_id`, (2) `google_maps_reviews data_id=`. -- `google_maps` returns lat/lng, rating, reviews count, hours, phone — richer than google_light for local. -- For competitor analysis: search category + location (`q="coffee shop Austin TX"`), then pull reviews for top results. -- `sort_by=newestFirst` on reviews gives freshest signal; default sort is by relevance/rating. +- Two-step: `google_maps q="business city"` → grab `data_id` → `google_maps_reviews data_id=`. +- Maps returns lat/lng, rating, reviews count, hours, phone — richer than google_light for local. +- Competitor analysis: category + location (`q="coffee shop Austin TX"`) → reviews for top results. +- `sort_by=newestFirst` on reviews for freshest signal. -## [tags: scholar, academic, research, citation] Academic research patterns +## [tags: scholar, academic, research, citation] Academic research - `google_scholar q="topic" as_ylo=2024` — restrict to recent papers. -- Result includes `cited_by.total` — useful for gauging paper importance. -- For specific authors: `google_scholar_author author_id=` gives full publication list. -- Combine with `google_light q="paper title" site:arxiv.org"` to find preprints. +- `cited_by.total` in results = paper importance signal. +- `google_scholar_author author_id=` for full publication list. +- Combine with `google_light q="paper title site:arxiv.org"` for preprints. ## [tags: shopping, price, product, comparison] Product price intelligence -- `google_shopping_light` returns `price`, `extracted_price` (numeric), `source`, `link`. -- For price tracking: same query + `no_cache=true` at intervals (costs 1 credit per check). +- `extracted_price` field = numeric (for comparison). `source` = retailer name. +- Price tracking: same query + `no_cache=true` at intervals. - Cross-reference: `google_shopping_light` (aggregator) vs `amazon` engine (direct) for price gaps. -- `google_shopping_filters` returns available facets (brand, price range, condition) — useful for building filter UIs. +- `google_shopping_filters` returns facets (brand, price range, condition). + +## [tags: context, compaction, tokens, budget, agent-loop] Context pressure +- Agent runtimes auto-compact at 50–85% context window. Search results from early turns vanish. +- Always extract findings immediately after each search — don't defer. +- Budget-gated: check `total_searches_left` before fan-out. If < 20, single-engine mode + `num=5`. +- `serpapi archive ` re-fetches without credit cost — store IDs in working notes. + +## [tags: subagent, delegation, isolation, parallel, agent-sdk] Subagent delegation +- Subagent runtimes start with fresh context. Delegate search when parent context is large. +- Pattern: parent → subagent runs 3–5 searches → returns structured summary → parent continues. +- For parallel research: multiple subagents (one per topic), each with `serpapi_search` access. +- Extract facts/URLs into concise handoff, never pass raw JSON between agents. diff --git a/skills/serpapi-web-search/SKILL.md b/skills/serpapi-web-search/SKILL.md index 9bb4769..c0beeb7 100644 --- a/skills/serpapi-web-search/SKILL.md +++ b/skills/serpapi-web-search/SKILL.md @@ -1,141 +1,163 @@ --- name: serpapi-web-search description: >- - Search the web using SerpApi's 100+ search engines. Use this skill whenever - the user needs current or web-sourced information: researching a topic, - checking recent news, comparing products or prices, finding local businesses, - searching images or videos, or looking up academic papers, flights, hotels, - or stocks — even if they don't explicitly ask to "search the web." Default - to google_light for speed. Supports Google, Bing, DuckDuckGo, YouTube, - Amazon, Maps, Scholar, and more. + Structured search data via 130+ engines — use INSTEAD OF web_search when you + need: exact citations (google_scholar), local business details (google_maps), + flight prices (google_flights), hotel rates (google_hotels), shopping prices + (google_shopping_light), job listings (google_jobs), or any task where + web_search gives approximate/unstructured results. Returns machine-readable + JSON. Default engine: google_light. compatibility: >- - Requires one of: a native serpapi_search tool; or the serpapi CLI (brew install serpapi/tap/serpapi-cli); or an SDK; or outbound network - access with curl. All paths require a SERPAPI_KEY. + Requires: serpapi_search MCP tool, or serpapi CLI, or SDK, or curl. + All paths need SERPAPI_KEY. license: MIT --- -## Invocation - -Use the first available method: - -**1. MCP tool** — use `serpapi_search` if available ([source](https://github.com/serpapi/serpapi-mcp)): -``` -serpapi_search(params={"engine": "google_light", "q": "query", "num": 20}, mode="compact") -``` -`mode="compact"` strips `search_metadata` and `search_parameters` — smaller context, same results. +You have `serpapi_search`. This file helps you pick the right engine, +extract the right response key, and avoid common mistakes. -**2. serpapi-cli** — preferred shell fallback; optimized for AI agents ([source](https://github.com/serpapi/serpapi-cli)): +**Auth check — do this first if you get 401 or haven't used serpapi before:** ```bash -serpapi search engine=google_light q="your query" num=20 +# Check if already authenticated: +serpapi account 2>&1 | head -1 +# If "Active" → you're good. If not: +serpapi login # interactive — stores key persistently +# Or set env: export SERPAPI_KEY= ``` -Install: `brew install serpapi/tap/serpapi-cli` -Auth: `SERPAPI_KEY` env var, `--api-key` flag, or `serpapi login`. -Exit codes: `0` success · `1` API error · `2` usage error. Errors are JSON on stderr. -For `--fields` / `--jq` filtering: see [rules/examples.md](rules/examples.md). - -**Result count:** Default to `num=20`. More results = better context for the model. Use `num=10` for simple lookups, `num=3` only for single-fact verification. +If `serpapi` is not on PATH: install with `brew install serpapi/tap/serpapi-cli`. +If no MCP tool and no CLI: use curl with `api_key=${SERPAPI_KEY}` param (see below). -**Token efficiency** — minimize context window usage: -```bash -# Only return organic results (drop metadata, ads, related searches) -serpapi search --fields "organic_results" engine=google_light q="query" +## Invocation -# Extract just title+link+snippet — smallest useful payload -serpapi search --jq "[.organic_results[]|{title,link,snippet}]" engine=google_light q="query" ``` -With MCP: use `mode="compact"` to strip metadata automatically. +serpapi_search(params={"engine": "google_light", "q": "", "num": 20}, mode="compact") +``` -**3. SDK** — when writing code: see [rules/sdks.md](rules/sdks.md) — Python, JS, Go, Ruby, PHP, Java, .NET. +`mode="compact"` strips metadata — same results, ~200 fewer tokens. +Default `num=20`. Use `num=10` for simple lookups, `num=3` for single-fact verification. +Empty results ≠ error. `organic_results` may be absent on 200 — widen query or switch engine. -**4. curl** — universal fallback: +**CLI fallback** ([serpapi-cli](https://github.com/serpapi/serpapi-cli)): ```bash -curl -G "https://serpapi.com/search.json" \ - --data-urlencode "q=your query" \ - --data-urlencode "engine=google_light" \ - --data-urlencode "api_key=${SERPAPI_KEY}" +serpapi search engine=google_light q="query" num=20 ``` - -## Engine Selection - -Pick the engine that matches the user's intent: - -| Use Case | Engine | -|:---|:---| -| **General web — default for AI agents** | `google_light` ⚡ | -| Comprehensive (knowledge graph, local pack, featured snippets) | `google` | -| News | `google_news_light` | -| Images | `google_images_light` | -| Shopping / prices | `google_shopping_light` | -| Flights | `google_flights` | -| Hotels | `google_hotels` | -| Jobs | `google_jobs` | -| Alternative web | `bing` | -| Privacy-first | `duckduckgo` | -| Academic / research | `google_scholar` | -| Local / maps | `google_maps` | -| Video | `youtube` | -| **SerpApi's own crawled index** | `search_index` 🔬 | - -**For AI/LLM agents:** `google_light` is the recommended default — it has the lowest latency, smallest response payload, and returns clean organic results without the noise of the full `google` engine. Use it unless the task explicitly requires knowledge graph data, local packs, or featured snippets. - -**`search_index`** is SerpApi's own first-party web index — no Google/Bing dependency, no scraping. It is in active development and improving rapidly. Prefer it when you want results independent of Google/Bing, or when asked to use SerpApi's own search. It will be the best LLM-native search option as it matures. - -Prefer `_light` variants — they're faster and cheaper. Use the full engine only when you need knowledge graph, local pack, or featured snippets. - -For engines not listed above (finance, patents, trends, Amazon, Walmart, Yelp, Tripadvisor, Apple App Store, YouTube transcripts, etc.), read [rules/ENGINES.md](rules/ENGINES.md). - -## Composition Patterns - -**Research fan-out** — answer complex questions by querying multiple surfaces in parallel: +For `--fields`/`--jq` filtering: [rules/examples.md](rules/examples.md). +For SDKs (Python/JS/Go/Ruby/PHP/Java/.NET): [rules/sdks.md](rules/sdks.md). +For curl: `curl -G "https://serpapi.com/search.json" --data-urlencode "q=..." --data-urlencode "engine=google_light" --data-urlencode "api_key=${SERPAPI_KEY}"` + +## Engine selection + +Pick by intent. Prefer `_light` variants (faster, cheaper, cleaner JSON). + +| Intent | Engine | Result key | Key fields | +|---|---|---|---| +| General web (default) | `google_light` | `organic_results` | `.title`, `.link`, `.snippet` | +| Knowledge graph / featured snippets | `google` | `organic_results` + many | `.knowledge_graph`, `.answer_box` | +| News | `google_news_light` | `news_results` | `.title`, `.link`, `.date` | +| Images | `google_images_light` | `images_results` | `.original`, `.thumbnail` | +| Shopping / prices | `google_shopping_light` | `shopping_results` | `.title`, `.price`, `.source` | +| Academic papers | `google_scholar` | `organic_results` | `.title`, `.inline_links.cited_by.total` | +| Local businesses (list) | `google_maps` | `local_results` | `.title`, `.phone`, `.address`, `.rating`, `.reviews` | +| Local business (single) | `google_maps` | `place_results` | `.title`, `.phone`, `.address`, `.rating`, `.reviews` | +| Place reviews | `google_maps_reviews` | `reviews` | `.rating`, `.snippet`, `.date` | +| Video | `youtube` | `video_results` | `.title`, `.link`, `.views`, `.length` | +| Stock / ticker | `google_finance` | `summary` | `.price`, `.exchange`, `.currency` | +| Flights | `google_flights` | `best_flights` | `.flights[].airline`, `.price`, `.total_duration` | +| Hotels | `google_hotels` | `properties` | `.name`, `.rate_per_night.extracted_lowest`, `.total_rate.extracted_lowest`, `.overall_rating` | +| Jobs | `google_jobs` | `jobs_results` | `.title`, `.company_name`, `.location` | +| App Store (iOS) | `apple_app_store` | `organic_results` | `.title`, `.rating[0].rating`, `.rating[0].count`, `.developer.name` | +| Alternative web | `bing`, `duckduckgo` | `organic_results` | `.title`, `.link`, `.snippet` | +| SerpApi's own index (alpha) | `search_index` | `organic_results` | `.title`, `.link`, `.snippet` | + +All 130+ engines: [rules/ENGINES.md](rules/ENGINES.md) · Online: [serpapi.com/search-engine-apis](https://serpapi.com/search-engine-apis) + +## Gotchas + +- **Shopping = third-party reseller prices.** For a specific retailer's price, use `google_light` with `site:` (e.g., `q="MacBook Air M4 site:apple.com"`). Google Shopping aggregates from feeds — prices may not match the retailer's own site (e.g., Target sale prices may lag). +- **Maps returns `place_results` OR `local_results`** — named business → `place_results`; category search → `local_results`. Always check both keys. +- **Finance returns `summary`, not `organic_results`.** Same for Flights (`best_flights`), Hotels (`properties`). +- **Scholar citation count** is at `.organic_results[0].inline_links.cited_by.total` — not a top-level field. Use `--jq '.organic_results[0].inline_links.cited_by.total'` to extract. +- **Maps review count** is at `.place_results.reviews` (integer) or `.local_results[].reviews`. Rating at `.rating`. +- **Flights require specific params** — not `q`. Use `departure_id=JFK arrival_id=LAX outbound_date=2026-07-10 type=2` (type 2 = one-way). +- **Hotels require dates** — `q="hotels in Kyoto" check_in_date=2026-07-20 check_out_date=2026-07-22 adults=2`. Price is at `.properties[].rate_per_night.extracted_lowest` (per night) or `.total_rate.extracted_lowest` (total stay). Sort by price: `sort_by=8`. +- **Apple App Store uses `term`** — not `q`. Rating is nested: `.organic_results[0].rating[0].rating` (float, e.g. 4.78). +- **Non-standard query params:** + + | Engine | Param (not `q`) | + |---|---| + | `youtube` | `search_query` | + | `amazon` | `k` | + | `ebay` | `_nkw` | + | `walmart` | `query` | + | `google_maps_reviews` | `data_id` | + | `google_flights` | `departure_id` + `arrival_id` + `outbound_date` | + | `google_hotels` | `q` + `check_in_date` + `check_out_date` + `adults` | + | `apple_app_store` | `term` (not `q`) | + +## Parameters + +Most tasks need only `engine`, `q`, `num`. Add when relevant: + +| Param | Use | +|---|---| +| `gl` | Country code (`us`, `uk`, `de`). Default `us`. | +| `hl` | Language (`en`, `es`, `fr`). Affects ranking. | +| `location` | City string (`"Austin, Texas"`). Overrides `gl`. | +| `tbs` | Time: `qdr:d` (day), `qdr:w` (week), `qdr:m` (month), `qdr:y` (year). | +| `start` | Pagination offset. Prefer `serpapi_pagination.next` when present. | +| `no_cache` | `"true"` = live crawl (costs 1 credit). | + +Full reference: [rules/parameters.md](rules/parameters.md) · Locations: [serpapi.com/locations-api](https://serpapi.com/locations-api) + +## Composition + +**Fan out** for research (parallel, not sequential): ```bash -# "What's the market outlook for AAPL?" → 3 parallel calls serpapi search engine=google_finance q="AAPL:NASDAQ" & -serpapi search engine=google_news_light q="Apple earnings 2026" & +serpapi search engine=google_news_light q="Apple earnings" & serpapi search engine=google_light q="AAPL analyst consensus" num=5 & wait ``` -**Progressive refinement** — start narrow, widen on empty results: -1. `google_light q="exact phrase" num=5` — try exact match first -2. If empty: broaden query terms, drop quotes -3. If still sparse: add `tbs=qdr:y` (past year) or switch engine (`bing`, `duckduckgo`) - -**Verification loop** — cross-reference claims across engines: +**Common exact-data extractions** (copy-paste patterns): ```bash -# Verify a fact from multiple independent sources -serpapi search engine=google_light q="claim to verify" num=3 -serpapi search engine=bing q="claim to verify" num=3 -# Compare: if both agree → high confidence; if they diverge → flag uncertainty +# Exact citation count +serpapi search engine=google_scholar q="paper title" --jq '.organic_results[0].inline_links.cited_by.total' + +# Business phone + rating + reviews +serpapi search engine=google_maps q="Business Name City" --jq '.place_results | {phone, rating, reviews}' + +# Live flight price +serpapi search engine=google_flights departure_id=JFK arrival_id=LAX outbound_date=2026-07-10 type=2 --jq '.best_flights[0] | {price, airline: .flights[0].airline}' + +# Shopping prices by retailer +serpapi search engine=google_shopping_light q="Product Name" --jq '[.shopping_results[:5] | .[] | {title, price, source}]' ``` -For more patterns (brand monitoring, product catalog, local business): [rules/use-cases.md](rules/use-cases.md). +**Progressive refinement:** exact phrase → drop quotes → add `tbs=qdr:y` → switch engine. + +**Two-step reviews:** `google_maps q="business"` → grab `data_id` → `google_maps_reviews data_id=`. -## Error Reference +**Cross-check:** same query on `google_light` + `bing` — both agree → high confidence. -- **401** — Invalid or missing API key. -- **429** — Monthly quota reached. Check usage: `serpapi account` or [serpapi.com/dashboard](https://serpapi.com/dashboard) · [account API](https://serpapi.com/account-api). -- **400** — Missing required parameter (`q` or `engine`). +**Extract inline.** After each search, pull `{title, link, snippet}` into working notes. Don't rely on raw results surviving context compaction. -## Docs +More patterns: [rules/use-cases.md](rules/use-cases.md) -Official reference (link these when agents need deeper detail): +## Errors -| Topic | URL | -|:---|:---| -| Main API reference | [serpapi.com/search-api](https://serpapi.com/search-api) | -| All engines (online) | [serpapi.com/search-engine-apis](https://serpapi.com/search-engine-apis) | -| Locations lookup | [serpapi.com/locations-api](https://serpapi.com/locations-api) | -| Account & quota API | [serpapi.com/account-api](https://serpapi.com/account-api) | -| Search Index API (alpha) | [serpapi.com/search-index-api](https://serpapi.com/search-index-api) | -| Pricing | [serpapi.com/pricing](https://serpapi.com/pricing) | +| Code | Meaning | Fix | +|---|---|---| +| 400 | Missing `q` or `engine` | Add the required param. | +| 401 | Invalid API key | Run `serpapi login` or set `SERPAPI_KEY=`. Do NOT retry with the same key. | +| 429 | Quota exhausted | Switch to `_light`, reduce `num`, check [dashboard](https://serpapi.com/dashboard). | -## Rules +If you get 401: the key is wrong or missing. Do not loop — fix the env var first. +Billing: only successful searches count. Same query + params = free cached result for 1 hour. -Read these files when you need more detail: +## Reference links -- **Parameters** (locale, time filter, pagination, safe search): [rules/parameters.md](rules/parameters.md) -- **Response format** (result keys, JSON shape, pagination): [rules/response.md](rules/response.md) -- **Examples** (news, shopping, time-filtered, Bing): [rules/examples.md](rules/examples.md) -- **SDKs** (Python, JS, Go, Ruby, PHP, Java, .NET): [rules/sdks.md](rules/sdks.md) -- **All 100+ engines** (flights, hotels, jobs, finance, patents…): [rules/ENGINES.md](rules/ENGINES.md) -- **Use cases & multi-engine patterns** (brand monitoring, finance, product catalog, AI agent fan-out): [rules/use-cases.md](rules/use-cases.md) +- [serpapi.com/search-api](https://serpapi.com/search-api) — full API docs +- [serpapi.com/search-engine-apis](https://serpapi.com/search-engine-apis) — all engines +- [serpapi.com/pricing](https://serpapi.com/pricing) — credits & plans +- [github.com/serpapi](https://github.com/serpapi) — SDKs, CLI, MCP server diff --git a/skills/serpapi-web-search/rules/examples.md b/skills/serpapi-web-search/rules/examples.md index 14543ac..313e1df 100644 --- a/skills/serpapi-web-search/rules/examples.md +++ b/skills/serpapi-web-search/rules/examples.md @@ -1,93 +1,84 @@ # SerpApi Examples -All examples use `serpapi-cli` (preferred). For curl equivalents, swap `serpapi search engine=X q=Y` with `curl -G "https://serpapi.com/search.json" --data-urlencode "q=Y" --data-urlencode "engine=X" --data-urlencode "api_key=${SERPAPI_KEY}"`. +All examples use `serpapi-cli`. curl: `curl -G "https://serpapi.com/search.json" --data-urlencode "q=Y" --data-urlencode "engine=X" --data-urlencode "api_key=${SERPAPI_KEY}"`. -## Google News - -Latest news on a topic: +> Some engines use non-standard query params: `search_query` (YouTube), `k` (Amazon), `data_id` (Google Maps Reviews). See [parameters.md](parameters.md). +## Google News ```bash serpapi search engine=google_news_light q="artificial intelligence" ``` - Result key: `news_results` ## Google Shopping - -Product prices and availability: - ```bash serpapi search engine=google_shopping_light q="iphone 16 pro" ``` - Result key: `shopping_results` -## Bing Web Search - -Alternative web results (cross-reference or privacy): - -```bash -serpapi search engine=bing q="serpapi documentation" -``` - -Result key: `organic_results` - ## Time-Filtered Search - -Results from the past week: - ```bash serpapi search engine=google_light q="latest AI models" tbs=qdr:w ``` - See [parameters.md](parameters.md) for all `tbs` values. -## Result Filtering - -Fetch only what you need — fewer tokens, faster processing: +## Result Filtering (`--fields` / `--jq`) ```bash -# Server-side: only return top 10 organic results (reduces API payload) +# Server-side: reduces API payload serpapi search --fields "organic_results[0:10]" engine=google_light q="coffee" -# Client-side: extract title + link + snippet after receiving full response +# Client-side: transform after receiving full response serpapi search --jq ".organic_results[0:10]|[.[]|{title,link,snippet}]" engine=google_light q="coffee" -# Both combined: minimum bandwidth + minimum context window tokens +# Both: minimum bandwidth + minimum tokens serpapi search --fields "organic_results[0:10]" --jq "[.organic_results[]|{title,link,snippet}]" engine=google_light q="coffee" ``` -## Search Index (SerpApi's Own Index) +curl (`fields=` + `jq`): +```bash +curl -s -G "https://serpapi.com/search.json" \ + --data-urlencode "engine=google_light" --data-urlencode "q=coffee" \ + --data-urlencode "api_key=${SERPAPI_KEY}" --data-urlencode "fields=organic_results" \ + | jq '[.organic_results[]|{title,link,snippet}]' +``` -Query SerpApi's first-party web index — no Google/Bing dependency, direct index access: +`--fields` maps to `fields=` in REST API. `--jq` is CLI-only. +## Google Maps ```bash -serpapi search engine=search_index q="serpapi documentation" - -# With field filtering -serpapi search --jq ".organic_results[0:10]|[.[]|{title,link,snippet}]" engine=search_index q="coffee" +serpapi search engine=google_maps q="The French Laundry Yountville California" +serpapi search engine=google_maps q="coffee shops" ll="@37.7749,-122.4194,15z" ``` -Result key: `organic_results` (same structure as `google_light`) +## Google Finance +```bash +serpapi search engine=google_finance q="AAPL:NASDAQ" --jq '.summary | {price, currency, previous_close}' +``` +Result keys: `summary`, `graph`, `news_results` -## Paginate All Results +## Search Index +```bash +serpapi search engine=search_index q="serpapi documentation" +serpapi search --jq ".organic_results[0:10]|[.[]|{title,link,snippet}]" engine=search_index q="coffee" +``` +Result key: `organic_results` +## Pagination ```bash serpapi search engine=google q="coffee" --all-pages --max-pages 3 ``` -## Retrieve a Cached Search - -Every SerpApi response includes a `search_metadata.id`. Retrieve it later without an extra quota cost: - +## Non-Standard Query Parameters ```bash -serpapi archive +serpapi search engine=youtube search_query="machine learning tutorial" +serpapi search engine=amazon k="wireless headphones" +serpapi search engine=google_maps_reviews data_id="0x89c25090129c363d:0x40c6a5770d25022b" +serpapi search engine=ebay _nkw="vintage watch" ``` +Result keys: YouTube → `video_results`, Amazon → `organic_results`, Maps Reviews → `reviews`, eBay → `organic_results`. -## Account Usage - -Check remaining quota: - +## Cached Search ```bash -serpapi account +serpapi archive ``` diff --git a/skills/serpapi-web-search/rules/parameters.md b/skills/serpapi-web-search/rules/parameters.md index 5ae2c19..94eb9bc 100644 --- a/skills/serpapi-web-search/rules/parameters.md +++ b/skills/serpapi-web-search/rules/parameters.md @@ -1,13 +1,13 @@ # SerpApi Parameters -All parameters for `GET https://serpapi.com/search.json`. +`GET https://serpapi.com/search.json` ## Required | Parameter | Type | Description | |:---|:---|:---| -| `engine` | string | The search engine to use (e.g., `google_light`). | -| `q` | string | The search query. Required for most engines. Exceptions: `youtube` uses `search_query`; `amazon` uses `k`; `instagram_profile` uses `profile_id`; `google_maps_reviews` uses `data_id`. | +| `engine` | string | Search engine to use (e.g., `google_light`). | +| `q` | string | Search query. Most engines use `q`; exceptions: `youtube` → `search_query`, `amazon` → `k`, `instagram_profile` → `profile_id`, `google_maps_reviews` → `data_id`. Per-engine docs: [serpapi.com/search-engine-apis](https://serpapi.com/search-engine-apis). | | `api_key` | string | Your SerpApi API key. | ## Pagination @@ -15,26 +15,21 @@ All parameters for `GET https://serpapi.com/search.json`. | Parameter | Type | Description | |:---|:---|:---| | `num` | integer | Results per page (max 100, default 10). | -| `start` | integer | Result offset. Use `start=10&num=10` for page 2. | +| `start` | integer | Result offset. `start=10&num=10` = page 2. | ## Locale Targeting -Set these based on the user's context — they improve result relevance and ranking order. - | Parameter | Type | Description | |:---|:---|:---| | `gl` | string | Country code (e.g., `us`, `uk`, `de`). Default: `us`. | | `hl` | string | Language code (e.g., `en`, `es`, `fr`). Default: `en`. | -| `location` | string | Canonical city/region for precise geo-targeting (e.g., `Austin, Texas`). Takes precedence over `gl`. Look up valid values at [serpapi.com/locations-api](https://serpapi.com/locations-api). | +| `location` | string | Canonical city/region (e.g., `Austin, Texas`). Overrides `gl`. Valid values: [serpapi.com/locations-api](https://serpapi.com/locations-api). | -**Example — French results from France:** ```bash serpapi search engine=google_light q="restaurants paris" gl=fr hl=fr ``` -## Time Filtering - -Use `tbs` to restrict results to a time range. +## Time Filtering (`tbs`) | Value | Meaning | |:---|:---| @@ -43,7 +38,6 @@ Use `tbs` to restrict results to a time range. | `qdr:m` | Past month | | `qdr:y` | Past year | -**Example — news from the past week:** ```bash serpapi search engine=google_light q="AI announcements" tbs=qdr:w ``` @@ -52,26 +46,17 @@ serpapi search engine=google_light q="AI announcements" tbs=qdr:w | Parameter | Type | Description | |:---|:---|:---| -| `safe` | string | Safe search: `active` or `off`. | -| `no_cache` | string | Pass `"true"` to bypass cached results and force a live crawl. | -| `zero_trace` | string | Enterprise only. Pass `"true"` to enable ZeroTrace (ZDR — Zero Data Retention) — search parameters, files, and metadata are not stored on SerpApi servers. Works on all engines. | -| `device` | string | `desktop` (default), `tablet`, or `mobile`. | -| `async` | string | Pass `"true"` to submit search and retrieve later via [Searches Archive API](https://serpapi.com/search-archive-api). | -| `output` | string | `json` (default) or `html` (raw HTML from the search engine). | - -**Parameter conflicts:** -- `no_cache` + `async` — **incompatible** (per docs: "should not be used together"). -- `async` should not be used on accounts with [Ludicrous Speed](https://serpapi.com/ludicrous-speed) enabled. -- `zero_trace` — works with all engines and all other parameters. Searches won't appear in your dashboard or archive. +| `safe` | string | `active` or `off`. | +| `no_cache` | string | `"true"` forces live crawl. | +| `zero_trace` | string | Enterprise. ZDR — no data stored on SerpApi. | +| `device` | string | `desktop` (default), `tablet`, `mobile`. | +| `async` | string | Async submit; retrieve via [Archive API](https://serpapi.com/search-archive-api). | +| `output` | string | `json` (default) or `html`. | -## Caching & Billing +**Conflicts:** `no_cache` + `async` incompatible. `async` not for [Ludicrous Speed](https://serpapi.com/ludicrous-speed). `zero_trace` always costs 1 credit. -- Only **successful** searches count toward your quota. Cached, errored, and failed searches are free. -- Cache: same query + same parameters = free cached result for 1 hour. -- `no_cache=true` forces a live crawl (costs 1 credit). -- `zero_trace=true` searches always cost 1 credit (no caching possible). -- Response size doesn't matter — 100 results or 0 results both count as 1 search. -- Check quota: `serpapi account` → look at `total_searches_left` (includes extra_credits). +## Caching & Billing ---- -Full parameter reference: [serpapi.com/search-api](https://serpapi.com/search-api) · Locations lookup: [serpapi.com/locations-api](https://serpapi.com/locations-api) · ZeroTrace: [serpapi.com/zero-trace-mode](https://serpapi.com/zero-trace-mode) +- Successful searches only count toward quota. Same query+params = free cache for 1 hour. +- `no_cache=true` forces live crawl (1 credit). Response size doesn't affect cost. +- Check quota: `serpapi account` → `total_searches_left`. diff --git a/skills/serpapi-web-search/rules/response.md b/skills/serpapi-web-search/rules/response.md index f441dde..ae0992d 100644 --- a/skills/serpapi-web-search/rules/response.md +++ b/skills/serpapi-web-search/rules/response.md @@ -1,8 +1,6 @@ # SerpApi Response Format -## JSON Structure - -All engines return JSON with `search_metadata` and `search_parameters` at the top level. Result arrays and `serpapi_pagination` are present when results exist; they are absent on empty results or errors. +All engines return JSON with `search_metadata` and `search_parameters` at the top level. Result arrays and `serpapi_pagination` are absent on empty results or errors. ```json { @@ -20,12 +18,10 @@ All engines return JSON with `search_metadata` and `search_parameters` at the to } ``` -When using `mode="compact"` with the native tool, `search_metadata` and `search_parameters` are stripped from the response. +`mode="compact"` (native tool) strips `search_metadata` and `search_parameters`. ## Result Key by Engine -Different engines use different top-level array keys for their results: - | Engine Category | Result Key | |:---|:---| | Web (`google_light`, `google`, `bing`, `duckduckgo`) | `organic_results` | @@ -34,18 +30,16 @@ Different engines use different top-level array keys for their results: | Shopping (`google_shopping_light`, `google_shopping`) | `shopping_results` | | Amazon, Walmart, eBay | `organic_results` | | Jobs (`google_jobs`) | `jobs_results` | -| Maps (`google_maps`) | `local_results` | +| Maps (`google_maps`) — list | `local_results` | +| Maps (`google_maps`) — single place | `place_results` | | Maps Reviews (`google_maps_reviews`) | `reviews` | | Videos (`google_videos_light`) | `video_results` | | YouTube (`youtube`) | `video_results` | | Scholar (`google_scholar`) | `organic_results` | | Flights (`google_flights`) | `best_flights`, `other_flights` | -| Finance (`google_finance`) | `summary`, `graph`, `news_results` (multiple top-level keys) | +| Finance (`google_finance`) | `summary`, `graph`, `news_results` | | Trends (`google_trends`) | `interest_over_time`, `related_queries`, `related_topics` | ## Pagination -Use `serpapi_pagination.next` as the URL for the next page of results. It includes all current parameters plus the correct pagination offset for the engine — pass it directly without modification. - ---- -Full response schema: [serpapi.com/search-api](https://serpapi.com/search-api) +Use `serpapi_pagination.next` for the next page — includes all current params plus correct offset. Pass directly without modification. diff --git a/skills/serpapi-web-search/rules/use-cases.md b/skills/serpapi-web-search/rules/use-cases.md index 38ba3cf..b9c2b57 100644 --- a/skills/serpapi-web-search/rules/use-cases.md +++ b/skills/serpapi-web-search/rules/use-cases.md @@ -1,6 +1,4 @@ -# SerpApi Use Cases & Multi-Engine Patterns - -Common use cases, the engine stacks that serve them, and parallel query patterns for AI agents. +# SerpApi Use Cases ## Use Case → Engine Mapping @@ -22,88 +20,40 @@ Common use cases, the engine stacks that serve them, and parallel query patterns ## Multi-Engine Parallel Pattern -Run independent engine calls concurrently — don't serialize them. +```bash +serpapi search engine=google_news_light q='"Acme Corp" lawsuit' & +serpapi search engine=google_light q='site:sec.gov "Acme Corp"' & +wait +``` ```python import serpapi, asyncio - client = serpapi.Client(api_key="your_key_here") -async def search(params): - return await asyncio.to_thread(client.search, params) - async def main(): - # Brand risk monitoring: 3 surfaces in parallel results = await asyncio.gather( - search({"engine": "google_news_light", "q": '"Acme Corp" (lawsuit OR breach OR recall)'}), - search({"engine": "google_light", "q": 'site:sec.gov "Acme Corp"'}), - search({"engine": "google_maps", "q": "Acme Corp headquarters"}), + asyncio.to_thread(client.search, {"engine": "google_news_light", "q": '"Acme Corp" (lawsuit OR breach OR recall)'}), + asyncio.to_thread(client.search, {"engine": "google_light", "q": 'site:sec.gov "Acme Corp"'}), + asyncio.to_thread(client.search, {"engine": "google_maps", "q": "Acme Corp headquarters"}), ) - news, web, maps = results - return news, web, maps + return results asyncio.run(main()) ``` -```bash -# CLI: run in parallel with & and wait -serpapi search engine=google_news_light q='"Acme Corp" lawsuit' & -serpapi search engine=google_light q='site:sec.gov "Acme Corp"' & -wait -``` - -## AI Agent Fan-Out Pattern - -One user prompt typically triggers multiple sub-queries. Plan capacity accordingly. - -``` -1 user question - → 1 primary web query (google_light) - → 1 news freshness check (google_news_light) - → 1–3 follow-up clarifications (google_light, num=3) - ───────────────────────────────────────────── - = 3–5 API calls per agent turn -``` - -For research agents with citation requirements, multiply by the number of claims to verify. A 10-claim research report typically generates 20–50 API calls. - -## Usage Estimation - -``` -monthly_searches = entities × query_variants × engines × cadence_multiplier - -cadence_multiplier: - real-time / continuous → 30 (daily) to 720 (hourly) - daily → 30 - weekly → 4 - on-demand / batch → 1 -``` - -### Examples by segment - -| Segment | Formula | Example | -|---|---|---| -| Ticker enrichment | tickers × engines × queries/ticker × monthly refreshes | 500 × 3 × 5 × 4 = 30,000/mo | -| Brand monitoring | entities × query_variants × engines × cadence | 200 × 3 × 3 engines × 30 days = 54,000/mo | -| Product catalog | SKUs × (organic + shopping) × refresh rate | 1,000 × 2 × 8 = 16,000/mo | -| KYB verification | applications/mo × surfaces/entity | 500 × 4 = 2,000/mo | -| AI agent (research) | sessions/day × fan-out × 30 | 100 × 15 × 30 = 45,000/mo | - ## Financial Intelligence Stack ```bash -# Price + news + trends for a ticker — 3 parallel calls -serpapi search engine=google_finance q="AAPL:NASDAQ" & -serpapi search engine=google_news_light q='"AAPL" (earnings OR acquisition OR investigation)' & -serpapi search engine=google_trends q="Apple stock" geo=US date="today 3-m" & +serpapi search engine=google_finance q="AAPL:NASDAQ" & +serpapi search engine=google_news_light q='"AAPL" (earnings OR acquisition OR investigation)' & +serpapi search engine=google_trends q="Apple stock" geo=US date="today 3-m" & wait ``` ## Product Catalog Intelligence Stack ```bash -# Organic ranking + shopping prices + ad presence -serpapi search engine=google_light q="noise cancelling headphones" gl=us & +serpapi search engine=google_light q="noise cancelling headphones" gl=us & serpapi search engine=google_shopping_light q="noise cancelling headphones" gl=us & wait ``` @@ -113,12 +63,21 @@ wait ```bash # Step 1: find the place and get data_id serpapi search engine=google_maps q="Acme Auto Repair Austin TX" -# → grab local_results[0].data_id from the response # Step 2: pull reviews using that data_id serpapi search engine=google_maps_reviews data_id="" ``` ---- +## Budget-Gated Fan-Out -See [ENGINES.md](ENGINES.md) for the full engine list · [parameters.md](parameters.md) for locale/time filtering · [examples.md](examples.md) for CLI patterns. +```bash +REMAINING=$(serpapi account | grep -o '"total_searches_left":[0-9]*' | grep -o '[0-9]*') +if [ "$REMAINING" -lt 20 ]; then + serpapi search engine=google_light q="$QUERY" num=5 +else + serpapi search engine=google_light q="$QUERY" num=20 & + serpapi search engine=google_news_light q="$QUERY" & + serpapi search engine=google_scholar q="$QUERY" & + wait +fi +```