Your browser is the API. No keys. No bots. No scrapers.
Fork Note (skVPN/bb-browser-api): This fork adds three new HTTP API endpoints to the daemon and changes the default port to 18888. Install with
npm install -g bb-browser-api. See What's Changed in This Fork for details.Original Project: epiral/bb-browser
You're already logged into Twitter, Reddit, YouTube, Zhihu, Bilibili, LinkedIn, GitHub — bb-browser lets AI agents use that directly.
bb-browser-api site twitter/search "AI agent" # search tweets
bb-browser-api site zhihu/hot # trending on Zhihu
bb-browser-api site arxiv/search "transformer" # search papers
bb-browser-api site eastmoney/stock "茅台" # real-time stock quote
bb-browser-api site boss/search "AI engineer" # search jobs
bb-browser site wikipedia/summary "Python" # Wikipedia summary
bb-browser site youtube/transcript VIDEO_ID # full transcript
bb-browser site stackoverflow/search "async" # search SO questions103 commands across 36 platforms. All using your real browser's login state. Full list →
The internet was built for browsers. AI agents have been trying to access it through APIs — but 99% of websites don't offer one.
bb-browser flips this: instead of forcing websites to provide machine interfaces, let machines use the human interface directly. The adapter runs eval inside your browser tab, calls fetch() with your cookies, or invokes the page's own webpack modules. The website thinks it's you. Because it is you.
| Playwright / Selenium | Scraping libs | bb-browser | |
|---|---|---|---|
| Browser | Headless, isolated | No browser | Your real Chrome |
| Login state | None, must re-login | Cookie extraction | Already there |
| Anti-bot | Detected easily | Cat-and-mouse | Invisible — it IS the user |
| Complex auth | Can't replicate | Reverse engineer | Page handles it itself |
npm install -g bb-browser-apibb-browser-api site update # pull community adapters
bb-browser-api site recommend # see which adapters match your browsing habits
bb-browser-api site zhihu/hot # goIf you use OpenClaw, bb-browser-api runs directly through OpenClaw's built-in browser — no Chrome extension or daemon required:
bb-browser-api site reddit/hot --openclaw
bb-browser-api site xueqiu/hot-stock 5 --openclaw --jq '.items[] | {name, changePercent}'Skill on ClawHub: bb-browser-openclaw
{
"mcpServers": {
"bb-browser-api": {
"command": "npx",
"args": ["-y", "bb-browser-api", "--mcp"]
}
}
}Community-driven via bb-sites. One JS file per command.
| Category | Platforms | Commands |
|---|---|---|
| Search | Google, Baidu, Bing, DuckDuckGo, Sogou WeChat | search |
| Social | Twitter/X, Reddit, Weibo, Xiaohongshu, Jike, LinkedIn, Hupu | search, feed, thread, user, notifications, hot |
| News | BBC, Reuters, 36kr, Toutiao, Eastmoney | headlines, search, newsflash, hot |
| Dev | GitHub, StackOverflow, HackerNews, CSDN, cnblogs, V2EX, Dev.to, npm, PyPI, arXiv | search, issues, repo, top, thread, package |
| Video | YouTube, Bilibili | search, video, transcript, popular, comments, feed |
| Entertainment | Douban, IMDb, Genius, Qidian | movie, search, top250 |
| Finance | Xueqiu, Eastmoney, Yahoo Finance | stock, hot stocks, feed, watchlist, search |
| Jobs | BOSS Zhipin, LinkedIn | search, detail, profile |
| Knowledge | Wikipedia, Zhihu, Open Library | search, summary, hot, question |
| Shopping | SMZDM | search deals |
| Tools | Youdao, GSMArena, Product Hunt, Ctrip | translate, phone specs, trending products |
bb-browser guide # full tutorialTell your AI agent: "turn XX website into a CLI". It reads the guide, reverse-engineers the API with network --with-body, writes the adapter, tests it, and submits a PR. All autonomously.
Three tiers of adapter complexity:
| Tier | Auth method | Example | Time |
|---|---|---|---|
| 1 | Cookie (fetch directly) | Reddit, GitHub, V2EX | ~1 min |
| 2 | Bearer + CSRF token | Twitter, Zhihu | ~3 min |
| 3 | Webpack injection / Pinia store | Twitter search, Xiaohongshu | ~10 min |
We tested this: 20 AI agents ran in parallel, each independently reverse-engineered a website and produced a working adapter. The marginal cost of adding a new website to the agent-accessible internet is approaching zero.
Without bb-browser, an AI agent's world is: files + terminal + a few APIs with keys.
With bb-browser: files + terminal + the entire internet.
An agent can now, in under a minute:
# Cross-platform research on any topic
bb-browser site arxiv/search "retrieval augmented generation"
bb-browser site twitter/search "RAG"
bb-browser site github search rag-framework
bb-browser site stackoverflow/search "RAG implementation"
bb-browser site zhihu/search "RAG"
bb-browser site 36kr/newsflashSix platforms, six dimensions, structured JSON. Faster and broader than any human researcher.
bb-browser-api open https://example.com
bb-browser-api snapshot -i # accessibility tree
bb-browser-api click @3 # click element
bb-browser-api fill @5 "hello" # fill input
bb-browser-api eval "document.title" # run JS
bb-browser-api fetch URL --json # authenticated fetch
bb-browser-api network requests --with-body --json # capture traffic
bb-browser-api screenshot # take screenshotAll commands support --json output, --jq <expr> for inline filtering, and --tab <id> for concurrent multi-tab operations.
bb-browser-api site xueqiu/hot-stock 5 --jq '.items[] | {name, changePercent}'
# {"name":"云天化","changePercent":"2.08%"}
# {"name":"东吴股份","changePercent":"-7.60%"}
bb-browser-api site info xueqiu/stock # view adapter args, example, domainThe daemon exposes an HTTP API for direct integration. Default port: 18888 (changed from upstream's 19824).
# Start daemon
bb-browser-api daemon start
# Fetch API — execute a request inside the browser context
curl -X POST http://localhost:18888/api/fetch \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.github.com/users/octocat",
"method": "GET",
"credentials": "include"
}'
# Capture API — visit a URL and capture matching network requests
curl "http://localhost:18888/api/capture?url=https://example.com&pattern=api"
# Storage API — read cookies / localStorage / sessionStorage for a domain
curl "http://localhost:18888/api/storage?domain=example.com"
# Cookies API — set cookies (single / batch / Set-Cookie header string)
curl -X POST http://localhost:18888/api/cookies \
-H "Content-Type: application/json" \
-d '{"domain":"example.com","name":"session","value":"abc","path":"/","secure":true}'Key advantage: Executes in your real browser context with cookies and login state automatically included.
const response = await fetch('http://localhost:18888/api/fetch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://www.reddit.com/api/me.json',
credentials: 'include', // send cookies
}),
});
const result = await response.json();
console.log(result.body); // Your Reddit user dataimport requests
response = requests.post('http://localhost:18888/api/fetch', json={
'url': 'https://api.github.com/users/octocat',
})
result = response.json()
print(result['body'])Documentation: API Fetch Guide · Capture & Storage Guide
The daemon binds to 127.0.0.1:18888 by default. You can customize the host with --host:
bb-browser-api daemon --host 127.0.0.1 # IPv4 only (fix macOS IPv6 issues)
bb-browser-api daemon --host 0.0.0.0 # listen on all interfaces (for Tailscale / ZeroTier remote access)AI Agent (Claude Code, Codex, Cursor, etc.)
┆ CLI or MCP (stdio)
▼
bb-browser CLI ──HTTP──▶ Daemon ──CDP WebSocket──▶ Your Real Browser
┆
┌──────┴──────┐
│ Per-tab │
│ event cache │
│ (network, │
│ console, │
│ errors) │
└─────────────┘
This fork (skVPN/bb-browser-api) adds the following changes on top of the upstream epiral/bb-browser:
File: packages/shared/src/constants.ts
- export const DAEMON_PORT = 19824;
+ export const DAEMON_PORT = 18888;Execute an HTTP request inside the browser context (with cookies, login state, etc.).
File: packages/daemon/src/http-server.ts, packages/daemon/src/command-dispatch.ts
Request:
POST http://localhost:18888/api/fetch
{
"url": "https://api.example.com/data",
"method": "GET",
"headers": { "Accept": "application/json" },
"credentials": "include",
"body": ""
}| Field | Type | Required | Description |
|---|---|---|---|
url |
string | ✅ | Target URL |
method |
string | — | HTTP method, default GET |
headers |
object | — | Custom request headers |
credentials |
omit | same-origin | include |
— | Cookie behavior, default omit |
body |
string | — | Request body (for POST/PUT) |
tabId |
string | number | — | Specific tab to use |
useDomain |
string | — | Only used when inBrowser=true. Run the fetch inside a tab under this hostname, e.g. "3ue.com" |
redirect |
follow | manual | error |
— | Redirect policy, default follow. With manual, the 3xx response is proxied through verbatim (including Location); error fails on any redirect |
inBrowser |
boolean | — | If true, run the fetch inside a browser tab (legacy behavior) and return a {status, contentType, body} JSON envelope. Combine with useDomain / tabId. redirect is ignored in this mode. Default false |
Response (default — transparent proxy):
/api/fetch proxies the upstream response as-is: HTTP status, status text, all response headers (Set-Cookie, Location, Content-Type, ...) and the raw body, with no envelope.
You get exactly what curl-ing the target URL would yield, but with the browser's login state.
# Upstream returns JSON -> body is the raw JSON
curl -X POST http://localhost:18888/api/fetch \
-d '{"url":"https://api.example.com/me","credentials":"include"}' \
-H "Content-Type: application/json"
# -> 200, body is {"id":1,"name":"alice"} (no envelope)
# Upstream 302 -> status + Location header proxied through
curl -i -X POST http://localhost:18888/api/fetch \
-d '{"url":"https://example.com/login","redirect":"manual","credentials":"include"}' \
-H "Content-Type: application/json"
# -> HTTP/1.1 302 Found
# -> location: https://account.example.com/sso?...
# -> set-cookie: ...Response (inBrowser: true — legacy JSON envelope):
{
"status": 200,
"contentType": "application/json",
"body": { ... }
}Note on
credentials:Sec-Fetch-*headers are set by the browser automatically and cannot be overridden via JavaScript — this is a browser security requirement. Thecredentialsfield controls whether cookies are sent.
When to use useDomain?
⚠️ useDomainonly applies wheninBrowser: true(the default transparent-proxy mode does not need a tab).
When you opt into in-tab fetch (inBrowser: true) and the target subdomain redirects to about:blank / is unreachable / rejects OPTIONS, the in-page fetch fails with Failed to fetch (TypeError) from page: about:blank.
Workaround: run the fetch inside an already-open tab on a sibling domain.
# https://3ue.com is already open in some tab
# Use inBrowser=true + useDomain to run the fetch in that tab's context
curl -X POST http://localhost:18888/api/fetch \
-H "Content-Type: application/json" \
-d '{
"url": "https://ggg-zh-g--ggg---scriptsem.3ue.com/__static__/webpack/9752.5376bb6d1330b4eb.js",
"inBrowser": true,
"useDomain": "3ue.com",
"credentials": "include"
}'CLI equivalent (the CLI command always runs in-tab):
bb-browser-api fetch https://ggg-zh-g--ggg---scriptsem.3ue.com/__static__/webpack/9752.js \
--use-domain 3ue.comCapturing a 302 redirect without following
When you need the raw 3xx response (e.g. to read Location, trace SSO chains), pass "redirect": "manual":
curl -i -X POST http://localhost:18888/api/fetch \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/login-redirect",
"redirect": "manual",
"credentials": "include"
}'Response (proxied through at the HTTP layer — no JSON envelope):
HTTP/1.1 302 Found
Location: https://account.example.com/sso?next=...
Set-Cookie: session=abc; Path=/; HttpOnly
Content-Length: 0
redirect: "error" works the same way — when the upstream returns 3xx, the daemon responds with HTTP 502 + an error message (also without a JSON envelope).
Visit a URL and capture network requests matching a pattern.
File: packages/daemon/src/http-server.ts
GET http://localhost:18888/api/capture?url=https://example.com&pattern=api\.&timeout=5000
| Parameter | Required | Description |
|---|---|---|
url |
✅ | Page URL to visit |
pattern |
— | Regex to filter captured requests |
timeout |
— | Wait time in ms, default 5000 |
Response:
{
"requests": [
{
"url": "https://example.com/api/data",
"method": "GET",
"status": 200,
"responseBody": "..."
}
]
}Read cookies, localStorage, and sessionStorage for a domain.
File: packages/daemon/src/http-server.ts
GET http://localhost:18888/api/storage?domain=example.com
| Parameter | Required | Description |
|---|---|---|
domain |
✅ | Domain to read storage from |
Response:
{
"cookies": [ { "name": "session", "value": "...", "domain": "example.com" } ],
"localStorage": { "key": "value" },
"sessionStorage": { "key": "value" }
}Set one or more cookies in the browser. Uses CDP Storage.setCookies directly,
so no tab needs to be open at that domain.
File: packages/daemon/src/http-server.ts
Three input styles are accepted (use whichever is most convenient):
# 1) Single cookie
curl -X POST http://localhost:18888/api/cookies \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"name": "session",
"value": "abc123",
"path": "/",
"secure": true,
"httpOnly": true,
"sameSite": "Lax",
"maxAge": 3600
}'
# 2) Multiple cookies in one call
curl -X POST http://localhost:18888/api/cookies \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"cookies": [
{ "name": "a", "value": "1" },
{ "name": "b", "value": "2", "secure": true }
]
}'
# 3) Raw Set-Cookie header strings (one or many)
curl -X POST http://localhost:18888/api/cookies \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"setCookie": "session=abc123; Path=/; Secure; HttpOnly; Max-Age=3600"
}'| Field | Type | Required | Description |
|---|---|---|---|
name |
string | ✅* | Cookie name (required when setting a single cookie) |
value |
string | ✅* | Cookie value |
url |
string | — | Used to derive domain if domain is omitted |
domain |
string | — | Explicit cookie domain |
useDomain |
string | — | Force-override the cookie domain, e.g. "example.com" |
path |
string | — | Cookie path, default / |
expires |
number | — | Unix seconds; omit for session cookie |
maxAge |
number | — | Lifetime in seconds (alternative to expires) |
secure |
boolean | — | Secure flag |
httpOnly |
boolean | — | HttpOnly flag |
sameSite |
Strict | Lax | None |
— | SameSite attribute |
cookies |
array | — | Batch form: array of cookie objects |
setCookie |
string | string[] | — | Raw Set-Cookie header string(s) |
Either
urlordomainmust be provided (top-level or per-cookie).
Response:
{
"set": 1,
"cookies": [
{ "name": "session", "domain": "example.com", "path": "/", "secure": true, "httpOnly": true, "sameSite": "Lax", "expires": 1893456000 }
]
}File: packages/shared/src/protocol.ts
export interface Request {
// ...existing fields...
/** fetch credentials option: omit | same-origin | include (default: omit) */
credentials?: "omit" | "same-origin" | "include";
}Previously the fetch implementation hardcoded credentials: 'include'. It now defaults to 'omit' and respects the caller's choice.
MIT