Skip to main content

search-cli (Python)

Python port of search-cli. Async library for querying multiple search providers in parallel with result deduplication.

Requirements

  • Python 3.11+

Installation

pip install -e .

# With dev dependencies (pytest, respx)
pip install -e ".[dev]"

Configuration

Set API keys as environment variables. Only providers with configured keys are used.

Variable Provider
BRAVE_API_KEY Brave Search
SERPER_API_KEY Serper (Google)
EXA_API_KEY Exa (neural search)
JINA_API_KEY Jina
TAVILY_API_KEY Tavily
PERPLEXITY_API_KEY Perplexity

Usage

import asyncio
from search_cli import search, Mode

async def main():
    # General web search
    response = await search("python asyncio tutorial")
    for r in response.results:
        print(r.title, r.url)

    # News search
    response = await search("AI news", mode=Mode.NEWS, count=10)

    # Deep search (maximum coverage)
    response = await search("quantum computing", mode=Mode.DEEP)

    # JSON output
    print(response.model_dump_json(indent=2))

asyncio.run(main())

Modes

Mode Providers
Mode.GENERAL brave, serper, exa, jina, tavily, perplexity
Mode.NEWS brave, serper, tavily, perplexity
Mode.DEEP brave, exa, serper, tavily, perplexity

Advanced: Manage context lifecycle

from search_cli import search, Mode, SearchContext, AppConfig

async def main():
    # Reuse a single httpx client across multiple searches
    async with SearchContext(config=AppConfig()) as ctx:
        r1 = await search("query one", ctx=ctx)
        r2 = await search("query two", mode=Mode.NEWS, ctx=ctx)

Public API

Symbol Type Description
search() async function Main entry point — query, mode, count, opts, ctx
Mode Enum GENERAL, NEWS, DEEP
SearchResult BaseModel title, url, snippet, source, published?, image_url?, extra?
SearchResponse BaseModel version, status, query, mode, results, metadata
SearchOpts BaseModel include_domains, exclude_domains, freshness
AppConfig BaseSettings keys (ApiKeys), timeout (10s), count (5)
ApiKeys BaseSettings 6 provider API keys, loaded from env vars
SearchContext class Async context manager holding httpx client + config
SearchError Exception Base exception with code, message, suggestion

Architecture

src/search_cli/
├── __init__.py          # Public exports
├── enums.py             # Mode enum
├── models.py            # Pydantic models (SearchResult, SearchResponse, etc.)
├── config.py            # ApiKeys + AppConfig (pydantic-settings, env vars)
├── context.py           # SearchContext (httpx.AsyncClient lifecycle)
├── engine.py            # Parallel search orchestration, URL dedup
├── errors.py            # SearchError hierarchy
└── providers/
    ├── __init__.py      # Registry: PROVIDERS_FOR_MODE, build_providers()
    ├── base.py          # BaseProvider ABC (resolve_key, retry_request)
    ├── brave.py         # Brave Search API
    ├── serper.py        # Serper (Google Search) API
    ├── exa.py           # Exa neural search API
    ├── jina.py          # Jina search API
    ├── tavily.py        # Tavily search API
    └── perplexity.py    # Perplexity chat completions API

How it works

  1. search() receives a query + mode
  2. providers_for_mode() selects providers mapped to that mode, filtered to those with configured API keys
  3. asyncio.TaskGroup launches all provider searches in parallel, each with per-provider timeout
  4. Individual provider failures are caught and recorded (other providers continue)
  5. Results are deduplicated by normalized URL (first occurrence wins)
  6. SearchResponse is returned with results + metadata (timing, provider stats)

Testing

pytest -v

23 tests covering models, engine, and all 6 providers. All tests use respx for HTTP mocking — no real API keys required.

Dependencies

Package Purpose
httpx Async HTTP client (maps to Rust reqwest)
pydantic Data models + JSON serialization (maps to Rust serde)
pydantic-settings Config from env vars (maps to Rust figment)

Dev only

Package Purpose
pytest Test runner
pytest-asyncio Async test support
respx httpx request mocking

Metadata

Release files for py-multi-agent-search 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for py-multi-agent-search 0.1.0
File Size Uploaded
py_multi_agent_search-0.1.0.tar.gz 10.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for py-multi-agent-search 0.1.0
File Interpreter ABI Platform
py_multi_agent_search-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 24.5 kB

Release files / py_multi_agent_search-0.1.0.tar.gz

Download URL py_multi_agent_search-0.1.0.tar.gz
Size 10.4 kB
Tags Source
SHA-256 checksum
How to use checksums
06cc2c4c06c8e335453c4720e421280f77199e138992fa710099c4ead45d6116
BLAKE2b-256 checksum
How to use checksums
ef150ecc004d619f5c6e9436995e258db91873f621ca90e40f986002d6a7db5f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release files / py_multi_agent_search-0.1.0-py3-none-any.whl

Download URL py_multi_agent_search-0.1.0-py3-none-any.whl
Size 14.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f483e4619518682b8f62240a66acff2517c8b74fcba62c0ba6eb1a5dd8763110
BLAKE2b-256 checksum
How to use checksums
7d02350b27f5e743885c875ec2f3e938a4fb21fa45c8c265ab6a153f0f5f27dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page