BLOG
Browser Use Reached: What Changes When Agents Hit Production
Browser use reached production scale. Learn what that means for hosted Chromium, CDP sessions, and reliable AI browser automation.
# Browser Use Reached: What Changes When Agents Hit Production
Browser use reached a turning point in 2026. What started as a research pattern — an LLM reading a DOM and clicking through a page — is now a workload that teams run continuously, against protected sites, with SLAs attached. The question is no longer whether an agent can drive a browser. It's whether the browser it drives can survive a week of production traffic without leaking state, burning budget, or getting blocked.
That shift is what "browser use reached" actually describes: the moment browser automation stopped being a demo and started being infrastructure. This post covers what changed, where hosted Chromium fits, and how to wire a remote runtime into a browser-use workflow without rewriting your agent loop.
What "browser use reached" means in practice
Browser use reached production when three things became true at once:
- Agents run longer than a single task. A browser-use agent that books a flight is a script. One that monitors inventory across 40 supplier portals every hour is a service. Services need session persistence, retries, and observability.
- Targets got harder. Login walls, bot detection, and per-IP rate limits are now the default on any site worth automating. A local Chrome instance on a laptop IP fails fast.
- Cost became measurable. Teams now track cost per solved task, not cost per API call. That metric only makes sense when browser time is metered separately from model tokens.
If you're still running chromium.launch() on your own machine, none of these pressures have hit you yet. The moment you move to a queue, a worker pool, or a scheduled job, they all arrive together.
Why local browser use stops scaling
The failure mode is predictable. You start with Playwright or Puppeteer on a dev machine. It works. You containerize it. It works, mostly. Then:
- Memory pressure. Each headless Chromium instance holds 150–400 MB resident. Ten concurrent agents on a 4 GB container will OOM-kill mid-task.
- IP reputation. All your sessions share one egress IP. The first time you hit a site with meaningful traffic, you get challenged — and every subsequent session inherits the flag.
- State bleed. Cookies, localStorage, and service workers persist between runs unless you tear down the profile. Agents that "remember" a previous user's session produce wrong answers that are hard to debug.
- No live view. When an agent gets stuck at step 14 of 30, you have a log line and a screenshot. You can't attach to the running session and see what the model sees.
None of these are Playwright bugs. They're the cost of treating a browser as a library instead of a runtime.
Hosted Chromium as the runtime layer
A hosted browser runtime separates the browser from the process that drives it. The agent connects over CDP to a Chromium instance running elsewhere, gets a session-scoped endpoint, and disconnects when done. The runtime handles lifecycle, isolation, and network egress.
Remote Browser is built for exactly this shape of workload. The pieces that matter for browser-use agents:
- Hosted Chromium sessions with a CDP WebSocket endpoint you can pass to Playwright, Puppeteer, or Selenium.
- Persistent profiles so an agent can log in once and reuse authenticated state across runs — scoped to a profile ID, not a shared global cookie jar.
- Session isolation so two concurrent agents never see each other's storage.
- Live viewer for attaching to a running session during debugging.
- Configurable browser settings including proxy configuration and stealth-related launch options, so you can match the runtime to the target site's expectations.
- Usage controls so you can cap spend per team or per workflow.
The point isn't that hosted is always better. It's that browser use reached the stage where the browser is a dependency you operate, not a binary you import.
Connecting a browser-use agent over CDP
The integration path is the same one Playwright documents for connecting over CDP. You take the WebSocket endpoint from your session and hand it to chromium.connectOverCDP.
import { chromium, Browser, BrowserContext, Page } from 'playwright';
interface SessionInfo {
cdpUrl: string;
sessionId: string;
}
// Fetch a session from the Remote Browser API.
// See /documentation for the current endpoint shape and auth headers.
async function createSession(profileId?: string): Promise<SessionInfo> {
const res = await fetch('https://api.remote-browser.dev/v1/sessions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.REMOTE_BROWSER_API_KEY}`,
},
body: JSON.stringify({
profileId, // reuse auth state across runs
proxy: 'residential',
timeoutSeconds: 900,
}),
});
if (!res.ok) {
throw new Error(`session create failed: ${res.status} ${await res.text()}`);
}
return (await res.json()) as SessionInfo;
}
async function runAgentTask(url: string, profileId: string) {
const { cdpUrl, sessionId } = await createSession(profileId);
let browser: Browser | undefined;
try {
browser = await chromium.connectOverCDP(cdpUrl);
const context: BrowserContext = browser.contexts()[0] ?? (await browser.newContext());
const page: Page = context.pages()[0] ?? (await context.newPage());
await page.goto(url, { waitUntil: 'domcontentloaded' });
// Hand `page` to your browser-use loop here. The agent sees a normal
// Playwright Page — CDP is transparent above this line.
await page.getByRole('button', { name: /sign in/i }).click();
await page.waitForLoadState('networkidle');
return await page.title();
} finally {
// Closing the CDP connection does not necessarily end the session.
// Terminate explicitly so you are not billed for idle browser time.
await browser?.close();
await fetch(`https://api.remote-browser.dev/v1/sessions/${sessionId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${process.env.REMOTE_BROWSER_API_KEY}` },
});
}
}Two details worth calling out. First, browser.close() on a CDP connection detaches the client; it does not always kill the remote browser. Terminate the session through the API so metering stops. Second, contexts()[0] is the right entry point for a persistent profile — the runtime restores storage into that default context, so creating a fresh context discards the auth state you paid to establish.
If you're coming from a local browser-use install, the change is one line: swap launch() for connectOverCDP(). The agent's tool layer doesn't need to know.
Local vs hosted: a production comparison
| Dimension | Local Chromium | Hosted Chromium runtime |
|---|---|---|
| Session startup | 300–800 ms per launch | Connection handshake, no cold browser boot |
| Concurrency ceiling | Bound by container memory | Bound by your plan; see /pricing |
| IP reputation | Single egress IP | Configurable proxy per session |
| Auth state | Manual profile dirs, easy to leak | Profile IDs, isolated per session |
| Debugging | Screenshots and logs | Live viewer attached to the running session |
| Cost model | Fixed infra + your time | Metered browser time, itemized |
| Failure blast radius | One OOM kills the worker | Session-scoped; other agents unaffected |
| Stealth posture | Whatever flags you remember | Configurable browser settings per session |
The trade-off is real: hosted adds a network hop and a vendor dependency. For a one-off script, local wins. For anything on a schedule with more than a handful of concurrent agents, the operational surface of local Chromium grows faster than the cost of hosting it.
Where browser-use agents actually break
Once you're running hosted sessions, the remaining failures cluster into four categories. Knowing them upfront saves a week of debugging.
Session lifetime vs task lifetime. Agents that pause for human approval can sit idle for minutes. If your session timeout is shorter than your approval window, the CDP connection drops mid-task. Set timeouts to match your slowest path, and terminate explicitly when the task ends.
Profile drift. A persistent profile that accumulates cookies across dozens of runs eventually looks nothing like a fresh user. Rotate profiles per logical identity, not per task. If you need a clean slate, create a new profile rather than clearing storage on a shared one.
Detection at the network layer. Stealth flags in the browser only get you so far if every request exits from a datacenter ASN. Proxy configuration matters more than user-agent strings. Remote Browser exposes proxy settings per session; match the proxy class to the target site's tolerance.
Unbounded retries. An agent that retries a blocked page ten times burns ten browser-hours for zero progress. Cap retries at the session level and surface the failure to your orchestrator.
Cost and metering
Browser use reached the point where browser time is a line item. The useful mental model is cost per solved task, which combines three inputs: browser-hours consumed, network traffic, and model tokens. Only the first two are the runtime's concern.
Two habits keep that number honest:
- Terminate sessions in a `finally` block. Idle sessions that never close are the single most common source of wasted browser-hours.
- Separate long-lived profiles from short-lived sessions. A profile can persist for weeks; a session should live only as long as the task.
Current rates and plan limits are on /pricing — they change, and quoting them here would go stale.
When hosted isn't the right call
Be honest about the boundary. Stay local if:
- You're prototyping a single workflow and haven't hit a concurrency wall.
- Your targets are internal tools behind a VPN with no bot detection.
- You need sub-50 ms interaction latency and the network hop is measurable in your task success rate.
Move to hosted when you're running more than a few concurrent agents, when IP reputation is affecting success, or when you've spent more time on browser lifecycle than on the agent itself. That last signal is the clearest one.
Getting started
The path from a local browser-use script to a hosted runtime is short:
- Read the /documentation for the current session API and auth model.
- Replace
chromium.launch()withchromium.connectOverCDP(session.cdpUrl). - Move profile handling from local directories to profile IDs.
- Add a
finallyblock that terminates the session. - Attach the live viewer the first time an agent gets stuck — it will change how you debug.
For the broader architecture, see Remote Browser for AI agents and running a remote browser online. If you're specifically wiring up interactive control, remote control browser covers the attach-and-drive pattern.
Browser use reached production because the tooling finally caught up to the workload. The agent loop was never the hard part. The browser was.