Metadata-Version: 2.5
Name: anysearch-sdk
Version: 0.2.0
Summary: A unified adapter layer to call any web search API provider (Exa, Parallel, Brave, Tavily, Keenable, OpenAI, and more) with one interface — plus a stdio MCP server.
Project-URL: Homepage, https://github.com/dhruv-anand-aintech/anysearch
Project-URL: Repository, https://github.com/dhruv-anand-aintech/anysearch
Author: anysearch contributors
License: MIT
Keywords: anthropic,brave,brightdata,dataforseo,deepseek,exa,keenable,keiro,llm,mcp,openai,parallel,rag,search,search-api,serpapi,tavily,tinyfish,xai
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Provides-Extra: all
Requires-Dist: ddgs>=6.0; extra == 'all'
Requires-Dist: exa-py>=1.0; extra == 'all'
Requires-Dist: firecrawl-py>=1.0; extra == 'all'
Requires-Dist: google-genai>=1.0; extra == 'all'
Requires-Dist: google-search-results>=2.4; extra == 'all'
Requires-Dist: keirolabs>=0.1; extra == 'all'
Requires-Dist: linkup-sdk>=0.2; extra == 'all'
Requires-Dist: octen>=0.7.5; extra == 'all'
Requires-Dist: parallel-web>=0.4; extra == 'all'
Requires-Dist: perplexityai>=0.4; extra == 'all'
Requires-Dist: tavily-python>=0.5; extra == 'all'
Provides-Extra: anthropic
Provides-Extra: brave
Provides-Extra: brightdata
Provides-Extra: dataforseo
Provides-Extra: deepseek
Provides-Extra: dev
Requires-Dist: fastapi>=0.100; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: uvicorn[standard]>=0.23; extra == 'dev'
Provides-Extra: duckduckgo
Requires-Dist: ddgs>=6.0; extra == 'duckduckgo'
Provides-Extra: exa
Requires-Dist: exa-py>=1.0; extra == 'exa'
Provides-Extra: firecrawl
Requires-Dist: firecrawl-py>=1.0; extra == 'firecrawl'
Provides-Extra: gemini
Requires-Dist: google-genai>=1.0; extra == 'gemini'
Provides-Extra: google-pse
Provides-Extra: jina
Provides-Extra: kagi
Provides-Extra: keenable
Provides-Extra: keiro
Requires-Dist: keirolabs>=0.1; extra == 'keiro'
Provides-Extra: linkup
Requires-Dist: linkup-sdk>=0.2; extra == 'linkup'
Provides-Extra: octen
Requires-Dist: octen>=0.7.5; extra == 'octen'
Provides-Extra: openai
Provides-Extra: parallel
Requires-Dist: parallel-web>=0.4; extra == 'parallel'
Provides-Extra: perplexity
Requires-Dist: perplexityai>=0.4; extra == 'perplexity'
Provides-Extra: proxy
Requires-Dist: fastapi>=0.100; extra == 'proxy'
Requires-Dist: uvicorn[standard]>=0.23; extra == 'proxy'
Provides-Extra: searchapi
Provides-Extra: searxng
Provides-Extra: serpapi
Requires-Dist: google-search-results>=2.4; extra == 'serpapi'
Provides-Extra: serper
Provides-Extra: tavily
Requires-Dist: tavily-python>=0.5; extra == 'tavily'
Provides-Extra: tinyfish
Provides-Extra: xai
Provides-Extra: you
Description-Content-Type: text/markdown

# anysearch (Python)

**One interface for every web search API.** `anysearch` is an adapter layer — think
LiteLLM, but for search providers — that lets you call Octen, Exa, Parallel, Brave, Tavily,
SerpAPI, Perplexity, Linkup, Firecrawl, Keenable, OpenAI, and more through a single function with a
common set of parameters and a common response shape. It also ships a dependency-free
**stdio MCP server** and optional **FastAPI proxy server** that adapt to whatever API
keys you have configured.

```python
import anysearch

# Auto-selects a provider based on which API keys are set in your environment.
resp = anysearch.search("best vector databases 2026", max_results=5)
for r in resp:
    print(r.title, r.url)

# Pin a provider and ask for richer content — same parameters everywhere.
resp = anysearch.search(
    "how do transformers work",
    provider="exa",
    include_content=True,
    highlights=True,
    include_summary=True,
)
```

## Install

Install the released package from PyPI, or the latest `main` from GitHub:

```bash
pip install anysearch-sdk

# or from main
pip install "git+https://github.com/dhruv-anand-aintech/anysearch.git@main#subdirectory=python"
# or
uv add anysearch-sdk
```

The base install works for **all** providers (every adapter is REST-based on `httpx`).
Use extras to also install a provider's official SDK (for `anysearch.native(...)`):

```bash
pip install "anysearch-sdk[exa,parallel]"
pip install "anysearch-sdk[all]"
uv add "anysearch-sdk[exa,tavily,brave]"
```

REST-only providers (`brave`, `serper`, `you`, `jina`, `kagi`, `searchapi`,
`google-pse`, `searxng`, `keenable`, `tinyfish`, `deepseek`, `brightdata`,
`dataforseo`, `openai`, `anthropic`, `xai`) need no extra dependency — the extra name
still resolves so the selection syntax is uniform.

## Providers

| Provider | Name / aliases | Env var(s) | Extra (native SDK) |
| --- | --- | --- | --- |
| Octen | `octen`, `octen_ai` | `OCTEN_API_KEY` | `octen` |
| Exa | `exa` | `EXA_API_KEY` | `exa-py` |
| Parallel | `parallel` | `PARALLEL_API_KEY` | `parallel-web` |
| Tavily | `tavily` | `TAVILY_API_KEY` | `tavily-python` |
| Brave | `brave` | `BRAVE_API_KEY` | — |
| Keiro | `keiro` | `KEIRO_API_KEY` | — |
| Linkup | `linkup` | `LINKUP_API_KEY` | `linkup-sdk` |
| Perplexity | `perplexity`, `pplx` | `PERPLEXITY_API_KEY` | `perplexityai` |
| Gemini | `gemini`, `google_gemini` | `GEMINI_API_KEY` | `google-genai` |
| OpenAI | `openai` | `OPENAI_API_KEY` | — |
| Anthropic | `anthropic`, `claude` | `ANTHROPIC_API_KEY` | — |
| xAI | `xai`, `grok` | `XAI_API_KEY` | — |
| Keenable | `keenable` | `KEENABLE_API_KEY` | — |
| TinyFish | `tinyfish` | `TINYFISH_API_KEY` | — |
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` | — |
| Bright Data | `brightdata`, `bright_data` | `BRIGHTDATA_API_KEY` + `BRIGHTDATA_ZONE` | — |
| DataForSEO | `dataforseo`, `data_for_seo` | `DATAFORSEO_LOGIN` + `DATAFORSEO_PASSWORD` | — |
| Serper | `serper` | `SERPER_API_KEY` | — |
| SerpApi | `serpapi`, `serp` | `SERPAPI_API_KEY` | `google-search-results` (engine: bing, baidu, yandex, …) |
| SearchApi | `searchapi` | `SEARCHAPI_API_KEY` | — |
| You.com | `you`, `ydc` | `YDC_API_KEY` | — |
| Jina | `jina` | `JINA_API_KEY` | — |
| Kagi | `kagi` | `KAGI_API_KEY` | — |
| Firecrawl | `firecrawl` | `FIRECRAWL_API_KEY` | `firecrawl-py` |
| Google PSE | `google_pse`, `google` | `GOOGLE_PSE_API_KEY` + `GOOGLE_PSE_CX` | — |
| SearXNG | `searxng`, `searx` | `SEARXNG_BASE_URL` (no key) | — |
| DuckDuckGo | `duckduckgo`, `ddg` | none (keyless fallback) | `ddgs` |

## Unified parameters

| Param | Type | Notes |
| --- | --- | --- |
| `query` | `str` | required |
| `max_results` | `int` | honored everywhere (sliced client-side when needed) |
| `search_type` | `"web" \| "news"` | |
| `country` | `str` | ISO 3166-1 alpha-2 |
| `language` | `str` | ISO 639-1 |
| `include_domains` / `exclude_domains` | `list[str]` | |
| `start_published_date` / `end_published_date` | `str` | ISO 8601 / `YYYY-MM-DD` |
| `safe_search` | `"off" \| "moderate" \| "strict"` | |
| `mode` | `"fast" \| "balanced" \| "deep"` | depth/quality knob, mapped per provider |
| `answer` | `bool` | synthesized, cited answer where supported |
| `include_content` | `bool` | full page text where supported |
| `include_summary` | `bool` | per-result AI summary where supported |
| `highlights` | `bool` | query-relevant excerpts where supported |
| `extra` | `dict` | raw provider-specific passthrough |

Unsupported parameters are handled by `on_unsupported` (`"warn"` default, or `"ignore"`
/ `"error"`). Unknown keyword arguments are forwarded to the provider as `extra`.

### Common response shape

`search()` returns a `SearchResponse`:

```python
resp.provider          # "exa"
resp.query             # "..."
resp.answer            # synthesized answer, if any
resp.results           # list[SearchResult]
resp.urls              # [r.url for r in results]
resp.to_dict()         # JSON-serializable

r = resp.results[0]
r.title, r.url, r.snippet, r.text, r.summary, r.highlights, r.score, r.published_date, r.author, r.source
r.raw                  # the provider's raw result
```

## Fallbacks, async, and the native escape hatch

```python
from anysearch import AnySearch

client = AnySearch(provider="exa", fallbacks=["tavily", "brave"])
resp = client.search("...", mode="deep")

# Async
resp = await client.asearch("...")

# Drop down to a provider's official SDK (requires the extra installed)
exa = anysearch.native("exa")        # -> exa_py.Exa instance
```

## MCP server

```bash
anysearch-mcp            # stdio MCP server
anysearch-mcp --probe    # also verify each provider with a tiny live query at startup
```

Tools exposed: `search`, `list_providers`, `check_providers`, and `migrate_codebase`
(scans a repo for direct search-API usage and suggests unified replacements). See the
repo root README for client configuration examples.

## FastAPI proxy

The proxy is a small search gateway inspired by LiteLLM Proxy: bearer-key auth,
provider routing/fallbacks, OpenAI-style `/v1/models` and `/v1/search`, native
`/search`, health checks, and simple in-process request limits.

```bash
pip install "anysearch-sdk[proxy] @ git+https://github.com/dhruv-anand-aintech/anysearch.git@main#subdirectory=python"

export ANYSEARCH_PROXY_KEYS="dev-key"
export EXA_API_KEY="..."
anysearch-proxy
```

```bash
curl -H "Authorization: Bearer dev-key" http://localhost:4000/v1/models
curl -X POST http://localhost:4000/v1/search \
  -H "Authorization: Bearer dev-key" \
  -H "Content-Type: application/json" \
  -d '{"model":"exa","query":"best vector databases","max_results":3}'
```

Useful env vars: `ANYSEARCH_PROXY_KEYS`, `ANYSEARCH_PROXY_ADMIN_KEYS`,
`ANYSEARCH_PROXY_REQUIRE_AUTH=0`, `ANYSEARCH_PROXY_REQUEST_LIMIT`,
`ANYSEARCH_PROXY_HOST`, and `ANYSEARCH_PROXY_PORT`.
