BLOG
Browser-Use Library: What It Is and Where It Runs
The browser-use library drives real Chromium from Python. Here is what it does, where it breaks in production, and how to run it on a hosted runtime.
# Browser-Use Library: What It Is and Where It Runs
The browser-use library is an open-source Python package that lets an LLM drive a real Chromium instance: the model reads an indexed view of the page, picks an action, and the library executes it through Playwright or CDP. It is the reference implementation for most "AI agent that uses a browser" demos, and it is genuinely good at that job. What it is not is a runtime. This post covers what the library actually does, where local execution stops scaling, and how to point browser-use at a hosted Chromium session without rewriting your agent.
What the browser-use library actually is
Strip away the framing and browser-use is three things glued together:
- A DOM-to-text serializer. It walks the page, assigns stable numeric indices to interactive elements, and hands the model a compact representation instead of raw HTML. This is the core trick — it keeps token usage bounded and gives the model something it can act on deterministically.
- An action space. Click, type, scroll, extract, go back, switch tab, and a few others. Each action maps to a Playwright call.
- An agent loop. Send state to the model, parse the chosen action, execute it, capture the new state, repeat until the model emits a
doneaction or you hit a step limit.
Underneath, it is Playwright. That matters, because it means everything you know about Playwright's connection model — connect_over_cdp, browser contexts, page events — applies directly. The library is a reasoning layer on top of a browser driver, not a replacement for one.
The project is Python-first, with a TypeScript port and a growing set of integrations (LangChain, LlamaIndex, custom LLM wrappers). It supports multiple model providers, which is useful because the model choice often matters more than the prompt for task success.
Where the library stops and the runtime begins
Here is the search intent most people actually have when they type "browser-use library": *it works on my laptop, why does it fall over in production?*
The answer is almost never the library. It is the browser underneath it.
| Concern | Local browser-use | Hosted Chromium runtime |
|---|---|---|
| Browser lifecycle | You launch and kill Chrome per run | Session created via API, torn down on TTL |
| Concurrency | One machine, N cores, memory-bound | Independent sessions, isolated per task |
| Profile persistence | Local user-data-dir, easy to corrupt | Named profiles that survive restarts |
| Proxies / egress | Manual, per-process | Configurable at session level |
| Debugging | Headed window on your desk | Live viewer + CDP attach from anywhere |
| Failure recovery | Process dies, state is gone | Session state survives worker restarts |
| Cost model | Your infra, your ops time | Metered per browser-hour — see /pricing |
The pattern is consistent: the library handles *reasoning*, and the runtime handles *everything that has to be true for reasoning to happen reliably*. Mixing them is where teams lose weeks.
The three failure modes you will hit
1. Session lifecycle drift
A local script launches Chrome, runs a task, and exits. In production you have a queue, retries, and workers that get recycled. If the browser is a child process of the worker, every deploy, OOM kill, or autoscale event takes live sessions with it.
The fix is to decouple the browser from the worker. Create the session through an API, get a CDP endpoint back, and let the worker connect to it. If the worker dies, the session is still there and a new worker can reattach. This is the same architectural move as separating your app server from your database, and it is the single highest-leverage change you can make.
2. Profile and state loss
Agents that log in, fill carts, or maintain a session across steps need a persistent profile. Local user-data-dir directories work until two runs collide on the same directory, or until the container is rescheduled onto a fresh node. Persistent profiles managed by the runtime — created once, referenced by ID, mounted into each session — remove an entire class of "it worked yesterday" bugs.
3. Detection and IP quality
A datacenter IP plus a stock headless Chromium gets blocked on a meaningful fraction of real sites. This is not a browser-use problem and no prompt fixes it. What helps is configurable browser settings at the session level (user agent, locale, timezone, viewport) plus proxy egress you control. Be skeptical of anything promising to defeat detection outright — the honest framing is that you are reducing the surface area of obvious automation signals, not eliminating them.
Connecting browser-use to a hosted runtime
The cleanest integration point is CDP. Most hosted runtimes expose a WebSocket endpoint per session; you connect Playwright to it and hand the resulting browser object to browser-use.
import { chromium, Browser, BrowserContext } from 'playwright';
interface SessionResponse {
id: string;
cdpUrl: string; // wss://.../devtools/browser/<id>
}
async function createSession(apiKey: string): Promise<SessionResponse> {
const res = await fetch('https://api.remote-browser.dev/v1/sessions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
profileId: 'checkout-agent', // persistent profile
timeoutSeconds: 900,
proxy: { country: 'us' },
}),
});
if (!res.ok) throw new Error(`session create failed: ${res.status}`);
return res.json() as Promise<SessionResponse>;
}
async function runTask(apiKey: string) {
const session = await createSession(apiKey);
// Connect over CDP — no local Chrome, no browser download.
const browser: Browser = await chromium.connectOverCDP(session.cdpUrl);
const context: BrowserContext = browser.contexts()[0];
try {
// Hand `context` to your browser-use agent loop here.
// The agent sees a normal Playwright context; it does not
// know or care that the browser is running in the cloud.
const page = await context.newPage();
await page.goto('https://example.com');
// ... agent steps ...
} finally {
await browser.close(); // closes the CDP connection, not the session
}
}Two details worth internalizing:
browser.close()on a CDP connection detaches your client. Whether it also terminates the remote session depends on the runtime — check the docs before relying on it for cleanup.browser.contexts()[0]is the existing context on the remote browser. Creating a new context is fine too, but reusing the default one is what preserves the profile's cookies and storage.
For the Python side, the equivalent is playwright.chromium.connect_over_cdp(cdp_url), and browser-use accepts a pre-built browser or context in most versions. Check the version you are pinned to — the constructor signature has moved around.
Choosing between self-hosting and a managed runtime
Both are legitimate. The decision is about where you want your operational attention.
Self-host if: you already run container orchestration, you have a hard data-residency requirement that a vendor cannot meet, your volume is high enough that per-hour metering is worse than fixed infra cost, and you have someone who enjoys debugging zombie Chrome processes.
Use a managed runtime if: you want sessions to outlive workers, you need profiles and proxy egress without building them, you want a live viewer for debugging without SSH, and you would rather spend engineering time on the agent than on the browser.
The middle path — self-hosted Playwright in your own cluster, connected to a managed browser — is also common and often the right first step. You keep your orchestration and move only the browser.
If you are still deciding, /blog/remote-browser-for-ai-agents walks through the runtime layer in more depth, and /blog/remote-web-browser covers the practical differences between a remote browser and a headless container you manage yourself.
Production criteria that actually matter
When you evaluate any runtime for browser-use workloads, these are the questions that separate a demo from something you can page on:
- Session isolation. Can two concurrent agents share a browser process? They should not. Verify that each session gets its own browser, not just its own context.
- Reconnect semantics. If your worker restarts, can it reattach to the live session by ID? What is the TTL?
- Profile lifecycle. Are profiles first-class objects with IDs, or just directories you mount? Can you snapshot and restore them?
- CDP fidelity. Does the endpoint support the full protocol, or a subset? Playwright's
connectOverCDPneeds more thanPage.navigate. The Chrome DevTools Protocol is the reference for what "full" means. - Observability. Is there a live viewer? Can you pull a screenshot or a trace after a failure without reproducing it?
- Egress control. Per-session proxy configuration, and a clear answer about which countries are available.
- Metering. Per browser-hour, per session, per task? Idle sessions that bill are a real cost trap. See /pricing for how Remote Browser meters.
A note on the ecosystem
The browser-use library sits in a crowded space. Browserbase, Browserless, Steel, Kernel, Hyperbrowser, and Anchor all offer hosted browser infrastructure, and most publish their own benchmarks. Treat vendor benchmarks as directional, not decisive — the workload that matters is yours, and a 5-point difference on someone else's task suite rarely predicts your success rate on a login flow behind a bot wall.
What is worth comparing is the integration surface. If a runtime speaks CDP and gives you a WebSocket endpoint, browser-use will work with it. If it only offers a proprietary SDK, you are locked into that vendor's agent abstractions. Prefer the former unless you have a specific reason not to.
The library itself is also worth reading before you trust it. The action space is small and the serializer is opinionated; understanding how it indexes the DOM will tell you why certain pages confuse it (heavy shadow DOM, canvas-rendered UIs, virtualized lists) faster than any amount of prompt tuning.
Getting started
The shortest path from a local browser-use script to a production-shaped one:
- Keep your agent loop exactly as it is.
- Replace the local browser launch with a CDP connection to a session created via API.
- Move profile state out of
user-data-dirand into a named profile. - Add a live viewer to your debugging workflow so you can watch a failing run instead of guessing.
- Set a session TTL and make sure your worker can reattach rather than restart.
Steps 1 and 2 are an afternoon. Steps 3 through 5 are what turn a demo into something you can run on a schedule without watching it.
The documentation covers session creation, CDP endpoints, and profile management in detail. If you want to see a session running before you write any code, /blog/remote-browser-online shows what the live viewer looks like from the outside.
The browser-use library is the right tool for the reasoning layer. Give it a browser that does not fall over, and it will do the rest.