Run a local API server that gives you free access to 9 AI providers — no API key, no billing, no rate-limit surprises. Build chatbots, CLIs, scripts, and automations on top of it.
AnyAPI is a self-hosted API server built on Playwright browser automation. Start the daemon once; it drives a real browser session and exposes a JSON-RPC socket API. Then call that API from any Python project, shell script, chatbot framework, or tool you're building. A full interactive REPL (anyapi) ships as a built-in client — but it's just one of many things you can wire to the API.
Legal notice: This tool automates web interfaces without using official APIs. Use may violate the Terms of Service of individual providers. It is intended for personal experimentation and research only. You are responsible for reviewing each provider's ToS before use. The authors accept no liability.
- Chatbots — a Telegram bot, a Discord bot, a Slack slash-command handler
- CLI tools — a domain-specific shell assistant, a code-review tool, a doc generator
- Automation scripts — summarise PRs, triage issues, generate commit messages
- Agent pipelines — chain calls across providers, route by task type, compare outputs
- REPL / playground — AnyAPI's built-in interactive shell (ships with the package)
All powered by the same free web UIs you already use — no billing setup required.
| Provider | Free to use | Setup |
|---|---|---|
duckduckgo |
✅ | Nothing — start immediately |
copilot |
✅ | Nothing — start immediately |
huggingchat |
✅ | Nothing — start immediately |
perplexity |
✅ | Nothing — start immediately |
you |
✅ | Nothing — start immediately |
deepseek |
✅ | Export cookies + localStorage (~5 min) |
chatgpt |
✅ | Export cookies (~5 min) |
gemini |
✅ | Export cookies (~5 min) |
qwen |
✅ | Export cookies + localStorage (~5 min) |
pip install anyapi
playwright install chromium # one-time browser download (~150 MB)Requires Python 3.10+ on Linux or macOS. WSL2 works great on Windows.
Install from source:
git clone https://github.com/dheeraj7000/anyapi
cd anyapi
pip install -e .
playwright install chromiumanyapi-daemon --provider duckduckgoThe daemon launches Chromium, navigates to the provider, and listens on a Unix socket at ~/.local/share/anyapi/duckduckgo_daemon.sock. Leave it running.
import asyncio
from anyapi.cli.client import DaemonClient
from pathlib import Path
async def ask(prompt: str, provider: str = "duckduckgo") -> str:
sock = Path.home() / f".local/share/anyapi/{provider}_daemon.sock"
client = DaemonClient(sock)
reply = []
async for event in client.send_request("ask", {"prompt": prompt}):
if event.event == "token":
reply.append(event.data)
elif event.event in ("done", "error"):
break
return "".join(reply)
# Summarise a file
answer = asyncio.run(ask("Summarise this: " + open("README.md").read()))
print(answer)anyapi --provider duckduckgoAnyAPI — Duckduckgo · /help for commands
› What's the difference between async and parallel?
Async is about waiting efficiently — one thread handles many tasks by
yielding control while waiting for I/O. Parallel is about doing multiple
things at the exact same time across multiple CPU cores...
› /save first-session
Saved: first-session (id=a3f8c1d2e4b5)
anyapi --provider duckduckgo "Summarise: $(cat main.py)"
git diff HEAD~1 | anyapi --provider deepseek "Review this diff for bugs"
echo "What is 17 * 89?" | anyapi --provider duckduckgoThe daemon accepts JSON-RPC requests over a Unix domain socket. Each request is a newline-delimited JSON object; the daemon streams back events until a done or error event closes the exchange.
{"id": "uuid", "method": "ask", "params": {"prompt": "Hello!"}}{"id": "uuid", "event": "status", "data": {"stage": "typing"}}
{"id": "uuid", "event": "token", "data": "Hello"}
{"id": "uuid", "event": "token", "data": " there"}
{"id": "uuid", "event": "done", "data": {"text": "Hello there", "chars": 11}}| Method | Params | Description |
|---|---|---|
ask |
prompt: str |
Send a prompt; streams token events then done |
new_chat |
— | Start a fresh browser conversation |
save_conversation |
name?: str |
Save current conversation |
load_conversation |
id: str |
Load a saved conversation (prefix match) |
list_conversations |
— | List all saved conversations |
current_conversation |
— | Get full turn history for active conversation |
stats |
— | Rate-limit status and session info |
health |
— | Browser and provider health check |
reload_auth |
— | Re-inject cookies without restarting the daemon |
shutdown |
— | Gracefully shut down the daemon |
import asyncio
from anyapi.cli.client import DaemonClient
from pathlib import Path
async def stream(prompt: str, provider: str = "duckduckgo"):
sock = Path.home() / f".local/share/anyapi/{provider}_daemon.sock"
client = DaemonClient(sock)
async for event in client.send_request("ask", {"prompt": prompt}):
if event.event == "token":
print(event.data, end="", flush=True)
elif event.event == "done":
print() # newline after response
break
elif event.event == "error":
print(f"\nError: {event.data}")
break
asyncio.run(stream("Write a haiku about Unix pipes"))from anyapi.cli.client import DaemonClient
from pathlib import Path
class AIBot:
def __init__(self, provider="deepseek"):
sock = Path.home() / f".local/share/anyapi/{provider}_daemon.sock"
self.client = DaemonClient(sock)
async def reply(self, user_message: str) -> str:
parts = []
async for event in self.client.send_request("ask", {"prompt": user_message}):
if event.event == "token":
parts.append(event.data)
elif event.event in ("done", "error"):
break
return "".join(parts)
bot = AIBot(provider="deepseek")
# In your Telegram/Discord/Slack handler:
# response = await bot.reply(message.text)
# await message.reply(response)AnyAPI reads credentials from:
~/.local/share/anyapi/providers/<provider>/cookies.json
~/.local/share/anyapi/providers/<provider>/localstorage.json # deepseek, qwen only
- Log in to the provider's website in your normal browser.
- Install the Cookie-Editor browser extension.
- Open the extension → Export → JSON format. Copy the output.
- Create the credentials directory and save the file:
mkdir -p ~/.local/share/anyapi/providers/deepseek
chmod 700 ~/.local/share/anyapi/providers/deepseek
nano ~/.local/share/anyapi/providers/deepseek/cookies.json # paste here- For DeepSeek and Qwen only — also export localStorage:
- Open DevTools → Console
- Run:
copy(JSON.stringify(localStorage)) - Save:
nano ~/.local/share/anyapi/providers/deepseek/localstorage.json- Lock down permissions:
chmod 600 ~/.local/share/anyapi/providers/deepseek/*.json- Start and verify:
anyapi-daemon --provider deepseek
anyapi --provider deepseek
› /health# Start (foreground — use tmux/screen or systemd to background it)
anyapi-daemon --provider duckduckgo
# Watch the browser (useful for first-run auth)
anyapi-daemon --provider deepseek --headed
# Verbose logging
anyapi-daemon --provider duckduckgo --verbose
# Custom state directory
anyapi-daemon --provider chatgpt --base-dir /path/to/dir# ~/.config/systemd/user/anyapi-duckduckgo.service
[Unit]
Description=AnyAPI daemon — duckduckgo
[Service]
ExecStart=%h/.local/bin/anyapi-daemon --provider duckduckgo
Restart=on-failure
[Install]
WantedBy=default.targetsystemctl --user enable --now anyapi-duckduckgo| Command | Description |
|---|---|
/help |
Show all commands |
/new |
Start a new conversation |
/list |
List saved conversations |
/load <id> |
Load by 12-char prefix from /list |
/save [title] |
Save current conversation |
/file <path> |
Stage a file to inline into your next prompt |
/auth |
Show credential setup instructions |
/stats |
Rate-limit and session info |
/health |
Daemon and browser health check |
/clear |
Clear screen |
/exit or /quit |
Exit |
› Explain this: @src/main.py
› What's wrong with @tests/test_auth.py?
› Review all: @src/**/*.py
Chromium requires --no-sandbox without user namespaces:
export ANYCLI_NO_SANDBOX=1
anyapi-daemon --provider duckduckgo Your code / chatbot / script
│
│ import DaemonClient
│ send_request("ask", {"prompt": "..."})
▼
Unix socket (~/.local/share/anyapi/<provider>_daemon.sock)
│
▼
anyapi-daemon (API server)
┌─────────────────────────────────────────┐
│ Playwright · Chromium browser │
│ Rate limiter (bucket + burst cap) │
│ Conversation manager (save/load) │
│ Provider plugins: deepseek, chatgpt … │
└─────────────────────────────────────────┘
anyapi (built-in reference client)
┌─────────────────────────────────────────┐
│ Interactive REPL (prompt_toolkit) │
│ One-shot mode (stdin pipe) │
│ Rich streaming renderer │
└─────────────────────────────────────────┘
The daemon and CLI are decoupled. Multiple clients can share one daemon. You can replace anyapi entirely with your own client — the socket protocol is stable.
- Selector fragility — Providers use DOM selectors that break when sites update. Open an issue if one stops working.
- No true streaming — Responses arrive after the AI finishes generating (stability polling), not token-by-token in real time.
- Rate limits — AnyAPI applies local caps (60 req/hour, 300/day) to avoid triggering provider-side blocks.
- Browser context — Conversation history lives in the browser tab;
/loadrestores turn history and navigates to the saved URL, but the exact browser context may differ after a daemon restart. - Terms of Service — Browser automation likely violates each provider's ToS. Use responsibly.
See CONTRIBUTING.md for dev setup, adding providers, and PR guidelines.
pip install -e ".[dev]"
playwright install chromium
python -m pytest tests/MIT — see LICENSE.