Self-hosted cloud browser API for AI agents. Sessions, scraping, screenshots, PDF, stealth, profile persistence, and human-in-the-loop control behind one REST endpoint you operate.
BrowseFleet runs a fleet of stealthed headless Chrome instances behind a single REST API. Agents and automation code spin up sessions, control them over the Chrome DevTools Protocol, scrape pages, screenshot, generate PDFs, persist profiles, and tear sessions down again, all from one HTTP host you run.
It is open source, MIT licensed, and ships with zero phone-home behavior. You host it.
Working in this repo with an AI agent? Read
skill.mdfirst. It teaches Claude Code, Cursor, Aider, or any coding agent how to set up, run, test, and contribute to this repo with no further instruction.
docker run -p 3000:3000 --shm-size=2g ghcr.io/therjmurray/browsefleet:latest
# in another terminal:
curl -X POST localhost:3000/v1/scrape \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com"}'That is the entire integration. Add API_KEYS=key1,key2 and an x-api-key header once you take this off localhost.
latesttracks the most recent release. In production, pin to a release tag instead (:MAJOR.MINOR.PATCH, e.g. the version on the latest release). Multi-arch images (linux/amd64,linux/arm64) are published to GHCR for every tagged release. To build from source instead:docker build -t browsefleet . && docker run -p 3000:3000 --shm-size=2g browsefleet. Seeskill.mdfor the full local dev path.
- REST + CDP. High-level endpoints for the common case (scrape, screenshot, pdf). Direct CDP WebSocket proxy for everything else.
- Stealth.
puppeteer-extra-plugin-stealthbaked in. Per-session randomized viewport, user agent, and platform. - Persistent profiles. Reuse a Chrome user-data directory across sessions. Useful for any flow that needs to stay logged in.
- Operator mode. Sessions can start in
humancontrol, let a real person log in, then hand off to an agent. State machine:agent/human/paused. - AI agent layer. Built-in vision-based agent (
/v1/agent) that takes a natural-language task and drives the browser using Claude or GPT. - CAPTCHA solving. Plug a 2captcha key into
.envand call/v1/sessions/:id/captcha/solve. - Self-hosting friendly. One Node process, one SQLite file, one Docker container. No Redis, no Postgres, no external queue.
- Honest authentication. Timing-safe API key check, per-key and per-IP rate limiting, security headers default-on.
A single Node process manages a pool of Chrome child processes via puppeteer-extra. HTTP requests on port 3000 route to handlers that lease a browser context from the pool, do the work, and return. The CDP WebSocket proxy on the same port exposes raw DevTools Protocol when callers need it.
sequenceDiagram
participant Client as Your agent / SDK
participant API as BrowseFleet API (Hono)
participant Pool as BrowserPool
participant Chrome as Chrome (puppeteer-extra + stealth)
Client->>API: POST /v1/sessions
API->>Pool: createSession()
Pool->>Chrome: spawn child process
Chrome-->>Pool: page handle
Pool-->>API: session id + CDP URL
API-->>Client: 201 { id, cdpUrl }
Client->>API: POST /v1/scrape { url }
API->>Pool: withEphemeralContext()
Pool->>Chrome: page.goto()
Chrome-->>Pool: HTML
Pool-->>API: extracted markdown
API-->>Client: 200 { url, markdown, readability }
State lives in SQLite (./data/browsefleet.db, WAL mode) for API keys, usage metrics, and profile metadata. Chrome user-data directories live under ./data/profiles/.
Deeper docs live under docs/:
- Architecture, process model, request lifecycle, where state lives.
- API reference, every endpoint, request shape, response shape, error codes.
- Configuration, every environment variable.
- Deployment, Docker Compose on a $4/mo VPS, Fly.io, AWS ECS Fargate.
- Stealth, what stealth does, when to turn it down, ethics.
- Operator mode, human-in-the-loop sessions, the control state machine.
- Profiles, persistent Chrome user-data directories.
- Agent, the vision-based AI agent layer.
- Comparison, honest comparison vs Steel.dev, Browserbase, raw Playwright.
Runnable examples for the common flows live under examples/. Each has its own README.
# Curl
curl -X POST localhost:3000/v1/screenshot \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com"}' --output example.png// Node. No SDK and no dependencies, just the REST API.
// Full version: examples/node-quickstart/index.ts
const res = await fetch('http://localhost:3000/v1/scrape', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://example.com' }),
});
const { markdown } = await res.json();
console.log(markdown);# Python. httpx against the same endpoint.
# Full version: examples/python-quickstart/main.py
import httpx
res = httpx.post('http://localhost:3000/v1/scrape',
json={'url': 'https://example.com'}, timeout=60)
print(res.json()['markdown'])Full examples in examples/: curl/, node-quickstart/, python-quickstart/, operator-mode/, cdp-direct/.
Three recipes in docs/deployment.md:
| Host | Cost | Concurrent sessions |
|---|---|---|
| Hetzner CX22 + docker-compose | ~$4/mo | ~10 |
| Fly.io single machine | ~$15/mo | ~20 |
| AWS ECS Fargate (1 task) | ~$30/mo | ~25 |
All three are copy-paste deployable. Chrome wants roughly 200 to 500 MB of RAM per active stealth session.
PRs welcome. Read CONTRIBUTING.md for the workflow, and skill.md for the exact setup commands. Conventional Commits, squash-merge, base branch is master.
Good first issues are tagged good first issue on the tracker.
- Questions and design discussion: GitHub Discussions.
- Bug reports and feature requests: GitHub Issues.
Do not file security issues publicly. See SECURITY.md for the private disclosure process.
MIT. See LICENSE.
Built on Hono, puppeteer-core, puppeteer-extra, puppeteer-extra-plugin-stealth, better-sqlite3, and Mozilla Readability. Standing on a lot of shoulders.