Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

paperfoot/search-cli

Open more actions menu

Repository files navigation

Search CLI — Web Search for AI Agents

One binary, 13 providers, 13 modes, rank-fused results. The web search tool your AI agent is missing.


Star this repo    Follow @longevityboris


Crates.io Downloads License: MIT Build


A single Rust binary that aggregates Brave, Serper, Exa, Linkup, Jina, Firecrawl, Tavily, SerpApi, Perplexity, Parallel, xAI, and more into one search interface. Built for AI agents from day one: structured JSON, semantic exit codes, self-describing agent-info, reciprocal rank fusion across providers, and a usage command that reports remaining API credits.

Install | How It Works | Features | Providers | Contributing

Why This Exists

Every search API is good at something different. Brave has its own 35-billion page index. Serper gives you raw Google results plus Scholar, Patents, and Places. Exa does neural/semantic search. Perplexity gives AI-synthesized answers with citations. Jina reads any URL into clean markdown. Firecrawl renders JavaScript-heavy pages. xAI searches X/Twitter.

You shouldn't have to wire up each one separately, handle their different response formats, or manage rate limits. search does the plumbing: you pick the mode (the CLI never guesses intent), it fans out to that mode's providers in parallel and fuses the results with reciprocal rank fusion. A URL that three engines independently return outranks any single engine's top hit — the fastest provider no longer wins by default, the most-agreed-on result does.

search "CRISPR gene therapy breakthroughs"

That's it — a plain search "query" runs a general multi-provider web search and merges the results in under 2 seconds. You stay in control of routing: pick a mode with -m or specific providers with -p (run search agent-info for the full map). The CLI never guesses intent from your query.

Install

Cargo (recommended):

cargo install agent-search

Homebrew:

brew tap paperfoot/tap
brew install search-cli

One-liner (macOS / Linux):

curl -fsSL https://raw.githubusercontent.com/paperfoot/search-cli/master/install.sh | sh

From source:

cargo install --git https://github.com/paperfoot/search-cli

Binary size is ~6 MB. Startup is ~2 ms. Memory is ~5 MB. No Python, no Node, no Docker.

Quick Start

# Set your API keys (any combination works -- even just one)
search config set keys.brave YOUR_BRAVE_KEY
search config set keys.serper YOUR_SERPER_KEY
search config set keys.exa YOUR_EXA_KEY

# Or use environment variables
export SEARCH_KEYS_BRAVE=YOUR_KEY
export SEARCH_KEYS_EXA=YOUR_KEY

# Search
search "your query here"

How It Works

                          ┌─────────────┐
                          │ Query + -m  │  you pick the mode —
                          └──────┬──────┘  no intent guessing
                                 │
                    ┌────────────┼────────────┐
                    ▼            ▼            ▼
              ┌──────────┐ ┌──────────┐ ┌──────────┐
              │  Brave   │ │  Serper  │ │   Exa    │  parallel fan-out
              └────┬─────┘ └────┬─────┘ └────┬─────┘  via tokio::JoinSet
                   │            │            │
                   └────────────┼────────────┘
                                │
                         ┌──────▼──────┐
                         │ Rank fusion │  dedup + reciprocal rank
                         │  (RRF k=60) │  fusion across providers
                         └──────┬──────┘
                                │
                    ┌───────────┴───────────┐
                    ▼                       ▼
             ┌────────────┐         ┌────────────┐
             │    JSON    │         │   Table    │
             │  (piped)   │         │ (terminal) │
             └────────────┘         └────────────┘
  1. Parse -- Clap parses your query, mode, provider filter, and output preferences
  2. Route -- Your mode determines which providers to query (or you override with -p)
  3. Fan out -- tokio::JoinSet fires all providers in parallel with per-provider timeouts
  4. Collect -- Once enough unique results arrive, stragglers get a 1.5s grace window, then are cancelled (reported in metadata.providers_cancelled; deep mode always waits for everyone)
  5. Fuse -- Reciprocal rank fusion: URLs returned by multiple providers rank first (extra.also_found_by records consensus); ordering is deterministic, not fastest-provider-first
  6. Separate answers -- AI-synthesized answers (Perplexity/Tavily) land in answers[], so every results[].url is a fetchable web URL
  7. Render -- JSON envelope when piped, colored terminal table when interactive

Features

13 Search Modes

Pick a mode explicitly with -m (default is general). The -q column matters: extract/scrape/similar take a URL and reject text queries (exit 3). This table mirrors search agent-info, which is generated from the same routing registry the engine uses.

Mode Use when -q is Providers used
general Any web lookup not covered below (default) query Parallel + Brave + Serper + Exa + Jina + Linkup + Tavily + Perplexity
news Current events; add -f day/-f week query Parallel + Brave + Serper + Linkup + Tavily + Perplexity (news endpoints)
academic Papers/studies by topic (semantic + web) query Exa + Serper + Tavily + Perplexity
scholar Google Scholar records: citations, PDFs query Serper + SerpApi
deep Max coverage; waits for all providers — use -c 30 query Parallel + Brave (web + LLM Context) + Serper + Exa + Linkup + Tavily + Perplexity + xAI
people A person, their role, LinkedIn profile query Exa
social What's being said on X/Twitter query xAI (Grok)
patents Prior art, patent families query Serper
images Image search (check image_url fields) query Serper
places Local businesses, maps query Serper
extract Read one page as markdown, incl. JS/anti-bot URL Stealth -> Jina -> Firecrawl -> Browserless
scrape Alias of extract (identical) URL Stealth -> Jina -> Firecrawl -> Browserless
similar "More like this page" URL Exa

Rank Fusion, Not Fastest-Provider-First

Most meta-search tools return results in whatever order providers answer, so result #1 is the fastest API's opinion. search scores every URL across all providers (RRF, k=60): consensus ranks first, ordering is deterministic, and each result carries extra.also_found_by so you can see which engines agreed. The envelope tells you exactly what happened — who contributed (provider_results), who was cancelled (providers_cancelled), which filters a provider ignored (warnings), and whether you got a cached replay (cached).

Agent-First Design

Built for Claude Code, Codex CLI, Gemini CLI, OpenClaw, and any AI agent that can shell out to a command.

# Discover capabilities programmatically
search agent-info

# Structured JSON with metadata
search "query" --json
# {
#   "status": "success",
#   "query": "...",
#   "mode": "general",
#   "results": [...],                              // rank-fused, deduped
#   "answers": [{"provider": "perplexity_sonar", "text": "..."}],
#   "metadata": {
#     "elapsed_ms": 1542,
#     "result_count": 10,
#     "providers_queried": ["brave", "serper", "exa", "jina"],
#     "provider_results": {"brave": 10, "serper": 10},  // who contributed
#     "providers_cancelled": ["jina"],             // cut off after enough results
#     "warnings": []                               // e.g. filters a provider ignored
#   }
# }

# Check remaining credits (where the provider API exposes them)
search usage --json

Auto-JSON: Output is automatically JSON when piped to another program. Human-readable tables when you're in a terminal.

Semantic exit codes:

Code Meaning Agent action
0 Success Process results
1 Transient error (API, network) Retry might help
2 Config or auth error Fix setup / set API key
3 Bad input Fix arguments
4 Rate limited Back off and retry

Usage Examples

# Default general web search (no mode = general; no intent guessing)
search "quantum computing advances"
search search -q "who is the CEO of Anthropic" -m people
search search -q "CRISPR research papers" -m academic

# Force a specific mode
search search -q "transformer architectures" -m academic
search search -q "Sam Altman" -m people
search search -q "AI startups 2026" -m news
search search -q "BRCA1 gene patent" -m patents

# Search X (Twitter) only
search --x "AI agents"

# URL modes take a URL, not a query
search search -q https://example.com/article -m extract
search search -q https://stripe.com -m similar

# Pick specific providers
search search -q "machine learning" -p exa
search search -q "rust programming" -p brave,serper

# Control output
search "query" --json | jq '.results[].url'
search "query" -c 20                   # 20 results
search "query" 2>/dev/null             # suppress diagnostics

Providers

Provider What it does Best for
Parallel Agent-native search: objective in, LLM-ready ranked excerpts out Grounding content for agents, deterministic per-query cost
Brave Independent index (not resold Google/Bing) + LLM Context API Independent ranking signal, news, RAG grounding
Serper Cheapest raw Google SERP + specialist endpoints Actual Google rankings; scholar, patents, images, places
Exa Neural/semantic search, category filters Research papers, people search, similar sites
Jina Fast URL-to-markdown, 500 RPM free tier Reading article content, quick extraction
Linkup High-accuracy agent search (leads the SimpleQA benchmark) Factual lookups where accuracy matters most
Firecrawl JavaScript rendering, structured extraction Dynamic pages, SPAs, data extraction
Tavily General + deep search, research-focused Broad coverage, research queries
SerpApi Many engines: Google, Bing, YouTube, Baidu Multi-engine coverage; only provider with a real balance API
Perplexity LLM-synthesized answer with citations (Sonar) When you want an answer with sources, not raw pages
Browserless Cloud browser for Cloudflare/JS-heavy pages Anti-bot bypass, pages that need a real browser
Stealth Built-in anti-bot scraper Protected pages, no API key needed
xAI Only provider with native real-time X/Twitter search (Grok) Live social signal, trending topics, account activity

Checking Credits

search usage --json

Reports remaining credits/quota for every provider whose API exposes it: SerpApi (account.json), Firecrawl (credit-usage endpoint), Tavily (usage endpoint), Linkup (credits balance endpoint), xAI (management API — set XAI_MANAGEMENT_API_KEY + XAI_TEAM_ID), and Brave (rate-limit headers; the check consumes one metered request). The rest are dashboard-only and reported as supported: false. Purely informational: the CLI never disables or deprioritizes a provider because its balance is low — if a provider is out of credits, the failed call shows up as a billing_quota entry in metadata.provider_failures and you decide what to top up.

Health Check

search doctor --json

Test-fires every configured provider with a minimal query and reports ok/fail, latency, and failure category — the preflight to run before a fleet trusts the tool. config check tells you a key exists; doctor tells you it works. Each check is one billed (minimal) request. Exit 0 if all healthy, 1 otherwise.

Usage Analytics

search stats --json          # last 30 days
search stats --days 7        # custom window
search stats --prune 90      # delete logs older than 90 days

Computed entirely from the local search logs — nothing leaves the machine. Reports search volume by mode, per-provider call counts / failures / cancellations, estimated spend (from a static price table), measured balance burn (deltas between search usage snapshots), cache-hit rate, repeated queries, and a read-through table — which providers' results agents actually extract afterwards, the closest local proxy for result usefulness. Set SEARCH_LOG=off to disable logging entirely.

Configuration

Config file lives at ~/.config/search/config.toml (Linux) or ~/Library/Application Support/search/config.toml (macOS).

search config show          # View current config (keys masked)
search config check         # Which providers have a key set
search config set K V       # Set a value
echo "$KEY" | search config set keys.brave -   # read secret from stdin

The config file holds API keys, so it's created 0600 (owner-only) and any older world-readable file is tightened on load. Set secrets with - as the value to read from stdin and keep keys out of shell history and ps.

Environment variables override the config file, so a freshly-exported or CI-injected key always wins over stale local config. Two accepted forms:

# Standard per-provider variables (recommended):
export BRAVE_API_KEY=your-key
export SERPER_API_KEY=your-key
export EXA_API_KEY=your-key
# ...also PERPLEXITY_API_KEY, JINA_API_KEY, LINKUP_API_KEY, FIRECRAWL_API_KEY,
#        TAVILY_API_KEY, SERPAPI_API_KEY, BROWSERLESS_API_KEY, XAI_API_KEY,
#        PARALLEL_API_KEY

# Or the SEARCH_KEYS_ prefixed form:
export SEARCH_KEYS_BRAVE=your-key

Precedence (highest first): <PROVIDER>_API_KEY env → SEARCH_KEYS_* env → config file.

Updating

search update             # Self-update from GitHub releases
search update --check     # Check without installing

Building from Source

git clone https://github.com/paperfoot/search-cli
cd search-cli
cargo build --release
# Binary at target/release/search

Linux / containers: the stealth scraper depends on BoringSSL, which doesn't link on Linux. Build without it — the extract chain simply starts at Jina instead of the local scraper:

cargo install agent-search --no-default-features

Prebuilt binaries ship for macOS (both architectures) and Linux x86_64; every release includes a SHA256SUMS file, and both install.sh and self-update verify the checksum before installing.

Contributing

Contributions are welcome. See CONTRIBUTING.md for guidelines.

License

MIT


Built by Boris Djordjevic at Paperfoot AI


If this is useful to you:

Star this repo    Follow @longevityboris

Releases

Packages

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.