Chromium at a URL
Headless or visible Chromium, controllable through a flat HTTP API. Stealth on by default, CDP WebSocket returned at /start — point Puppeteer, Playwright, or any CDP client at it.
# Spawn a stealth Chromium with a fingerprint
$ curl "/start?browser_id=main&stealth=true&timezoneId=America/New_York&locale=en-US"
[ "browser_id": "main",
"webSocketDebuggerUrl": "ws://.../devtools/browser/...",
"stealth": true ]
# Navigate — or pass the WS URL to Puppeteer
$ curl "/browse?browser_id=main&url=https://hoody.com"
200 OK
# Capture; already works with your existing CDP client
$ curl "/screenshot?browser_id=main&format=png" -o shot.png
1920x1080 PNG
Drive, observe, and inspect a real browser over HTTP
Every Browser instance exposes the same primitives Puppeteer and Playwright rely on — navigation, DOM eval, screenshots, DevTools Protocol — but as flat HTTP endpoints. Pick a tab below to see which endpoint runs under the hood.
Start an instance, point it at a URL
GET /start spawns a stealth Chromium with your fingerprint. GET /browse?url=… loads a page. One HTTP roundtrip, no driver install, no docker image to maintain.
Eval JS in the page, return structured data
POST /eval runs arbitrary JavaScript in page context. GET /html and GET /text return the rendered DOM and visible text. No selector library, no headless-browser SDK.
Screenshot or PDF the current page
GET /screenshot returns PNG, JPEG, or base64 — fullPage or viewport. GET /pdf renders to A4/Letter with landscape + margins. Binary comes back directly; no headless-Chrome flags to wrangle.
Grab the CDP URLs, hand them to anything
GET /devtools-url returns three URLs: the CDP WebSocket (for Puppeteer / Playwright / any CDP client), the DevTools HTTP API, and the DevTools frontend UI. Open the frontend URL in a browser tab to watch what the headless instance sees.
Screenshot → decide → click, in a loop
The shape every Computer-Use model expects: a screenshot, a DOM snapshot, a way to click at (x, y). Browser exposes these over plain HTTP — drop it behind Claude Computer Use, OpenAI's Operator, or your own agent.
docker run chromium + fonts + fingerprint patches
curl /start?stealth=true → ready
Start, drive, and tear down Chromium over HTTP
Every endpoint returns deterministic JSON or binary. No stderr scraping, no /tmp files, no race conditions with a subprocess that forgot to flush.
# Full Browser flow — start, navigate, extract, shutdown
URL="https://proj-dev-browser-1.hoody.com"
ID="main"
# 1. Spawn a stealth Chromium
curl -s "$URL/start?browser_id=$ID&stealth=true"
# 2. Navigate
curl -s "$URL/browse?browser_id=$ID&url=https://hoody.com"
# 3. Extract structured data
curl -s -X POST "$URL/eval?browser_id=$ID" \
-H 'Content-Type: application/json' \
-d '["script":"document.title"]'
# > "Example Domain"
# 4. Release the slot
curl -s "$URL/shutdown?browser_id=$ID"
// Full Browser flow — start, navigate, extract, shutdown
const BASE = 'https://proj-dev-browser-1.hoody.com';
const id = 'main';
// 1. Spawn a stealth Chromium
const start = await fetch(`$[BASE]/start?browser_id=$[id]&stealth=true`);
const [ webSocketDebuggerUrl ] = await start.json();
// 2. Navigate
await fetch(`$[BASE]/browse?browser_id=$[id]&url=https://hoody.com`);
// 3. Extract structured data
const r = await fetch(`$[BASE]/eval?browser_id=$[id]`, [
method: 'POST',
headers: [ 'Content-Type': 'application/json' ],
body: JSON.stringify([ script: 'document.title' ])
]);
const title = await r.json();
// => "Example Domain"
// 4. Release the slot
await fetch(`$[BASE]/shutdown?browser_id=$[id]`);
# Full Browser flow — start, navigate, extract, shutdown
import requests
BASE = 'https://proj-dev-browser-1.hoody.com'
id = 'main'
# 1. Spawn a stealth Chromium
start = requests.get(f'[BASE]/start', params=['browser_id': id, 'stealth': 'true'])
ws = start.json()['webSocketDebuggerUrl']
# 2. Navigate
requests.get(f'[BASE]/browse', params=['browser_id': id, 'url': 'https://hoody.com'])
# 3. Extract structured data
r = requests.post(f'[BASE]/eval',
params=['browser_id': id],
json=['script': 'document.title'])
title = r.json()
# => "Example Domain"
# 4. Release the slot
requests.get(f'[BASE]/shutdown', params=['browser_id': id])
Where teams deploy Browser
A flat HTTP layer over Chromium opens up integration points that a locally-installed headless browser can't cover. Six deployment shapes we see most often.
Data pipelines
Ingest rendered HTML and JSON into your warehouse. Cron the /eval calls, pipe results into Snowflake, BigQuery, or Postgres. No per-worker Chromium install, no version drift across your cluster.
Agent frameworks
Plug into Claude Computer Use, OpenAI Operator, or LangChain browser tools. Screenshot + eval is the shape they already expect — Browser is a drop-in HTTP endpoint for whatever agent runtime you use.
Authenticated workflows
Drive logged-in dashboards — SaaS admin panels, bank portals, internal tools. POST /cookies persists the session, so subsequent calls keep the identity without re-authenticating on every run.
Review apps
Per-PR browser previews. Your CI spawns a Browser instance against the staged build, returns the /devtools-url frontend link in the PR comment, reviewers click once and watch the app live.
Scheduled monitoring
Cron-triggered /screenshot and /network captures into uptime, SLO, and perf dashboards. Ring-buffer the last 500 console and network entries for each probe — surface regressions before users do.
Compliance snapshots
Timestamped, fingerprint-locked captures of a third-party page for legal, audit, or evidence workflows. Pin the Chromium channel, the viewport, the geolocation — reproduce the exact render months later.
23 operations, 4 endpoint groups
Every Browser capability is a flat HTTP call. Method variants on the same path are listed separately because they behave differently — GET /eval runs a one-shot script, POST /eval accepts a full body.
Interaction
[count, plural, one [# endpoint] other [# endpoints]]'curl -X POST /eval -d '["script":"document.title"]' → "Example"
Session state
[count, plural, one [# endpoint] other [# endpoints]]'curl -X POST /cookies -d '[[name,value,domain,...]]' → [ added: 4 ]
Introspection
[count, plural, one [# endpoint] other [# endpoints]]'curl /devtools-url?browser_id=main → [ ws, http, frontend ]
Lifecycle
[count, plural, one [# endpoint] other [# endpoints]]'curl /start?browser_id=main&stealth=true → [ ws, stealth: true ]
Plus platform-inherited GET /health and GET /metrics on every Hoody Kit service.
Stealth, fingerprinting, and CDP — without the setup tax
Browser is a thin layer over Chromium — everything you'd install, patch, and babysit is a query parameter away. Six capabilities that matter when you actually ship.
Fingerprint spoofing
Configure UA, viewport, timezone, locale, and geolocation on /start — or point at a fingerprintId to load a preset JSON. Canvas and WebGL follow the profile automatically.
Stealth mode
Patchright anti-detection is on by default for Chromium. Not bulletproof against every detection vendor — pair with a CAPTCHA-solving provider for adversarial sites.
CDP pass-through
GET /devtools-url returns the WebSocket URL, DevTools HTTP API, and frontend UI. Existing Puppeteer or Playwright code keeps working — connect, don't rewrite.
Proxy & DoH
SOCKS5, SOCKS5h, HTTP, HTTPS proxies with auth and bypass lists. DNS-over-HTTPS via Cloudflare is default on. QUIC/HTTP3 opt-in per instance.
Chromium channels
Pin any Chromium build — full version string, major version, or channel (stable / beta / dev / canary). Firefox also supported via browser=firefox.
Debug ring buffers
Last 500 console entries and 500 network entries per instance, plus persistent browsing history with query and delete endpoints. No need to wire an external logging layer.
Add a real browser to your stack
Browser is one of the Hoody Kit services bundled with every container. No driver install, no Puppeteer scaffolding, no fingerprint patching. Start with a URL.