BrowserBro is a simple HTTP server and a collection of plugins that are useful for web scraping, testing and many other things. With browserBro you can turn any website into an API.
At its core, BrowserBro is using Rod, a high-level Go browser automation library.
First, clone the repository and navigate to the project directory.
To run the project you will need to install Docker.
docker-compose up --build -dThe command above will start two containers: the API server and the browser server. The API server is this project, and the browser server is Rod and is pre-built.
Alternatively, you can build the project manually and run the browser server separately in any way you want.
go build main.goYou can configure the server by setting the following environment variables:
BROWSERBRO_SERVER_ADDRESS - the address the API server will listen on (default: :10001)
BROWSERBRO_FILE_STORE_BASE_PATH - the directory where the files will be stored on the API server (default: /tmp/browserBro_files)
BROWSERBRO_BROWSER_SERVICE_URL - the address of the browser server (default: ws://localhost:7317)
BROWSERBRO_BROWSER_SERVER_ID - the ID of the browser server. Only necessary if you are running multiple browser instances (default: 1)
BROWSERBRO_BROWSER_BIN - the path of the browser binary to launch, inside the browser server (default: /usr/bin/chromium). Leave it empty to let the browser server download and use the build that ships with rod, which is several versions behind and is refused by sites that check for automation.
BROWSERBRO_BROWSER_MONITOR_ENABLED - enable/disable the browser monitor. Useful for debugging (default: true)
BROWSERBRO_BROWSER_USER_DATA_DIR - the directory that holds the browser profiles on the browser server (default: /tmp/rod/user-data/browserBro_userData). Each connection gets its own profile underneath it.
BROWSERBRO_MCP_ENABLED - enable/disable the MCP server at /mcp (default: true)
The browser server is built from Dockerfile.browser rather than pulled from
ghcr.io/go-rod/rod, because that image ships an old browser build without working WebGL. Sites
that check for automation - Google among them - refuse a browser that is years out of date or
cannot produce a WebGL context, so googlesearch returns a bot check instead of results when it
runs against one.
The browser it does use is the Chromium that ships with the image's Debian base - Debian 13
currently packages Chromium 151, which is current - installed as /usr/bin/chromium and pointed
at by BROWSERBRO_BROWSER_BIN. Nothing pins it to a version, so rebuilding the image is what
updates the browser:
docker compose build --pull browserThe API watches the browser it is driving, and if that browser goes away - the browser service
restarts, the container is recycled, someone kills the process - it connects to a new one within
a few seconds. Requests that arrive while there is no browser are answered with
503 the browser is not connected yet, try again in a moment, so a retry is all a caller needs.
Set BROWSERBRO_TIMEZONE to the timezone your containers appear to browse from, so that the
browser's clock agrees with the location of its IP address:
BROWSERBRO_TIMEZONE=America/Vancouver docker compose up --buildPlugins in context of the BrowserBro are automation scripts used to control the browser and perform various tasks. BrowserBro comes with a basic collection of plugins that are maintained by the contributors. If you want to create a plugin yourself to do something specific and/or contribute to the plugins collection - read more here
Plugins are available as HTTP endpoints and can be accessed by sending a POST request to the server. You can supply the plugin with the necessary parameters in the request body.
POST /api/v1/plugins/{plugin-name}
Look how simple it is to scrape google search results with BrowserBro 🔍
curl -X POST -d '{"query":"latest Golang news"}' http://localhost:10001/api/v1/plugins/googlesearchResponse:
{
"googlesearch": {
"web": [
{
"description": "A weekly newsletter about the Go programming language ...",
"link": "https://golangweekly.com/",
"title": "Golang Weekly"
},
...
]
}
}Google's other tabs are one parameter away - "type": ["images", "news", "videos"]
runs one search per kind and returns each under its own key, and max_results
pages through them when one page is not enough:
curl -X POST -d '{"query":"latest Golang news","type":["news"],"max_results":25}' \
http://localhost:10001/api/v1/plugins/googlesearchTo get a list of all available plugins, along with what each one does and the JSON Schema of its parameters:
GET /api/v1/plugins
{
"plugins": [
{
"name": "googlesearch",
"description": "Runs a Google search in a real browser and returns the results ...",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "The search query to submit to Google." }
},
"required": ["query"]
}
}
]
}BrowserBro is also a Model Context Protocol server, so AI agents can drive the browser directly. It is served over Streamable HTTP on the same port as the HTTP API:
POST /mcp
Every plugin is exposed as a tool named browserbro_{plugin-name}, described by the plugin's
own description and input schema. Files produced by a plugin come back as part of the tool
result: images are inlined so the agent can actually see them, and everything else is linked
as a browserbro://files/{name} resource that the agent can read on demand.
To register the server with an MCP client, such as Claude Code or Claude Desktop:
{
"mcpServers": {
"browserbro": { "url": "http://localhost:10001/mcp" }
}
}The endpoint is unauthenticated, exactly like the rest of the API. Do not expose it to a network you do not trust.
BrowserBro can also serve static files generated or downloaded by the plugins. The files are available at the following URL:
GET /api/v1/files/{fileID}Files can also be deleted by sending a DELETE request to the same URL.
DELETE /api/v1/files/{fileID}When you use the screenshot plugin, the plugin will save the screenshots as files and return the file IDs in the response.
{
"screenshot": {
"files": [
"Shu2vLZm.screenshot.png"
]
}
}You can then access the files by sending a GET request to the file URL.
curl http://localhost:10001/api/v1/files/Shu2vLZm.screenshot.pngTo check if the server is running, you can send a GET request to the health check endpoint.
GET /api/v1/health