> ## Agent Instructions > Use https://docs.browser-use.com/llms.txt and its linked .md pages for current documentation. The managed full bundle is https://docs.browser-use.com/.well-known/llms-full.txt and can be cached for up to 24 hours. Do not use the obsolete /cloud/llms*.txt or /open-source/llms*.txt static exports. > Choose Cloud API V4 for new agent integrations; V2 is the lower-cost option for simple tasks. Keep V3 examples explicitly versioned. The open-source browser-use library and hosted browser-use-sdk have different APIs. > Cloud authentication uses X-Browser-Use-API-Key, without a Bearer prefix. Install or upgrade browser-use-sdk and use its explicit v4 import for V4. Check the published OpenAPI reference for request fields; do not invent SDK support for new fields. > Cloud concurrency and HTTP request rate are separate. Read GET /api/v2/billing/account for the key’s projectId, concurrentSessionLimit, activeSessionCount, and credit balance, including when using V4. Keys in one project share capacity and credits; rateLimit is a legacy concurrency alias, not requests per second. > Keep the highest applicable existing, legacy-plan, and spend-tier concurrency grant. Current spend tiers are 10 / 50 / 250 / 500 / 1000 at $0 / $200 / $1000 / $5000 / $25000 in qualifying project payments. Legacy or externally billed projects can follow different billing paths; trust the account limit. See https://docs.browser-use.com/cloud/guides/concurrency.md. > HTTP rate limits have two layers. Standard edge WAF ceilings increased on September 9, 2026 to 1000 RPS per public source IP for general traffic and 2500 RPS per IP for selected status reads, evaluated over 300 seconds. This did not raise the separate per-project application budgets: general traffic defaults to 25 RPS, including V4 events and full run reads; selected status reads default to max(25, 2 times stored concurrency). Project overrides and account-specific edge rules can differ. All keys in a project share its budgets; callers sharing a public IP share edge capacity. See https://docs.browser-use.com/cloud/guides/concurrency.md. > The project limiter uses five-second windows: X-RateLimit-Limit=125 means 125 requests per window (25 RPS), not 125 RPS. Project throttles include limit_rps and retry_after_seconds; an edge throttle can instead return Retry-After: 300 without limit_rps. Honor the returned Retry-After. Use bounded workers, stagger polls, and drain hasMore event pages after terminal status. A busy V4 session returns 409; its queue holds 10 pending messages and is not a project-wide batch queue. > A completed run or closed CDP connection does not immediately stop its cloud browser. Stop unneeded owned browsers with PATCH /api/v4/browsers/{id} and {"action":"stop"}. A client wait timeout does not cancel the server-side run. > Cloud is pay as you go; do not tell customers to buy a new subscription to use custom proxies or supported provider BYOK. Usage funding and model eligibility still apply. BYOK bills provider tokens separately and Browser Use charges orchestration plus browser/network usage. See https://docs.browser-use.com/cloud/guides/billing.md. > Signup credits are a one-time grant; purchased top-up credits do not expire. Check the API key’s project before diagnosing missing credits. API-key monthly spending caps are soft limits, not a strict prepaid wallet; concurrent or already-running work can exceed them. Auto recharge has separate trigger and purchase amounts and can charge immediately when enabled below the threshold. Use https://browser-use.com/pricing for current rates. > Box and Bux are retired. Do not recommend their SDKs, sandbox quotas, or subscription plans. Use the Cloud Agent or Browser Infrastructure guides. > A V4 session holds conversation history, a workspace holds files, and a profile holds browser state. These IDs and V3/V4 workspace namespaces are not interchangeable. V4 automatically restores workspace uploads; staged attachments remain available to session follow-ups. Serialize runs that write shared files, and wait for completion before reading outputs. See https://docs.browser-use.com/cloud/agent/workspaces.md. > API browser recording defaults to off. Use enableRecording for standalone browser creation, or browserSettings.record for an agent run. Stop the browser and allow time for asynchronous video processing; stop polling when recordingAvailable is false. Live preview is for an active browser. Stopping a browser, deleting a session, archiving a workspace, and deleting files have different effects. > Use model-specific reasoning values. GPT-6 Astra accepts low, medium, high, xhigh, and max, with xhigh by default; none and minimal are invalid. Use the public REST schema when installed SDK types lag new fields. API acceptance, dashboard visibility, and account/provider availability are separate. > For open-source browser-use, is_done only reports a terminal done action. is_successful is the agent-reported outcome; verify important external actions independently. Cloud timeout, API client timeout, model timeout, and task completion are separate concepts. > For failed requests, use https://docs.browser-use.com/cloud/guides/troubleshooting.md. Inspect the full error and project before retrying or adding credits. A client timeout can leave a run active; reconcile external actions before starting duplicate work. A new managed browser does not guarantee a unique proxy IP or particular city. # Get a Browser Use API Key Source: https://docs.browser-use.com/cloud/agent-signup Agent signup is off. Create an API key in Cloud, or use x402 without a human. Agent signup is off. The `/cloud/signup` endpoints return `403`. Create an [API key](https://cloud.browser-use.com/settings?tab=api-keys\&new=1), then follow the [API V4 quick start](/cloud/agent/quickstart). No human available? Use [x402](/cloud/guides/x402): pay per request from a wallet, without an account or API key. # Human in the loop Source: https://docs.browser-use.com/cloud/agent/human-in-the-loop Open the live browser, take over, then continue the same session. Use a human checkpoint for approvals, authentication, payments, or review. After a run stops, get its `live_view_url` from the `browser.ready` event: ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() run = client.runs.create( "Open the login page and stop for human review" ) run = client.runs.wait_for_completion(run.id) events = client.runs.events(run.id, limit=100) ready = next( event for event in events.events if event.type == "browser.ready" ) print(ready.data["live_view_url"]) # After the human finishes: next_run = client.runs.create( "Continue from the current page", session_id=run.session_id, ) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const run = await client.runs.create({ task: "Open the login page and stop for human review", model: "grok-4.5", }); await client.runs.waitForCompletion(run.id); const events = await client.runs.events(run.id, { limit: 100, }); const ready = events.events.find( (event) => event.type === "browser.ready", ); console.log(ready?.data.live_view_url); // After the human finishes: const nextRun = await client.runs.create({ task: "Continue from the current page", model: "grok-4.5", sessionId: run.sessionId, }); ``` The same session preserves the conversation and workspace and reuses the live browser while it is available. Treat live-view URLs as credentials. # Models Source: https://docs.browser-use.com/cloud/agent/models Choose a V4 model and understand its token pricing. Omit `model` to use **GPT-5.6 Luna**, or pass a model ID when creating a run. The following Browser Use-hosted models have published token pricing: | Model | API string | Input | Cache read | Output | BYOK | | -------------------------- | --------------- | -----: | ---------: | ------: | --------- | | GPT-5.6 Luna (recommended) | `gpt-5.6-luna` | \$0.24 | \$0.024 | \$1.44 | OpenAI | | Claude Opus 5 | `claude-opus-5` | \$6.00 | \$0.60 | \$30.00 | Anthropic | | Grok 4.5 | `grok-4.5` | \$2.40 | \$0.36 | \$7.20 | — | | GPT-5.6 Sol | `gpt-5.6-sol` | \$6.00 | \$0.60 | \$36.00 | OpenAI | | MiniMax M3 | `minimax-m3` | \$0.36 | \$0.072 | \$1.44 | — | Token prices are USD per 1 million tokens. Browser sessions (\$0.02/hour) and network traffic (\$5/GB managed proxy or \$0.20/GB proxyless/BYOP) are charged separately. See [full pricing](https://browser-use.com/pricing). See [Thinking levels](/cloud/agent/thinking-levels) for V4 `modelParams` and the normalized `thinkingLevel` values available in V2 and V3. We recommend **GPT-5.6 Luna** for most tasks. It combines near-Opus benchmark performance with the lowest token price and fastest median response time in current V4 production traffic. ## All supported V4 model IDs The V4 request schema accepts the following model IDs. The Cloud dashboard may show a smaller curated set; availability can also depend on your account's rollout and configured provider key. An accepted ID does not guarantee access to every upstream provider. Experimental models are not recommended as a production default. MiniMax M3 remains an API option even where it is absent from the dashboard picker. | Provider | Model IDs | BYOK provider | | ----------------------- | ------------------------------------------------------------------------------------------ | ------------- | | Anthropic | `claude-opus-4.7`, `claude-opus-4.8`, `claude-opus-5`, `claude-fable-5`, `claude-sonnet-5` | Anthropic | | OpenAI | `gpt-5.5`, `gpt-5.6`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-6-astra` | OpenAI | | Google | `gemini-3-flash`, `gemini-3.1-pro`, `gemini-3.5-flash`, `gemini-3.6-flash` | Google | | xAI | `grok-4.5`, `grok-4.6` | — | | Z.ai | `glm-5.2`, `glm-5.3-flash` | — | | DeepSeek (experimental) | `deepseek-v4-flash-vision` | — | | Moonshot AI | `kimi-k3` | — | | MiniMax | `minimax-m3` | — | See [Thinking levels](/cloud/agent/thinking-levels) for the reasoning controls accepted by each model. Not every model accepts `modelParams`. ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() run = client.runs.create( "Compare three project-management tools", model="gpt-5.6-luna", ) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const run = await client.runs.create({ task: "Compare three project-management tools", model: "gpt-5.6-luna", }); ``` ```bash curl theme={null} curl https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"task":"Compare three PM tools","model":"gpt-5.6-luna"}' ``` ## Bring your own key BYOK is available to pay-as-you-go customers; no recurring subscription is required. After adding credits, add an Anthropic, OpenAI, or Google key under **Settings → API Keys → Bring Your Own Key**. V4 uses it automatically for matching models; no request flag is needed. You pay the provider directly, plus a 0.2× Browser Use orchestration fee. Models from other providers use Browser Use-managed keys. # Observability Source: https://docs.browser-use.com/cloud/agent/observability Poll ordered V4 events to monitor a run or build a custom UI. If you only need the result, use `runs.wait_for_completion()` in Python or `runs.waitForCompletion()` in TypeScript. It polls the lightweight status endpoint instead of downloading every event. For a custom UI, poll `runs.events()` with the previous cursor. Read status **before** fetching events so that the final fetch happens after you observe a terminal state, and drain all `hasMore` pages before stopping: ```python Python theme={null} import time after = None while True: status = client.runs.status(run.id).status.value page = client.runs.events(run.id, after=after) for event in page.events: print(event.type, event.data) after = page.next_after if page.next_after is not None else after if status in {"completed", "failed", "cancelled"} and not page.has_more: break time.sleep(2) ``` ```typescript TypeScript theme={null} let after: number | undefined; while (true) { const { status } = await client.runs.status(run.id); const page = await client.runs.events(run.id, { after }); for (const event of page.events) { console.log(event.type, event.data); } after = page.nextAfter ?? after; if (["completed", "failed", "cancelled"].includes(status) && !page.hasMore) break; await new Promise((resolve) => setTimeout(resolve, 2000)); } ``` Events cover run lifecycle, model calls, browser readiness, tool activity, artifacts, and completion. See [Get run events](/cloud/api-v4/runs/get-run-events) for the complete response shape. This two-second interval is an example for one stream, not a safe default for an unbounded batch. Event reads share the project's general request bucket. Use a shared rate limiter and stagger polling across runs. Keep the cursor when backing off after a 429, and resume from it rather than rereading history. See [Concurrency and rate limits](/cloud/guides/concurrency) for capacity, request budgets, retries, and the separate per-session message queue. # Run a task Source: https://docs.browser-use.com/cloud/agent/quickstart Give a high-accuracy browser agent a goal and get the result. Create an [API key](https://cloud.browser-use.com/settings?tab=api-keys\&new=1) and export it: ```bash theme={null} export BROWSER_USE_API_KEY=your_key ``` ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse with BrowserUse() as client: run = client.runs.create("Find the top Hacker News story") result = client.runs.wait_for_completion(run.id) print(result.result) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const run = await client.runs.create({ task: "Find the top Hacker News story", }); const result = await client.runs.waitForCompletion(run.id); console.log(result.result); ``` ```bash curl theme={null} curl https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"task":"Find the top Hacker News story"}' ``` Install the latest SDK with `pip install --upgrade browser-use-sdk` or `npm install browser-use-sdk@latest`. Curl needs no installation. Every new run implicitly creates a [session](/cloud/agent/sessions) for its conversation and live browser, plus a [workspace](/cloud/agent/workspaces) for persistent files. A task starts a run inside a session and the run reads and writes persistent workspace files A task starts a run inside a session and the run reads and writes persistent workspace files Give the compact context file to your coding agent. # Rerunnable scripts Source: https://docs.browser-use.com/cloud/agent/scripts Save a Browser Use task as a rerunnable script for repeated live data extraction and self-healing runs. Teach a Cloud agent a repeated browser task once. It can save the working code in its [workspace](/cloud/agent/workspaces). Later runs reuse that script, fetch fresh data from the live site, and repair the code if the site changed. This pattern works well for repeated data extraction, checks, and reports. The example below collects the top five Hacker News stories. The first run writes and tests the script. Every later run sends a new request to the live page. A first agent run saves and tests script.py and a README in a workspace; later runs reuse the script and repair it only when necessary A first agent run saves and tests script.py and a README in a workspace; later runs reuse the script and repair it only when necessary The file stays. The running process does not. Each later run starts an agent again, but it can reuse the code instead of rebuilding the browser workflow. ## Save the working script Create one workspace, then ask the first run to do the job and save a tested script: ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() workspace = client.workspaces.create(name="live-hn-data") first = client.runs.create( """ Open https://news.ycombinator.com/ and return the top five story titles and URLs as JSON. Save a script at scripts/extract_top_stories.py that collects the same fields. The script must send a fresh request to the live page every time it runs. Do not hard-code story titles, save the page HTML, or read an old result. Print JSON with source_url set to https://news.ycombinator.com/ and include fetched_at, source_sha256, and stories. Run the script twice and save the outputs as proof/run-1.json and proof/run-2.json. Confirm both outputs have a fresh fetched_at value and five stories. Add scripts/README.md with the command to run it again. """, model="grok-4.5", workspace_id=workspace.id, ) client.runs.wait_for_completion(first.id) saved = client.workspaces.files(workspace.id, prefix="scripts/") print([file.path for file in saved.files]) print(f"Keep this workspace ID: {workspace.id}") ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const workspace = await client.workspaces.create({ name: "live-hn-data" }); const first = await client.runs.create({ task: ` Open https://news.ycombinator.com/ and return the top five story titles and URLs as JSON. Save a script at scripts/extract_top_stories.py that collects the same fields. The script must send a fresh request to the live page every time it runs. Do not hard-code story titles, save the page HTML, or read an old result. Print JSON with source_url set to https://news.ycombinator.com/ and include fetched_at, source_sha256, and stories. Run the script twice and save the outputs as proof/run-1.json and proof/run-2.json. Confirm both outputs have a fresh fetched_at value and five stories. Add scripts/README.md with the command to run it again. `, model: "grok-4.5", workspaceId: workspace.id, }); await client.runs.waitForCompletion(first.id); const saved = await client.workspaces.files(workspace.id, { prefix: "scripts/", }); console.log(saved.files.map((file) => file.path)); console.log(`Keep this workspace ID: ${workspace.id}`); ``` ## Rerun it later Copy the workspace ID from the first process. Pass that ID without a session ID to start a new conversation with the saved files: ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() workspace_id = "your-workspace-id" later = client.runs.create( """ Run python scripts/extract_top_stories.py and return its JSON. Check that fetched_at is current, source_url is https://news.ycombinator.com/, and it has five stories. If the script fails or the result is malformed, inspect the live page and repair the saved script before returning the new result. """, model="grok-4.5", workspace_id=workspace_id, ) result = client.runs.wait_for_completion(later.id) print(result.result) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const workspaceId = "your-workspace-id"; const later = await client.runs.create({ task: ` Run python scripts/extract_top_stories.py and return its JSON. Check that fetched_at is current, source_url is https://news.ycombinator.com/, and it has five stories. If the script fails or the result is malformed, inspect the live page and repair the saved script before returning the new result. `, model: "grok-4.5", workspaceId, }); const result = await client.runs.waitForCompletion(later.id); console.log(result.result); ``` ## Live data, not a cached answer The saved script must request the source page on every execution. `fetched_at` shows when the request ran, and `source_sha256` identifies the response body. The data can stay the same between two runs when the page has not changed. The script still fetched it again. Do not put old output, page HTML, or fixed values inside the script. Save output files only as optional history or proof. ## Can rerunnable tasks be cheaper at scale? Reusing code can remove browser steps and model work that would otherwise be repeated on every extraction. Every later run still starts a model and uses tokens, so the savings depend on the task, site, and model. Compare the cost and duration of the first run with later runs in your own project before making a savings claim. ## Workspace or session? * Pass `workspace_id` / `workspaceId` for a new conversation with the same files. The new run gets a new browser. * Pass `session_id` / `sessionId` to continue the old conversation and its workspace. Cloud can also reuse that session's browser while it is still alive. A saved script can break when a site changes. Ask the agent to check the live result and self-heal the script instead of trusting stale output. # Sessions Source: https://docs.browser-use.com/cloud/agent/sessions Continue one conversation across multiple V4 runs. A **session** holds the agent's conversation and can reuse its live browser. One session ID can contain multiple runs. Every run creates a session implicitly unless you pass an existing session ID. One session ID containing three sequential runs, where each run continues the same conversation, workspace, and live browser One session ID containing three sequential runs, where each run continues the same conversation, workspace, and live browser Pass `session_id` / `sessionId` to continue: ```python Python theme={null} first = client.runs.create("Open Hacker News") client.runs.wait_for_completion(first.id) follow_up = client.runs.create( "Now summarize the top story", session_id=first.session_id, ) result = client.runs.wait_for_completion(follow_up.id) print(result.result) ``` ```typescript TypeScript theme={null} const first = await client.runs.create({ task: "Open Hacker News", model: "grok-4.5", }); await client.runs.waitForCompletion(first.id); const followUp = await client.runs.create({ task: "Now summarize the top story", model: "grok-4.5", sessionId: first.sessionId, }); const result = await client.runs.waitForCompletion(followUp.id); console.log(result.result); ``` Omit the session ID for a new conversation. Pass only a [workspace ID](/cloud/agent/workspaces) when you want a fresh conversation that keeps the same files. # Structured output Source: https://docs.browser-use.com/cloud/agent/structured-output Ask for JSON and validate the V4 result in your application. V4 returns `run.result` as a string. Ask for JSON only, then validate it client-side: ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse from pydantic import BaseModel client = BrowserUse() class Story(BaseModel): title: str points: int run = client.runs.create( 'Find the top HN story. Return only {"title":"...","points":0}.' ) run = client.runs.wait_for_completion(run.id) story = Story.model_validate_json(run.result or "{}") ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; import { z } from "zod"; const client = new BrowserUse(); const Story = z.object({ title: z.string(), points: z.number(), }); const run = await client.runs.create({ task: 'Find the top HN story. Return only {"title":"...","points":0}.', model: "grok-4.5", }); const result = await client.runs.waitForCompletion(run.id); const story = Story.parse(JSON.parse(result.result ?? "{}")); ``` V4 does not accept `output_schema` / `outputSchema`. Handle validation errors and retry with a [session follow-up](/cloud/agent/sessions) when needed. # Thinking levels Source: https://docs.browser-use.com/cloud/agent/thinking-levels Configure model reasoning depth across API V2, V3, and V4. Browser Use exposes reasoning controls in two forms: * **API V4** uses provider-native values inside `modelParams`. * **API V3 and V2** use the provider-neutral `thinkingLevel` field with `disabled`, `low`, `medium`, or `high`. See [Models](/cloud/agent/models) for the complete V4 model list, recommended model, token pricing, and BYOK routes. ## API V4 model parameters V4 forwards an allow-listed `modelParams` object to the selected provider. The field names and accepted values therefore differ by model family. | Model strings | Path | Accepted values | | ------------------------------------------------------------------------ | ------------------------------ | ----------------------------------------------- | | `gpt-6-astra` | `reasoning.effort` | `low`, `medium`, `high`, `xhigh`, `max` | | `gpt-5.5` | `reasoning.effort` | `none`, `low`, `medium`, `high`, `xhigh` | | `gpt-5.6`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` | `reasoning.effort` | `none`, `low`, `medium`, `high`, `xhigh`, `max` | | `claude-opus-4.7`, `claude-opus-4.8`, `claude-opus-5`, `claude-sonnet-5` | `output_config.effort` | `low`, `medium`, `high`, `xhigh`, `max` | | Same Claude models | `thinking.type` | `adaptive`, `disabled` | | Same Claude models | `thinking.display` | `omitted`, `summarized` | | `gemini-3-flash`, `gemini-3.5-flash`, `gemini-3.6-flash` | `thinkingConfig.thinkingLevel` | `minimal`, `low`, `medium`, `high` | | `gemini-3.1-pro` | `thinkingConfig.thinkingLevel` | `low`, `medium`, `high` | `glm-5.2`, `glm-5.3-flash`, `grok-4.5`, `grok-4.6`, `deepseek-v4-flash-vision`, `kimi-k3`, `minimax-m3`, and `claude-fable-5` do not currently accept V4 `modelParams`. Omitting `modelParams` applies Browser Use's defaults for that V4 model. For example, `gpt-5.6-luna` and `gpt-6-astra` default to `reasoning.effort: xhigh`. Astra does not accept `none` or `minimal`. Send an empty object (`"modelParams": {}`) to opt out and use the provider's defaults. ### V4 examples ```bash OpenAI theme={null} curl -X POST https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "task": "Compare three project-management tools", "model": "gpt-5.6-luna", "modelParams": {"reasoning": {"effort": "high"}} }' ``` ```bash Anthropic theme={null} curl -X POST https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "task": "Compare three project-management tools", "model": "claude-opus-5", "modelParams": {"output_config": {"effort": "high"}} }' ``` ```bash Google theme={null} curl -X POST https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "task": "Compare three project-management tools", "model": "gemini-3.6-flash", "modelParams": {"thinkingConfig": {"thinkingLevel": "high"}} }' ``` ## API V3 support V3 accepts the normalized REST field `thinkingLevel`. Legacy aliases are normalized before validation: `bu-mini` maps to `gemini-3-flash`, `bu-max` maps to `claude-sonnet-5`, and `bu-ultra` maps to `claude-opus-4.6`. | Model strings | `disabled` | `low` | `medium` | `high` | | ----------------------------------------------------------------------------------------------------------------------------------------- | :--------: | :---: | :------: | :----: | | `bu-mini`, `gemini-3-flash`, `gemini-3.5-flash` | Yes | Yes | Yes | Yes | | `bu-max`, `bu-ultra`, `claude-sonnet-4.6`, `claude-opus-4.6`, `claude-opus-4.7`, `claude-sonnet-5`, `claude-opus-4.8`, `claude-haiku-4.5` | Yes | Yes | Yes | Yes | | `gpt-5.2`, `gpt-5.4-mini`, `gpt-5.5`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` | Yes | Yes | Yes | Yes | | `gpt-5-mini` | — | Yes | Yes | Yes | | `gemini-3-pro`, `gemini-3.1-pro` | — | Yes | — | Yes | | `glm-5.2` | Yes | — | — | Yes | | `minimax-m3` | — | — | — | — | For a follow-up task sent to an existing V3 session: * Omit `thinkingLevel` to retain the session's current setting. * Send a supported value to update the setting for the next task and later follow-ups. * Send `"thinkingLevel": null` to clear it and return to provider defaults. The requested value is validated against the existing session's model, not the request model default. ## API V2 support V2 also accepts the normalized REST field `thinkingLevel`, but it has two additional limitations. It cannot configure GLM thinking, and it cannot enable fixed-budget thinking on older Claude 4/4.5 models because the legacy worker cannot safely replay their thinking blocks. Those Claude models still accept `disabled`. | Model strings | `disabled` | `low` | `medium` | `high` | | --------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------: | :---: | :------: | :----: | | `browser-use-llm`, `browser-use-2.0`, `gemini-2.5-flash`, `gemini-3-flash-preview`, `gemini-3.5-flash`, `gemini-flash-latest`, `gemini-flash-lite-latest` | Yes | Yes | Yes | Yes | | `gemini-2.5-pro` | — | Yes | Yes | Yes | | `gemini-3-pro-preview`, `gemini-3.1-pro-preview` | — | Yes | — | Yes | | `o3`, `o4-mini` | — | Yes | Yes | Yes | | `gpt-5.5`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna` | Yes | Yes | Yes | Yes | | `claude-sonnet-5`, `claude-opus-4-7`, `claude-opus-4-8`, `claude-opus-5` | Yes | Yes | Yes | Yes | | `claude-sonnet-4-20250514`, `claude-sonnet-4-5-20250929`, `claude-opus-4-5-20251101` | Yes | — | — | — | | `gpt-4.1`, `gpt-4.1-mini`, `glm-5.2`, `minimax-m3`, `llama-4-maverick-17b-128e-instruct`, `claude-3-7-sonnet-20250219` | — | — | — | — | V2's existing `thinking` boolean controls legacy agent behavior. It does not select a provider reasoning depth and does not replace `thinkingLevel`. ## Provider mappings for V2 and V3 Browser Use validates the normalized V2/V3 value before dispatch, then maps it to the provider's native control. | Provider/model family | Mapping | | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Gemini 3 Flash | `disabled` becomes provider level `minimal`; `low`, `medium`, and `high` map directly. | | Gemini 3.1 Pro | Supports only provider levels `low` and `high`. Legacy Gemini 3 Pro names route here. | | Gemini 2.5 | Uses thinking budgets of 0, 1,024, 4,096, and 8,192 tokens. Gemini 2.5 Pro cannot use the zero-token budget. | | Claude 4.6+ and Claude 5 | V3 uses adaptive thinking with the requested effort. V2 sends the effort without replaying thinking blocks. `disabled` turns thinking off where supported. | | Earlier supported Claude 4/4.5 | Uses fixed budgets of 1,024, 4,096, and 8,192 tokens. V2 cannot enable these fixed-budget modes. | | GPT-5.1+ | `disabled` becomes OpenAI reasoning effort `none`; the other levels map directly. | | Earlier GPT-5, o3, and o4 | Supports `low`, `medium`, and `high`, but not `disabled`. | | GLM | Exposes only a switch: `disabled` turns thinking off and `high` turns it on. V2 does not support the switch. | ## BYOK behavior * **V2:** `thinkingLevel` uses the existing model route. It does not add a per-request BYOK switch. * **V3:** set `useOwnKey: true` for models that require a provider key. Native and BYOK routes use the same support matrix. * **V4:** add an Anthropic, OpenAI, or Google key under **Settings → API Keys → Bring Your Own Key**. V4 automatically uses a matching key; there is no request flag. `modelParams` works the same with Browser Use-managed and customer-managed keys. ## V2 and V3 examples REST JSON uses the camelCase wire field `thinkingLevel` in both API versions. ```bash API V3 theme={null} curl -X POST https://api.browser-use.com/api/v3/sessions \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "task": "Compare three project-management tools", "model": "claude-opus-4.7", "thinkingLevel": "high" }' ``` ```bash API V2 theme={null} curl -X POST https://api.browser-use.com/api/v2/tasks \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "task": "Compare three project-management tools", "llm": "browser-use-2.0", "thinkingLevel": "high" }' ``` ## Validation errors Unsupported V2/V3 model-level pairs return HTTP 422. The request field is camelCase, while the validation detail currently names the backend field in snake\_case: ```text theme={null} Model "gemini-3.1-pro" (provider "google") does not support thinking_level="medium". Supported values: low, high ``` V4 validates the provider-native path and uses its wire name in the error: ```text theme={null} Model "gemini-3.1-pro" does not support modelParams.thinkingConfig.thinkingLevel='minimal'. Supported values: high, low, medium ``` ## Compatibility and API references The normalized V2/V3 field is optional and nullable. New sessions use provider defaults until a level is supplied. Existing V3 sessions retain their current setting when the field is omitted; send `null` to clear it. The current Python and TypeScript SDKs expose the V2/V3 and V4 reasoning fields in their request types and client methods. Generated V4 `modelParams` schema. Generated V3 `thinkingLevel` schema. Generated V2 `thinkingLevel` schema. # Workspaces & files Source: https://docs.browser-use.com/cloud/agent/workspaces Persist files across V4 runs and conversations. A **workspace** stores files that can be restored into later V4 runs, including runs in a different session. Use it for inputs, scripts, and generated files. A session holds the conversation; a workspace holds files; a profile holds browser login state. Their IDs are different. Create and reuse workspaces through API V4. A V3 workspace ID is not a V4 workspace ID. Workspace files are synchronized to storage between runs. This is not a live shared filesystem between simultaneous sessions: finish one writer before starting another run that needs its changes. Two independent sessions reading and writing people.csv, script.py, and output.json in one persistent workspace Two independent sessions reading and writing people.csv, script.py, and output.json in one persistent workspace ## Upload and attach a file ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() workspace = client.workspaces.create(name="research") client.workspaces.upload(workspace.id, "people.csv") run = client.runs.create( "Find everyone in the CSV who works at Google", workspace_id=workspace.id, ) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const workspace = await client.workspaces.create({ name: "research", }); await client.workspaces.upload( workspace.id, "people.csv", ); const run = await client.runs.create({ task: "Find everyone in the CSV who works at Google", model: "grok-4.5", workspaceId: workspace.id, }); ``` V4 restores files uploaded to that workspace under `uploads/` when a run uses its `workspaceId`. You do not need to repeat their IDs in `attachedFileIds`. Staged attachments are different: attach their IDs when creating the run. Files attached to a session remain available to its follow-ups. An explicitly attached staged file takes precedence over a workspace upload with the same stored filename. For a new conversation that needs persistent files, pass the same `workspaceId`; reusing a workspace does not copy conversation history. ## Retrieve created files Ask the agent to save its output, wait for the run to finish, then list the workspace: ```python Python theme={null} result = client.runs.wait_for_completion(run.id) files = client.workspaces.files( workspace.id, include_urls=True, ) for file in files.files: print(file.path, file.url) ``` ```typescript TypeScript theme={null} const result = await client.runs.waitForCompletion(run.id); const files = await client.workspaces.files( workspace.id, { includeUrls: true }, ); for (const file of files.files) { console.log(file.path, file.url); } ``` Download URLs expire after 60 seconds. See the [workspace API reference](/cloud/api-v4/workspaces/list-workspace-files) for pagination and limits. ## Storage and upload limits Check `GET /api/v4/workspaces/{workspace_id}/size` for `usedBytes` and `maxBytes`. The current V4 upload allowance is 950 MiB per workspace for every account, with 50 MiB per file. It leaves room for runtime files on the agent's 1 GiB persistent disk. An upload quota is separate from the number of workspaces you can create, and from temporary disk used by a running agent. A 413 response means a file or workspace limit was exceeded. Remove unneeded files, split large files, or use a fresh workspace. Do not retry an unchanged oversized upload. Deleting a session, archiving a workspace, and deleting a workspace are different operations; archiving is not a file-deletion request. Download a file promptly or request a fresh URL when its link expires. A short-lived download URL does not mean the stored file expires after 60 seconds. # API Reference Source: https://docs.browser-use.com/cloud/api-reference Authenticate and start using the Browser Use REST API. ## Authentication All requests require an API key in the `X-Browser-Use-API-Key` header: ``` X-Browser-Use-API-Key: bu_your_key_here ``` Get a key at [cloud.browser-use.com/settings](https://cloud.browser-use.com/settings?tab=api-keys\&new=1). Keys start with `bu_`. ## Base URL ``` https://api.browser-use.com/api/v3 ``` See [Thinking levels](/cloud/agent/thinking-levels) for V3 model support, follow-up behavior, BYOK handling, and a `thinkingLevel` request example. ## Quick example ```bash Create a session theme={null} curl -X POST https://api.browser-use.com/api/v3/sessions \ -H "X-Browser-Use-API-Key: bu_your_key_here" \ -H "Content-Type: application/json" \ -d '{"task": "Find the top 3 trending repos on GitHub today"}' ``` ```bash Get session result (replace SESSION_ID) theme={null} curl https://api.browser-use.com/api/v3/sessions/SESSION_ID \ -H "X-Browser-Use-API-Key: bu_your_key_here" ``` ## Environment variable Set the key once so SDKs pick it up automatically: ```bash theme={null} export BROWSER_USE_API_KEY=bu_your_key_here ``` *** Prefer the SDK? See the [Agent docs](/cloud/agent/quickstart) — the SDK has all API endpoints available as methods, including `client.browsers.create()`. ```bash Python theme={null} pip install browser-use-sdk ``` ```bash TypeScript theme={null} npm install browser-use-sdk ``` # API key Source: https://docs.browser-use.com/cloud/api-v2-overview Set your API key to access the Browser Use v2 REST API. Get a key at [cloud.browser-use.com/settings](https://cloud.browser-use.com/settings?tab=api-keys\&new=1), then: ```bash theme={null} export BROWSER_USE_API_KEY=your_key ``` Base URL: `https://api.browser-use.com/api/v2` See [Thinking levels](/cloud/agent/thinking-levels) for V2 model support, provider mappings, and a `thinkingLevel` request example. *** Prefer the SDK? See the [Agent (v2) docs](/cloud/legacy/agent). ```bash Python theme={null} pip install browser-use-sdk ``` ```bash TypeScript theme={null} npm install browser-use-sdk ``` # Get Account Billing Source: https://docs.browser-use.com/cloud/api-v2/billing/get-account-billing /cloud/openapi/v2.json get /billing/account Get authenticated account information including credit balance and account details. # Create Browser Session Source: https://docs.browser-use.com/cloud/api-v2/browsers/create-browser-session /cloud/openapi/v2.json post /browsers Create a new browser session. **Pricing:** Browser sessions are charged at $0.02/hour for all users. The full rate is charged upfront when the session starts. When you stop the session, any unused time is automatically refunded proportionally. Billing is rounded up to the minute (minimum 1 minute). For example, if you stop a session after 30 minutes, you'll be refunded half the charged amount. **Session Limits:** - All users: Up to 4 hours per session # Get Browser Session Source: https://docs.browser-use.com/cloud/api-v2/browsers/get-browser-session /cloud/openapi/v2.json get /browsers/{session_id} Get detailed browser session information including status and URLs. # List Browser Session Downloads Source: https://docs.browser-use.com/cloud/api-v2/browsers/list-browser-session-downloads /cloud/openapi/v2.json get /browsers/{session_id}/downloads List files the browser downloaded to S3 during the session. Pass ``includeUrls=true`` to receive presigned download URLs (15 min expiry) inline. Files are stored at ``downloads/projects/{project_id}/sessions/{session_id}/`` in the private bucket. # List Browser Sessions Source: https://docs.browser-use.com/cloud/api-v2/browsers/list-browser-sessions /cloud/openapi/v2.json get /browsers Get paginated list of browser sessions with optional status filtering. List responses intentionally omit per-session presigned recording URLs (`recording_url` is always `null` here). Each recording URL requires a synchronous boto3 SigV4 signing call (plus an S3 HEAD) that holds the GIL and blocks the event loop. With page_size up to the max this CPU-pegs the worker and starves the DB pool, cascading into pool exhaustion across the fleet. Clients should fetch the recording URL on demand via `GET /api/v2/browsers/{id}`, which signs exactly one URL for a single session. Mirrors the v3 sessions list fix (ENG-4904, PR #4621). # Update Browser Session Source: https://docs.browser-use.com/cloud/api-v2/browsers/update-browser-session /cloud/openapi/v2.json patch /browsers/{session_id} Stop a browser session. **Refund:** When you stop a session, unused time is automatically refunded. If the session ran for less than 1 hour, you'll receive a proportional refund. Billing is ceil to the nearest minute (minimum 1 minute). # Agent Session Upload File Presigned Url Source: https://docs.browser-use.com/cloud/api-v2/files/agent-session-upload-file-presigned-url /cloud/openapi/v2.json post /files/sessions/{session_id}/presigned-url Generate a secure presigned URL for uploading files to an agent session. # Browser Session Upload File Presigned Url Source: https://docs.browser-use.com/cloud/api-v2/files/browser-session-upload-file-presigned-url /cloud/openapi/v2.json post /files/browsers/{session_id}/presigned-url Generate a secure presigned URL for uploading files to a browser session. # Get Task Output File Presigned Url Source: https://docs.browser-use.com/cloud/api-v2/files/get-task-output-file-presigned-url /cloud/openapi/v2.json get /files/tasks/{task_id}/output-files/{file_id} Get secure download URL for an output file generated by the AI agent. # Create Profile Source: https://docs.browser-use.com/cloud/api-v2/profiles/create-profile /cloud/openapi/v2.json post /profiles Profiles allow you to preserve the state of the browser between tasks. They are most commonly used to allow users to preserve the log-in state in the agent between tasks. You'd normally create one profile per user and then use it for all their tasks. You can set a `user_id` when creating a profile to associate it with a user in your system. This allows you to later search for the profile using the GET /profiles endpoint with a query parameter. # Delete Browser Profile Source: https://docs.browser-use.com/cloud/api-v2/profiles/delete-browser-profile /cloud/openapi/v2.json delete /profiles/{profile_id} Permanently delete a browser profile and its configuration. # Get Profile Source: https://docs.browser-use.com/cloud/api-v2/profiles/get-profile /cloud/openapi/v2.json get /profiles/{profile_id} Get profile details. # List Profiles Source: https://docs.browser-use.com/cloud/api-v2/profiles/list-profiles /cloud/openapi/v2.json get /profiles Get paginated list of profiles. Use the `query` parameter to search profiles by name or user_id. This is useful when you have many profiles and need to find a specific user. Example: If you set `user_id` to your internal user identifier when creating profiles, you can later search for that user by passing their identifier as the `query` parameter. # Update Profile Source: https://docs.browser-use.com/cloud/api-v2/profiles/update-profile /cloud/openapi/v2.json patch /profiles/{profile_id} Update a browser profile's information. # Create Session Source: https://docs.browser-use.com/cloud/api-v2/sessions/create-session /cloud/openapi/v2.json post /sessions Create a new session with a new task. # Create Session Public Share Source: https://docs.browser-use.com/cloud/api-v2/sessions/create-session-public-share /cloud/openapi/v2.json post /sessions/{session_id}/public-share Create or return existing public share for a session. # Delete Session Source: https://docs.browser-use.com/cloud/api-v2/sessions/delete-session /cloud/openapi/v2.json delete /sessions/{session_id} Delete a session with all its tasks. # Delete Session Public Share Source: https://docs.browser-use.com/cloud/api-v2/sessions/delete-session-public-share /cloud/openapi/v2.json delete /sessions/{session_id}/public-share Remove public share for a session. # Get Session Source: https://docs.browser-use.com/cloud/api-v2/sessions/get-session /cloud/openapi/v2.json get /sessions/{session_id} Get detailed session information including status, URLs, and task details. # Get Session Public Share Source: https://docs.browser-use.com/cloud/api-v2/sessions/get-session-public-share /cloud/openapi/v2.json get /sessions/{session_id}/public-share Get public share information including URL and usage statistics. # List Sessions Source: https://docs.browser-use.com/cloud/api-v2/sessions/list-sessions /cloud/openapi/v2.json get /sessions Get paginated list of AI agent sessions with optional status filtering. # Purge Session Source: https://docs.browser-use.com/cloud/api-v2/sessions/purge-session /cloud/openapi/v2.json post /sessions/{session_id}/purge Immediately purge all data for a session (ZDR projects only). Redacts PII from database records and deletes all S3 objects (screenshots, output files, uploaded files, logs, history, downloads, agent state) for the given session. This is the same cleanup the ZDR cron performs, but on-demand and scoped to a single session with no grace period. # Update Session Source: https://docs.browser-use.com/cloud/api-v2/sessions/update-session /cloud/openapi/v2.json patch /sessions/{session_id} Stop a session and all its running tasks. # Clone Skill Source: https://docs.browser-use.com/cloud/api-v2/skills-marketplace/clone-skill /cloud/openapi/v2.json post /marketplace/skills/{skill_id}/clone Clone a public marketplace skill to the user's project. # Execute Skill Source: https://docs.browser-use.com/cloud/api-v2/skills-marketplace/execute-skill /cloud/openapi/v2.json post /marketplace/skills/{skill_id}/execute Execute a skill with the provided parameters. # Get Skill Source: https://docs.browser-use.com/cloud/api-v2/skills-marketplace/get-skill /cloud/openapi/v2.json get /marketplace/skills/{skill_slug} Get details of a specific public skill from the marketplace. # List Skills Source: https://docs.browser-use.com/cloud/api-v2/skills-marketplace/list-skills /cloud/openapi/v2.json get /marketplace/skills List all public skills available in the marketplace with optional filtering. # Cancel Generation Source: https://docs.browser-use.com/cloud/api-v2/skills/cancel-generation /cloud/openapi/v2.json post /skills/{skill_id}/cancel Cancel the current in-progress generation for a skill. # Create Skill Source: https://docs.browser-use.com/cloud/api-v2/skills/create-skill /cloud/openapi/v2.json post /skills Create a new skill via automated generation. # Delete Skill Source: https://docs.browser-use.com/cloud/api-v2/skills/delete-skill /cloud/openapi/v2.json delete /skills/{skill_id} Delete a skill owned by the project. # Execute Skill Source: https://docs.browser-use.com/cloud/api-v2/skills/execute-skill /cloud/openapi/v2.json post /skills/{skill_id}/execute Execute a skill with the provided parameters. # Get Skill Source: https://docs.browser-use.com/cloud/api-v2/skills/get-skill /cloud/openapi/v2.json get /skills/{skill_id} Get details of a specific skill owned by the project. # Get Skill Execution Output Source: https://docs.browser-use.com/cloud/api-v2/skills/get-skill-execution-output /cloud/openapi/v2.json get /skills/{skill_id}/executions/{execution_id}/output Get presigned URL for downloading skill execution output. # List Skill Executions Source: https://docs.browser-use.com/cloud/api-v2/skills/list-skill-executions /cloud/openapi/v2.json get /skills/{skill_id}/executions List executions for a specific skill. # List Skills Source: https://docs.browser-use.com/cloud/api-v2/skills/list-skills /cloud/openapi/v2.json get /skills List all skills owned by the authenticated project with optional filtering. # Refine Skill Source: https://docs.browser-use.com/cloud/api-v2/skills/refine-skill /cloud/openapi/v2.json post /skills/{skill_id}/refine Refine a skill based on feedback. # Rollback Skill Source: https://docs.browser-use.com/cloud/api-v2/skills/rollback-skill /cloud/openapi/v2.json post /skills/{skill_id}/rollback Rollback to the previous version (cannot be undone). # Update Skill Source: https://docs.browser-use.com/cloud/api-v2/skills/update-skill /cloud/openapi/v2.json patch /skills/{skill_id} Update skill metadata (name, description, enabled, etc.). # Create Task Source: https://docs.browser-use.com/cloud/api-v2/tasks/create-task /cloud/openapi/v2.json post /tasks Create and start a new task. You can either: 1. Start a new task without a sessionId (auto-creates a session with US proxy by default). Note: Tasks without a sessionId are one-off tasks that automatically close the session upon completion (keep_alive=false). Use sessionSettings to configure the auto-created session (e.g. proxyCountryCode, profileId, screen dimensions). 2. Start a new task in an existing session (reuse for follow-up tasks or custom configuration) Note: Without sessionSettings, a US proxy is enabled by default. Providing sessionSettings overrides defaults — proxy is only enabled if proxyCountryCode is set. For full control over session configuration (e.g. keep_alive), create a session first via POST /sessions with your desired settings, then pass that sessionId when creating tasks. # Get Task Source: https://docs.browser-use.com/cloud/api-v2/tasks/get-task /cloud/openapi/v2.json get /tasks/{task_id} Get detailed task information including status, progress, steps, and file outputs. # Get Task Logs Source: https://docs.browser-use.com/cloud/api-v2/tasks/get-task-logs /cloud/openapi/v2.json get /tasks/{task_id}/logs Get secure download URL for task execution logs with step-by-step details. # Get Task Status Source: https://docs.browser-use.com/cloud/api-v2/tasks/get-task-status /cloud/openapi/v2.json get /tasks/{task_id}/status Lightweight endpoint optimized for polling task status. Returns only the task status, output, and cost without loading steps, files, or session details. Use this endpoint for efficient polling instead of GET /tasks/{task_id}. Recommended polling pattern: 1. POST /tasks to create a task 2. Poll GET /tasks/{task_id}/status until status is 'finished' or 'stopped' 3. GET /tasks/{task_id} once at the end for full details including steps # List Tasks Source: https://docs.browser-use.com/cloud/api-v2/tasks/list-tasks /cloud/openapi/v2.json get /tasks Get paginated list of AI agent tasks with optional filtering by session and status. # Update Task Source: https://docs.browser-use.com/cloud/api-v2/tasks/update-task /cloud/openapi/v2.json patch /tasks/{task_id} Control task execution with stop, pause, resume, or stop task and session actions. # Get Account Billing Source: https://docs.browser-use.com/cloud/api-v3/billing/get-account-billing /cloud/openapi/v3.json get /billing/account Get authenticated account information including credit balance and account details. # Create Browser Session Source: https://docs.browser-use.com/cloud/api-v3/browsers/create-browser-session /cloud/openapi/v3.json post /browsers Create a new browser session. **Pricing:** Browser sessions are charged at $0.02/hour for all users. The full rate is charged upfront when the session starts. When you stop the session, any unused time is automatically refunded proportionally. Billing is rounded up to the minute (minimum 1 minute). For example, if you stop a session after 30 minutes, you'll be refunded half the charged amount. **Session Limits:** - All users: Up to 4 hours per session # Get Browser Session Source: https://docs.browser-use.com/cloud/api-v3/browsers/get-browser-session /cloud/openapi/v3.json get /browsers/{session_id} Get detailed browser session information including status and URLs. # List Browser Session Downloads Source: https://docs.browser-use.com/cloud/api-v3/browsers/list-browser-session-downloads /cloud/openapi/v3.json get /browsers/{session_id}/downloads List files the browser downloaded to S3 during the session. Pass ``includeUrls=true`` to receive presigned download URLs (15 min expiry) inline. Files are stored at ``downloads/projects/{project_id}/sessions/{session_id}/`` in the private bucket. # List Browser Sessions Source: https://docs.browser-use.com/cloud/api-v3/browsers/list-browser-sessions /cloud/openapi/v3.json get /browsers Get paginated list of browser sessions with optional status filtering. List responses intentionally omit per-session presigned recording URLs (`recording_url` is always `null` here). Each recording URL requires a synchronous boto3 SigV4 signing call (plus an S3 HEAD) that holds the GIL and blocks the event loop. With page_size up to the max this CPU-pegs the worker and starves the DB pool, cascading into pool exhaustion across the fleet. Clients should fetch the recording URL on demand via `GET /api/v2/browsers/{id}`, which signs exactly one URL for a single session. Mirrors the v3 sessions list fix (ENG-4904, PR #4621). # Update Browser Session Source: https://docs.browser-use.com/cloud/api-v3/browsers/update-browser-session /cloud/openapi/v3.json patch /browsers/{session_id} Stop a browser session. **Refund:** When you stop a session, unused time is automatically refunded. If the session ran for less than 1 hour, you'll receive a proportional refund. Billing is ceil to the nearest minute (minimum 1 minute). # Create Profile Source: https://docs.browser-use.com/cloud/api-v3/profiles/create-profile /cloud/openapi/v3.json post /profiles Profiles allow you to preserve the state of the browser between tasks. They are most commonly used to allow users to preserve the log-in state in the agent between tasks. You'd normally create one profile per user and then use it for all their tasks. You can set a `user_id` when creating a profile to associate it with a user in your system. This allows you to later search for the profile using the GET /profiles endpoint with a query parameter. # Delete Browser Profile Source: https://docs.browser-use.com/cloud/api-v3/profiles/delete-browser-profile /cloud/openapi/v3.json delete /profiles/{profile_id} Permanently delete a browser profile and its configuration. # Get Profile Source: https://docs.browser-use.com/cloud/api-v3/profiles/get-profile /cloud/openapi/v3.json get /profiles/{profile_id} Get profile details. # List Profiles Source: https://docs.browser-use.com/cloud/api-v3/profiles/list-profiles /cloud/openapi/v3.json get /profiles Get paginated list of profiles. Use the `query` parameter to search profiles by name or user_id. This is useful when you have many profiles and need to find a specific user. Example: If you set `user_id` to your internal user identifier when creating profiles, you can later search for that user by passing their identifier as the `query` parameter. # Update Profile Source: https://docs.browser-use.com/cloud/api-v3/profiles/update-profile /cloud/openapi/v3.json patch /profiles/{profile_id} Update a browser profile's information. # Create Session Source: https://docs.browser-use.com/cloud/api-v3/sessions/create-session /cloud/openapi/v3.json post /sessions Create a session and/or dispatch a task. - Without session_id, without task: creates a new idle session (e.g. for file uploads). - Without session_id, with task: creates a new session and dispatches the task. - With session_id, with task: dispatches the task to an existing idle session. - With session_id, without task: 422 — task is required when targeting an existing session. If keep_alive is false (default), the session auto-stops when the task finishes. If keep_alive is true, the session stays idle after the task, ready for follow-ups. # Delete Session Source: https://docs.browser-use.com/cloud/api-v3/sessions/delete-session /cloud/openapi/v3.json delete /sessions/{session_id} Soft-delete a session. Stops the sandbox first if still running. # Get Session Source: https://docs.browser-use.com/cloud/api-v3/sessions/get-session /cloud/openapi/v3.json get /sessions/{session_id} Get session details. Use this to poll for task completion and output. # List Session Messages Source: https://docs.browser-use.com/cloud/api-v3/sessions/list-session-messages /cloud/openapi/v3.json get /sessions/{session_id}/messages Return messages for a session with cursor-based pagination (chronological order). # List Sessions Source: https://docs.browser-use.com/cloud/api-v3/sessions/list-sessions /cloud/openapi/v3.json get /sessions List sessions for the authenticated project. List responses intentionally omit per-session presigned URLs (`screenshot_url`, `recording_urls`). Each presign is a synchronous boto3 SigV4 signing call that holds the GIL and blocks the event loop. With page_size up to 100 this CPU-pegs the worker and starves the DB pool, cascading into pool exhaustion across the fleet. Clients should fetch these URLs on demand via `GET /api/v3/sessions/{id}`, which signs exactly one screenshot URL plus any recording URLs for a single session. # Stop Session Source: https://docs.browser-use.com/cloud/api-v3/sessions/stop-session /cloud/openapi/v3.json post /sessions/{session_id}/stop Stop a session or the running task. - strategy=session (default): destroy sandbox entirely, session → stopped. - strategy=task: stop the running query, session stays alive (→ idle). # Create Workspace Source: https://docs.browser-use.com/cloud/api-v3/workspaces/create-workspace /cloud/openapi/v3.json post /workspaces Create a new workspace for persistent shared file storage across sessions. # Delete Workspace Source: https://docs.browser-use.com/cloud/api-v3/workspaces/delete-workspace /cloud/openapi/v3.json delete /workspaces/{workspace_id} Delete a workspace and its S3 data. # Delete Workspace File Source: https://docs.browser-use.com/cloud/api-v3/workspaces/delete-workspace-file /cloud/openapi/v3.json delete /workspaces/{workspace_id}/files Delete a single file from a workspace. # Get Workspace Source: https://docs.browser-use.com/cloud/api-v3/workspaces/get-workspace /cloud/openapi/v3.json get /workspaces/{workspace_id} Get workspace details. # Get Workspace Size Source: https://docs.browser-use.com/cloud/api-v3/workspaces/get-workspace-size /cloud/openapi/v3.json get /workspaces/{workspace_id}/size Get current storage usage for a workspace. # List Workspace Files Source: https://docs.browser-use.com/cloud/api-v3/workspaces/list-workspace-files /cloud/openapi/v3.json get /workspaces/{workspace_id}/files List files in a workspace's S3 prefix. # List Workspaces Source: https://docs.browser-use.com/cloud/api-v3/workspaces/list-workspaces /cloud/openapi/v3.json get /workspaces Get paginated list of workspaces. # Update Workspace Source: https://docs.browser-use.com/cloud/api-v3/workspaces/update-workspace /cloud/openapi/v3.json patch /workspaces/{workspace_id} Update a workspace's name. # Upload Workspace Files Source: https://docs.browser-use.com/cloud/api-v3/workspaces/upload-workspace-files /cloud/openapi/v3.json post /workspaces/{workspace_id}/files/upload Get presigned PUT URLs for uploading files to a workspace. # X402 Balance Source: https://docs.browser-use.com/cloud/api-v3/x402/x402-balance /cloud/openapi/v3.json post /x402/balance Read a wallet-derived project's credit balance auth by off-chain wallet signature # API Reference Source: https://docs.browser-use.com/cloud/api-v4-overview Authenticate and start using the Browser Use API v4 — the current REST API for long-horizon agents. ## Authentication All requests require an API key in the `X-Browser-Use-API-Key` header: ``` X-Browser-Use-API-Key: bu_your_key_here ``` Get a key at [cloud.browser-use.com/settings](https://cloud.browser-use.com/settings?tab=api-keys\&new=1). Keys start with `bu_`. ## Base URL ``` https://api.browser-use.com/api/v4 ``` See [Thinking levels](/cloud/agent/thinking-levels) for V4 `modelParams`, provider-native reasoning values, and BYOK behavior. ## The core loop Create a run, poll its status until terminal, then fetch the full result. `status` is a cheap indexed lookup — poll it, not the full run. ```bash Create a run theme={null} curl -X POST https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: bu_your_key_here" \ -H "Content-Type: application/json" \ -d '{"task": "Find the top 3 trending repos on GitHub today"}' ``` ```bash Poll status until completed | failed | cancelled (replace RUN_ID) theme={null} curl https://api.browser-use.com/api/v4/runs/RUN_ID/status \ -H "X-Browser-Use-API-Key: bu_your_key_here" ``` ```bash Fetch the full run once it's terminal theme={null} curl https://api.browser-use.com/api/v4/runs/RUN_ID \ -H "X-Browser-Use-API-Key: bu_your_key_here" ``` ## Sessions and follow-ups A run belongs to a session (a conversation). Send a follow-up message to a session's queue — it runs as the next turn, or immediately with `interrupt: true`: ```bash Queue a follow-up (replace SESSION_ID) theme={null} curl -X POST https://api.browser-use.com/api/v4/sessions/SESSION_ID/queue \ -H "X-Browser-Use-API-Key: bu_your_key_here" \ -H "Content-Type: application/json" \ -d '{"text": "Now open the top result", "interrupt": false}' ``` ## Direct browser control Use the browser endpoints when your code will control Chrome over CDP instead of asking a hosted agent to do the work: 1. `POST /browsers` creates a browser and returns its `id` and `cdpUrl`. 2. Use `cdpUrl` with a local Browser Use agent (`Browser(cdp_url=...)`), Browser Use CLI (`BU_CDP_URL`), Playwright, Puppeteer, or Selenium. 3. `PATCH /browsers/{id}` with `{"action":"stop"}` stops the browser. See [remote Browser Use](/open-source/customize/browser/remote), [Browser Use CLI](/open-source/browser-use-cli), or [Playwright, Puppeteer, Selenium](/cloud/browser/playwright-puppeteer-selenium) for complete connection examples. ## SDKs The [Cloud SDK quick start](/cloud/agent/quickstart) wraps this loop — `runs.create()` then `runs.waitForCompletion()` / `runs.wait_for_completion()` — for TypeScript and Python. # Create Browser Session Source: https://docs.browser-use.com/cloud/api-v4/browsers/create-browser-session /cloud/openapi/v4.json post /browsers Create a new browser session. **Pricing:** Browser sessions are charged at $0.02/hour for all users. The full rate is charged upfront when the session starts. When you stop the session, any unused time is automatically refunded proportionally. Billing is rounded up to the minute (minimum 1 minute). For example, if you stop a session after 30 minutes, you'll be refunded half the charged amount. **Session Limits:** - All users: Up to 4 hours per session # Get Browser Session Source: https://docs.browser-use.com/cloud/api-v4/browsers/get-browser-session /cloud/openapi/v4.json get /browsers/{session_id} Get detailed browser session information including status and URLs. # List Browser Session Downloads Source: https://docs.browser-use.com/cloud/api-v4/browsers/list-browser-session-downloads /cloud/openapi/v4.json get /browsers/{session_id}/downloads List files the browser downloaded to S3 during the session. Pass ``includeUrls=true`` to receive presigned download URLs (15 min expiry) inline. Files are stored at ``downloads/projects/{project_id}/sessions/{session_id}/`` in the private bucket. # List Browser Sessions Source: https://docs.browser-use.com/cloud/api-v4/browsers/list-browser-sessions /cloud/openapi/v4.json get /browsers Get paginated list of browser sessions with optional status filtering. List responses intentionally omit per-session presigned recording URLs (`recording_url` is always `null` here). Each recording URL requires a synchronous boto3 SigV4 signing call (plus an S3 HEAD) that holds the GIL and blocks the event loop. With page_size up to the max this CPU-pegs the worker and starves the DB pool, cascading into pool exhaustion across the fleet. Clients should fetch the recording URL on demand via `GET /api/v2/browsers/{id}`, which signs exactly one URL for a single session. Mirrors the v3 sessions list fix (ENG-4904, PR #4621). # Update Browser Session Source: https://docs.browser-use.com/cloud/api-v4/browsers/update-browser-session /cloud/openapi/v4.json patch /browsers/{session_id} Stop a browser session. **Refund:** When you stop a session, unused time is automatically refunded. If the session ran for less than 1 hour, you'll receive a proportional refund. Billing is ceil to the nearest minute (minimum 1 minute). # Get Shared Session Source: https://docs.browser-use.com/cloud/api-v4/get-shared-session /cloud/openapi/v4.json get /share/{share_token} Public transcript of a shared session (sensitive keys scrubbed). Events come back in bounded pages: walk them by passing the previous response's `nextAfter` back as `since` until `hasMore` is false (the wire is camelCase — the Python fields are `next_after`/`has_more`). Run metadata repeats on every page, so only `events` continues across pages. Paging exists because this is an unauthenticated read and the whole transcript in one response could occupy the event loop for seconds, starving the shared Redis client's auth and rate-limit calls of their 50 ms deadline and silently failing them open. Counts a view per full fetch. `since` makes this a live-tail poll: the share page repeats the request every few seconds while a run is unfinished, and each poll must stay cheap and must NOT inflate the view counter — a viewer idling on a running session would otherwise register a view every 10 seconds (the counter's throttle window). Later pages of an initial load carry a cursor too, so one load counts one view, not one per page. # Get Shared Session Recording Source: https://docs.browser-use.com/cloud/api-v4/get-shared-session-recording /cloud/openapi/v4.json get /share/{share_token}/recording Presigned URL for the shared session's browser recording (mp4). Recording ONLY — the live-view URL is an interactive browser and is never exposed through a share. Null until the session's browser stops and the upload lands (same lag as GET /runs/{run_id}/recording). V4 keeps one browser across follow-ups, so the newest run with a browser session is the session's recording. # Authorize Integration Source: https://docs.browser-use.com/cloud/api-v4/integrations/authorize-integration /cloud/openapi/v4.json post /integrations/{provider}/authorize Get the OAuth redirect URL for connecting an integration. The client opens the returned URL in a web auth session and polls `/integrations/{provider}/status` once the flow lands back on the callback path. The URL is Composio-hosted; no redirect URI is registered per client. # Disconnect Integration Source: https://docs.browser-use.com/cloud/api-v4/integrations/disconnect-integration /cloud/openapi/v4.json delete /integrations/{provider} Disconnect an integration for this project. Deliberately NOT gated like `authorize` above: a restricted project must still be able to tear down a connection it already has. # Get Connection Status Source: https://docs.browser-use.com/cloud/api-v4/integrations/get-connection-status /cloud/openapi/v4.json get /integrations/{provider}/status Check whether this project has connected a specific provider. Poll this after opening the authorize URL to learn when OAuth completed. # List Categories Source: https://docs.browser-use.com/cloud/api-v4/integrations/list-categories /cloud/openapi/v4.json get /integrations/categories Get all integration categories, for filtering the list above. # List Integrations Source: https://docs.browser-use.com/cloud/api-v4/integrations/list-integrations /cloud/openapi/v4.json get /integrations List available integrations with this project's connection status. `is_connected` is resolved per integration, so a client can render the whole catalogue and the connected subset from this one call. `connected_only` is what a "my integrations" view reads — including on a restricted project, which cannot connect anything but can still remove what it already has. # List Shared Session Files Source: https://docs.browser-use.com/cloud/api-v4/list-shared-session-files /cloud/openapi/v4.json get /share/{share_token}/files Public listing of a shared session's workspace files. Same shape as the authenticated workspace-files endpoint so the share page can reuse the same components. Scope is FORCED under `outputs/`: the worker contract promises the user that only outputs/ files are deliverables, so working files the agent left at the workspace root (notes, scripts, .env, uploads/) must never become public through a share link. # Create Profile Source: https://docs.browser-use.com/cloud/api-v4/profiles/create-profile /cloud/openapi/v4.json post /profiles Profiles allow you to preserve the state of the browser between tasks. They are most commonly used to allow users to preserve the log-in state in the agent between tasks. You'd normally create one profile per user and then use it for all their tasks. You can set a `user_id` when creating a profile to associate it with a user in your system. This allows you to later search for the profile using the GET /profiles endpoint with a query parameter. # Delete Browser Profile Source: https://docs.browser-use.com/cloud/api-v4/profiles/delete-browser-profile /cloud/openapi/v4.json delete /profiles/{profile_id} Permanently delete a browser profile and its configuration. # Get Profile Source: https://docs.browser-use.com/cloud/api-v4/profiles/get-profile /cloud/openapi/v4.json get /profiles/{profile_id} Get profile details. # List Profiles Source: https://docs.browser-use.com/cloud/api-v4/profiles/list-profiles /cloud/openapi/v4.json get /profiles Get paginated list of profiles. Use the `query` parameter to search profiles by name or user_id. This is useful when you have many profiles and need to find a specific user. Example: If you set `user_id` to your internal user identifier when creating profiles, you can later search for that user by passing their identifier as the `query` parameter. # Update Profile Source: https://docs.browser-use.com/cloud/api-v4/profiles/update-profile /cloud/openapi/v4.json patch /profiles/{profile_id} Update a browser profile's information. # Cancel Run Source: https://docs.browser-use.com/cloud/api-v4/runs/cancel-run /cloud/openapi/v4.json post /runs/{run_id}/cancel Cancel an in-flight run. Idempotent: a run already in a terminal state (completed / failed / cancelled) is returned as-is. Once cancelled, the gateway refuses every subsequent `chat_completions` call from the worker — so the project cannot be billed further regardless of whether the worker subprocess notices the cancel itself. The worker keeps running for at most one more bcode step (consuming no LLM tokens because CP 409s them) and then exits when bcode realizes the next tool call has no model behind it. # Create Run Source: https://docs.browser-use.com/cloud/api-v4/runs/create-run /cloud/openapi/v4.json post /runs # Get Run Source: https://docs.browser-use.com/cloud/api-v4/runs/get-run /cloud/openapi/v4.json get /runs/{run_id} # Get Run Events Source: https://docs.browser-use.com/cloud/api-v4/runs/get-run-events /cloud/openapi/v4.json get /runs/{run_id}/events Paginated event read. `after` is the event id cursor — clients doing periodic polls pass back the highest id they've seen to get only the delta. Default 0 returns from the start. # Get Run Status Source: https://docs.browser-use.com/cloud/api-v4/runs/get-run-status /cloud/openapi/v4.json get /runs/{run_id}/status Minimal status poll (see RunStatusResponse). POLL rate bucket: selects ONLY the status/project columns — a 2s poll never drags the task/result text (TOAST reads) that GET /runs/{id} pays. Poll until terminal, then fetch the full summary once. # List Run Attachments Source: https://docs.browser-use.com/cloud/api-v4/runs/list-run-attachments /cloud/openapi/v4.json get /runs/{run_id}/attachments The upload files attached to a run, for rendering attachment chips. Ordered to match the run's attached_file_ids. # List Runs Source: https://docs.browser-use.com/cloud/api-v4/runs/list-runs /cloud/openapi/v4.json get /runs # Cancel Queued Message Source: https://docs.browser-use.com/cloud/api-v4/sessions/cancel-queued-message /cloud/openapi/v4.json delete /sessions/{session_id}/queue/{message_id} Remove a still-pending queued message; 409 once the drain has claimed it. # Create Session Share Source: https://docs.browser-use.com/cloud/api-v4/sessions/create-session-share /cloud/openapi/v4.json post /sessions/{session_id}/share Enable sharing: reactivates the session's existing share (stable token, so a link that was toggled off keeps working when re-enabled) or creates one on first use. # Delete Session Source: https://docs.browser-use.com/cloud/api-v4/sessions/delete-session /cloud/openapi/v4.json delete /sessions/{session_id} Delete a session: it disappears from the session lists and can no longer be opened or continued. Soft delete (mirrors V2's `is_archived`): the runs, events and usage records stay in place so billing and analytics keep reconciling, and ZDR purge is the only thing that ever destroys V4 content. Idempotent — deleting twice is a no-op 204. An in-flight run is cancelled first, and pending queued messages are cancelled too; otherwise the drain would dispatch a follow-up run whose `session_archived_at` is NULL and resurrect the session. Cancel is forwarded to CP before anything is written (and outside the queue lock, per the enqueue path's ordering), so a CP failure (502) leaves the session visible rather than hiding a run that is still burning credits. # Get Session Source: https://docs.browser-use.com/cloud/api-v4/sessions/get-session /cloud/openapi/v4.json get /sessions/{session_id} Session detail — the same slim shape as one GET /sessions row (its latest run). Doubles as the busy/idle poll for a conversation (POLL rate bucket): slim columns only, indexed public_session_id lookup, never the heavy result/token fields. # Get Session Feedback Source: https://docs.browser-use.com/cloud/api-v4/sessions/get-session-feedback /cloud/openapi/v4.json get /sessions/{session_id}/feedback Every vote across the session in one query — the run view calls this once per session on load. A session the caller doesn't own returns an empty list (not 404), matching what an unshared session with no votes returns. Reads the PRIMARY, not the replica: this hydrates the thumbs the user may have set seconds ago, and replica lag would render a just-cast vote as silently lost. # Get Session Queue Message Source: https://docs.browser-use.com/cloud/api-v4/sessions/get-session-queue-message /cloud/openapi/v4.json get /sessions/{session_id}/queue/{message_id} Read one queued message including terminal handoff states. # Get Session Share Source: https://docs.browser-use.com/cloud/api-v4/sessions/get-session-share /cloud/openapi/v4.json get /sessions/{session_id}/share The session's share link (active or not); null if never shared. # List Session Queue Source: https://docs.browser-use.com/cloud/api-v4/sessions/list-session-queue /cloud/openapi/v4.json get /sessions/{session_id}/queue List open messages, recently delivered queued messages, and steering cutoffs. Includes live dispatch claims so clients can restore an accepted queued message after a refresh without briefly losing its waiting state. Consumed QUEUE rows are returned too, capped at the most recent _DELIVERED_QUEUE_HISTORY, plus up to _AGENCY_DELIVERED_QUEUE_HISTORY compact Agency choice and generation receipts so inbox state survives refresh. A message absorbed mid-run is announced by a `queued_input_injected` event carrying only its input id, so the chat has no other source for the text to render. The cap bounds this response because clients poll it while a run is active; a session with more delivered follow-ups than the cap renders its oldest injected bubbles without text. The latest consumed interrupt bridges the primary-write/read-replica gap before its replacement run appears; clients discard it once that run is visible. Compact per-source cutoffs preserve every historical interrupted transcript boundary without returning every historical steering message and its attachments. # List Sessions Source: https://docs.browser-use.com/cloud/api-v4/sessions/list-sessions /cloud/openapi/v4.json get /sessions List the project's V4 sessions — one row per session (its most recent run), most-recent first. A session is a conversation: follow-up runs reuse the same public_session_id, so GET /api/v4/runs would list one row per RUN. This collapses to one row per SESSION directly in Postgres with DISTINCT ON, so a chatty session with 100 runs is still a single row (no scan amplification), and selects only the slim columns the session list needs — never the heavy per-run text/token/cost fields. Keyset-paginated on the latest run's (created_at, id): pass `next_cursor` back as `cursor` to page. Offset/COUNT was dropped — it doesn't scale on projects with many sessions. # Purge Session Source: https://docs.browser-use.com/cloud/api-v4/sessions/purge-session /cloud/openapi/v4.json post /sessions/{session_id}/purge Immediately purge all data for a V4 session (ZDR projects only). Redacts DB records and deletes S3 objects (bcode state, workspace files, recordings, downloads, staging uploads) for every run in the session. Same cleanup the ZDR cron performs, but on-demand and with no grace period. V4 analog of the V2 POST /sessions/{id}/purge. Surfaces shared with a still-live sibling run are deferred by the same guards the cron uses. # Queue Session Message Source: https://docs.browser-use.com/cloud/api-v4/sessions/queue-session-message /cloud/openapi/v4.json post /sessions/{session_id}/queue Send a message to a session. If the session is busy it waits on the queue and runs as the next turn; if idle it drains immediately. `interrupt=true` cancels the session's active run so this message takes effect now instead of waiting for the current turn (no-op when the session is idle). # Set Session Feedback Source: https://docs.browser-use.com/cloud/api-v4/sessions/set-session-feedback /cloud/openapi/v4.json post /sessions/{session_id}/feedback Upsert one chat item's thumbs vote; `feedback_type: null` clears it. # Update Session Source: https://docs.browser-use.com/cloud/api-v4/sessions/update-session /cloud/openapi/v4.json patch /sessions/{session_id} Rename a session. `session_title` is denormalized onto every run in the session (the session list reads the latest run), so a rename writes them all in one UPDATE. Null clears the name and the UI falls back to the opening task — it does NOT re-run title generation, which only fills rows where the title IS NULL and so would silently overwrite the clear. # Update Session Share Source: https://docs.browser-use.com/cloud/api-v4/sessions/update-session-share /cloud/openapi/v4.json put /sessions/{session_id}/share Toggle the session's share link on/off. # Get Stripe Link Status Source: https://docs.browser-use.com/cloud/api-v4/wallets/get-stripe-link-status /cloud/openapi/v4.json get /stripe-link The project's active Stripe Link wallet connection, if any. Read-only: connecting happens in the cloud UI. A run opts into paying with it by passing `connection_id` as RunCreateRequest.stripe_link_connection_id, which is validated again there against this same project. # List Agentcard Wallets Source: https://docs.browser-use.com/cloud/api-v4/wallets/list-agentcard-wallets /cloud/openapi/v4.json get /agentcard/wallets The project's active AgentCard wallets, newest first. Read-only: funding is admin-only during the beta. The UI uses this to offer a wallet on the composer, which is how a run opts into spending — the id then rides RunCreateRequest.agentcard_wallet_id and is validated again there against this same project. # Create Workspace Source: https://docs.browser-use.com/cloud/api-v4/workspaces/create-workspace /cloud/openapi/v4.json post /workspaces Mint an empty workspace. Lets callers upload input files BEFORE the first run exists; pass the id as workspaceId on POST /runs. # Delete Workspace Source: https://docs.browser-use.com/cloud/api-v4/workspaces/delete-workspace /cloud/openapi/v4.json delete /workspaces/{workspace_id} Archive a workspace. Idempotent for missing, archived, and foreign ids. # Delete Workspace File Source: https://docs.browser-use.com/cloud/api-v4/workspaces/delete-workspace-file /cloud/openapi/v4.json delete /workspaces/{workspace_id}/files Delete one exact path from a workspace and remove matching upload metadata. # Get Workspace Source: https://docs.browser-use.com/cloud/api-v4/workspaces/get-workspace /cloud/openapi/v4.json get /workspaces/{workspace_id} # Get Workspace Size Source: https://docs.browser-use.com/cloud/api-v4/workspaces/get-workspace-size /cloud/openapi/v4.json get /workspaces/{workspace_id}/size Return current storage usage and the plan-specific workspace quota. # List Workspace Files Source: https://docs.browser-use.com/cloud/api-v4/workspaces/list-workspace-files /cloud/openapi/v4.json get /workspaces/{workspace_id}/files List files the agent has produced for this workspace, paginated. # Update Workspace Source: https://docs.browser-use.com/cloud/api-v4/workspaces/update-workspace /cloud/openapi/v4.json patch /workspaces/{workspace_id} Rename a workspace. Explicit null clears the name; omission is a no-op. # Upload Workspace Files Source: https://docs.browser-use.com/cloud/api-v4/workspaces/upload-workspace-files /cloud/openapi/v4.json post /workspaces/{workspace_id}/files/upload Presigned PUT URLs for input files, placed under ``uploads/`` in the workspace. The agent pulls them in at the start of every run. URLs are pinned to the declared Content-Type and exact Content-Length. # CAPTCHA handling Source: https://docs.browser-use.com/cloud/browser/captcha-handling Cloud browsers solve supported CAPTCHA challenges automatically. Automatic CAPTCHA solving is enabled by default for API V4 Agent runs and standalone Cloud Browser sessions. For a standalone browser created with `POST /api/v4/browsers`, set `"solveCaptchas": false` to handle challenges yourself. This standalone-browser field is not currently part of V4 run `browserSettings`. The automatic solver handles **CAPTCHAs**. Cloudflare and other bot walls are handled by the browser's stealth layer and residential proxies, not by the CAPTCHA solver. Other anti-bot pages may still be passed by the browser's stealth protections or residential proxy. That is separate from automatic CAPTCHA solver coverage. ## When a CAPTCHA appears Stop driving the page, wait about 10 seconds, then inspect it again. Do not refresh, click the challenge, or replace the browser while the solver is working. CAPTCHA solving is not guaranteed. If the challenge remains, open the [live preview](/cloud/browser/live-preview) for human control. If the whole site is blocking the browser rather than showing a challenge, try a different [proxy country](/cloud/browser/proxies). For a specific CAPTCHA or anti-bot issue, email [contact@browser-use.com](mailto:contact@browser-use.com). # Live preview & recording Source: https://docs.browser-use.com/cloud/browser/live-preview Watch an API V4 run in real time or record its browser. Wait for `browser.ready` as soon as you create the run so the URL is available while the browser is live: ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() run = client.runs.create("Find the top Hacker News story") ready = client.runs.wait_for_event(run.id, "browser.ready", interval=3) print(ready.data["live_view_url"]) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const run = await client.runs.create({ task: "Find the top Hacker News story", model: "grok-4.5", }); const ready = await client.runs.waitForEvent(run.id, "browser.ready", { interval: 3_000 }); console.log(ready.data.live_view_url); ``` These examples poll immediately, then every three seconds (Python uses seconds; TypeScript uses milliseconds). Setting the interval explicitly also reduces traffic on older SDK releases whose event default is one second. Event reads share the project's general request budget; [stagger concurrent waits](/cloud/guides/concurrency#when-you-need-events) and leave room for other calls. The helper advances the event cursor and times out after five minutes. See [run events](/cloud/agent/observability) for the full event stream and custom polling. ## Embed the live browser ```html theme={null} ``` The URL is hosted on `live.browser-use.com`. Add that origin to your Content Security Policy's `frame-src` directive when needed. Treat the URL as a credential: anyone with it can interact with the active browser. ### Read-only embeds To let users watch without clicking, typing, or scrolling the remote page, make the iframe non-interactive in your application. This works with V4 today and provides the same UI-level restriction as the V2 dashboard's view-only mode. No API request parameter is needed. Use `ready.data.live_view_url` from the V4 `browser.ready` event above. For REST integrations, read that event from [`GET /api/v4/runs/{run_id}/events`](/cloud/api-v4/runs/get-run-events). Standalone [`POST /api/v4/browsers`](/cloud/api-v4/browsers/create-browser-session) responses return the URL as `liveUrl`. ```html theme={null}
``` The preview keeps streaming. The `inert` wrapper prevents focus and keyboard input, while `pointer-events: none` blocks pointer interaction with the iframe. Use a browser that supports `inert`. To allow human takeover, remove `inert`, `tabindex="-1"`, and `pointer-events: none` when your application enables control. `ui=false` only hides the tabs and toolbar; it does not disable interaction. View-only embeds are a UI restriction, not a server-enforced permission. Anyone who opens the live URL directly or uses the underlying CDP URL can still control the browser. Keep both URLs private. ### Read-only URL option (staging) The staging viewer also supports `readOnly=true`. It blocks browser controls and page input while keeping connection and screencast Retry buttons usable: ```javascript theme={null} const previewUrl = new URL(liveViewUrl); previewUrl.searchParams.set("readOnly", "true"); previewUrl.searchParams.set("ui", "false"); // Optional: hide tabs and toolbar. iframe.src = previewUrl.toString(); ``` This option is not yet available on `live.browser-use.com`; use the iframe wrapper above in production. It is a viewer URL option, not a field in the browser or run creation request. Omitting it or setting it to `false` keeps the viewer interactive, and `/session/{id}` links preserve it when redirecting. Like the wrapper, it prevents UI input without changing CDP access permissions. ## Recording Enable recording when the run creates its browser: ```python Python theme={null} run = client.runs.create( "Test the checkout flow", browser_settings={"record": True}, ) ``` ```typescript TypeScript theme={null} const run = await client.runs.create({ task: "Test the checkout flow", model: "grok-4.5", browserSettings: { proxyCountryCode: "us", record: true }, }); ``` ```bash curl theme={null} curl https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"task":"Test checkout","browserSettings":{"record":true}}' ``` The MP4 becomes available in the Dashboard after the browser stops. API runs default to recording off, and Zero Data Retention projects never record. For standalone browsers, use `enableRecording: true` in `POST /api/v4/browsers` (`enable_recording=True` in the Python SDK). For agent-created browsers, use `browserSettings.record: true` as above. These are different request shapes. After stopping a standalone browser, poll `GET /api/v4/browsers/{id}` for `recordingUrl`. The stop response and browser-list response do not contain a ready recording URL. Stop polling when `recordingAvailable` is false; the video cannot appear for that session. If the installed SDK does not expose that field, use the REST response and the current API reference. # Playwright, Puppeteer, Selenium Source: https://docs.browser-use.com/cloud/browser/playwright-puppeteer-selenium Control a Browser Use cloud browser directly over CDP. Every browser runs in a [hardened Chromium fork](/cloud/browser/stealth) with stealth, anti-fingerprinting, and [residential proxies](/cloud/browser/proxies) enabled by default. This page is for direct browser control. To give an AI agent a goal instead, [create an API V4 run](/cloud/agent/quickstart). ## 1. Create a browser ```bash theme={null} session=$(curl -sS https://api.browser-use.com/api/v4/browsers \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"proxyCountryCode":"us"}') export BROWSER_SESSION_ID=$(echo "$session" | jq -r .id) export BROWSER_USE_CDP_URL=$(echo "$session" | jq -r .cdpUrl) ``` ## 2. Connect over CDP ### Playwright ```python Python theme={null} import os from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.connect_over_cdp(os.environ["BROWSER_USE_CDP_URL"]) page = browser.contexts[0].pages[0] page.goto("https://example.com") print(page.title()) ``` ```typescript TypeScript theme={null} import { chromium } from "playwright"; const browser = await chromium.connectOverCDP( process.env.BROWSER_USE_CDP_URL!, ); const page = browser.contexts()[0].pages()[0]; await page.goto("https://example.com"); console.log(await page.title()); ``` ### Puppeteer ```typescript theme={null} import puppeteer from "puppeteer-core"; const browser = await puppeteer.connect({ browserWSEndpoint: process.env.BROWSER_USE_CDP_URL!, }); const [page] = await browser.pages(); await page.goto("https://example.com"); console.log(await page.title()); ``` ### Selenium Selenium's `debugger_address` only supports local `host:port` connections. Use Playwright or Puppeteer for remote CDP over WebSocket. ## 3. Stop the browser ```bash theme={null} curl -X PATCH \ "https://api.browser-use.com/api/v4/browsers/$BROWSER_SESSION_ID" \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"action":"stop"}' ``` `browser.close()` and disconnecting CDP do not stop the managed browser. Call `PATCH /api/v4/browsers/{id}` with `{"action":"stop"}`. See [Create browser session](/cloud/api-v4/browsers/create-browser-session) and [Update browser session](/cloud/api-v4/browsers/update-browser-session) for every browser setting and response field. ## PDF rendering Standalone browser creation accepts `"pdfRendererEnabled": false` to disable Chrome's in-tab PDF viewer. PDFs are still saved to the browser session's download directory. This does not block PDF downloads or reduce their bytes to zero. The setting is not currently exposed in V4 agent `browserSettings`. ## Profiles and browser contexts To reuse a saved login, supply `profileId` when creating the browser and use the existing browser context shown above. Creating a fresh incognito context with `new_context()` / `newContext()` does not inherit that profile's cookies. A profile is not a guarantee that the site's login remains valid, and cookie sync does not import saved passwords or promise a persistent HTTP disk cache. See [Sync local and cloud cookies](/cloud/guides/profile-sync). # Proxies Source: https://docs.browser-use.com/cloud/browser/proxies Configure residential or custom proxies for API V4 browsers and agent runs. A US residential proxy is enabled by default. The browser's traffic passes through that residential IP before reaching the website, so the website sees the proxy's location—not your server's. A cloud browser routing its traffic through a residential proxy before reaching a website A cloud browser routing its traffic through a residential proxy before reaching a website Set `browser_settings` / `browserSettings` when you create a V4 run to choose another country: The current TypeScript SDK type requires `proxyCountryCode` whenever `browserSettings` is present. Use `"us"` to keep the default, or `null` to disable the managed proxy. ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() run = client.runs.create( "Get the iPhone 16 price on amazon.de", browser_settings={"proxyCountryCode": "de"}, ) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const run = await client.runs.create({ task: "Get the iPhone 16 price on amazon.de", model: "grok-4.5", browserSettings: { proxyCountryCode: "de" }, }); ``` ```bash curl theme={null} curl https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"task":"Get the iPhone 16 price on amazon.de", "browserSettings":{"proxyCountryCode":"de"}}' ``` ## Disable proxies Pass `null` for QA or internal sites that do not need a residential proxy: ```python Python theme={null} run = client.runs.create( "Test my staging site", browser_settings={"proxyCountryCode": None}, ) ``` ```typescript TypeScript theme={null} const run = await client.runs.create({ task: "Test my staging site", model: "grok-4.5", browserSettings: { proxyCountryCode: null }, }); ``` ## Custom proxy Custom proxies are available to all accounts, including free and pay-as-you-go accounts. No subscription is required. Normal credit, spend, and concurrency limits still apply. The request format depends on what you are creating: | REST endpoint | Custom proxy field | | ----------------------- | ----------------------------- | | `POST /api/v4/browsers` | Top-level `customProxy` | | `POST /api/v4/runs` | `browserSettings.customProxy` | `browser_settings` is the Python SDK keyword for agent runs. In REST JSON, the agent-run field is `browserSettings`. ### Standalone browsers For a browser you control with Playwright, Puppeteer, or another CDP client, put `customProxy` directly in the request body: ```bash curl theme={null} curl https://api.browser-use.com/api/v4/browsers \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customProxy": { "host": "proxy.example.com", "port": 8080, "username": "YOUR_PROXY_USERNAME", "password": "YOUR_PROXY_PASSWORD" } }' ``` Replace the proxy details with yours. Omit both `username` and `password` if your proxy does not require authentication. Do not wrap these settings in `browserSettings` or `browser_settings` on `/api/v4/browsers`. Keep the returned browser ID and stop the browser with `PATCH /api/v4/browsers/{id}` and `{"action":"stop"}` when finished. See the [Create browser reference](/cloud/api-v4/browsers/create-browser-session) and [Browser quickstart](/cloud/browser/quickstart). ### Agent runs For a hosted agent, pass the proxy in the run's browser settings: ```python Python theme={null} run = client.runs.create( "Check the account dashboard", browser_settings={ "customProxy": { "host": "proxy.example.com", "port": 8080, "username": "user", "password": "pass", "ignoreCertErrors": False, } }, ) ``` ```typescript TypeScript theme={null} const run = await client.runs.create({ task: "Check the account dashboard", model: "grok-4.5", browserSettings: { proxyCountryCode: "us", customProxy: { host: "proxy.example.com", port: 8080, username: "user", password: "pass", ignoreCertErrors: false, }, }, }); ``` A custom proxy overrides `proxyCountryCode` and must be passed again when a follow-up provisions a new browser. See the [Create run reference](/cloud/api-v4/runs/create-run) for the complete settings object. # Browser Infrastructure quickstart Source: https://docs.browser-use.com/cloud/browser/quickstart Launch a cloud browser and connect to it from your code. Every browser includes stealth, proxies, live preview, and recording. Its **CDP URL** is a WebSocket endpoint for remotely controlling Chrome. Your code using a CDP URL to connect to and control a cloud browser that accesses the web Your code using a CDP URL to connect to and control a cloud browser that accesses the web Get an [API key](https://cloud.browser-use.com/settings?tab=api-keys\&new=1) and export it: ```bash theme={null} export BROWSER_USE_API_KEY=your_key ``` ## Install the SDK Skip this step if you use curl. ```bash Python theme={null} pip install --upgrade browser-use-sdk playwright ``` ```bash Playwright theme={null} npm install browser-use-sdk@latest playwright ``` ```bash Puppeteer theme={null} npm install browser-use-sdk@latest puppeteer-core ``` ## Launch and connect The SDK and REST examples use API V4. Each SDK example creates a browser, connects over CDP, opens a page, and stops the managed browser in `finally`. Use its existing browser context so profile cookies and managed settings apply. No local browser download is needed for a remote CDP connection. ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse from playwright.sync_api import sync_playwright with BrowserUse() as client: session = client.browsers.create(proxy_country_code="us") try: if not session.cdp_url: raise RuntimeError("The browser did not return a CDP URL") with sync_playwright() as p: browser = p.chromium.connect_over_cdp(session.cdp_url) context = browser.contexts[0] page = context.pages[0] if context.pages else context.new_page() page.goto("https://example.com") print(page.title()) finally: client.browsers.stop(session.id) ``` ```typescript Playwright theme={null} import { BrowserUse } from "browser-use-sdk/v4"; import { chromium } from "playwright"; const client = new BrowserUse(); const session = await client.browsers.create({ proxyCountryCode: "us" }); try { if (!session.cdpUrl) throw new Error("The browser did not return a CDP URL"); const browser = await chromium.connectOverCDP(session.cdpUrl); try { const context = browser.contexts()[0]; const page = context.pages()[0] ?? await context.newPage(); await page.goto("https://example.com"); console.log(await page.title()); } finally { await browser.close(); } } finally { await client.browsers.stop(session.id); } ``` ```typescript Puppeteer theme={null} import { BrowserUse } from "browser-use-sdk/v4"; import puppeteer from "puppeteer-core"; const client = new BrowserUse(); const session = await client.browsers.create({ proxyCountryCode: "us" }); try { if (!session.cdpUrl) throw new Error("The browser did not return a CDP URL"); const browser = await puppeteer.connect({ browserWSEndpoint: session.cdpUrl }); try { const context = browser.defaultBrowserContext(); const page = (await context.pages())[0] ?? await context.newPage(); await page.goto("https://example.com"); console.log(await page.title()); } finally { await browser.disconnect(); } } finally { await client.browsers.stop(session.id); } ``` ```bash curl theme={null} # Requires curl and jq. Run this in the shell used by your automation script. browser=$(curl --fail-with-body -sS https://api.browser-use.com/api/v4/browsers \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"proxyCountryCode":"us"}') export BROWSER_SESSION_ID=$(echo "$browser" | jq -er .id) export BROWSER_USE_CDP_URL=$(echo "$browser" | jq -er .cdpUrl) # Connect your Playwright or Puppeteer script to BROWSER_USE_CDP_URL. # Keep this browser running until that script finishes, then stop it below. ``` Disconnecting CDP, Playwright's `browser.close()`, or `client.close()` does not stop the managed browser. Use `client.browsers.stop(session.id)` as above. With Puppeteer, use `disconnect()` to release the connection, then stop the managed browser through the API. For a browser created with curl, run this **after** your automation finishes: ```bash theme={null} curl --fail-with-body -X PATCH \ "https://api.browser-use.com/api/v4/browsers/$BROWSER_SESSION_ID" \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"action":"stop"}' ``` Selenium cannot connect to a remote WebSocket CDP URL; use Playwright or Puppeteer. Use the [live preview](/cloud/browser/live-preview) while the browser is running, then stop it when you are done. Recording is off by default for API browsers. Set `enable_recording=True` / `enableRecording: true` when creating the browser if you need a recording. The video becomes available asynchronously after it stops; Zero Data Retention projects never record. Full connection guide and stop semantics. Configure proxies, screen size, recording, and timeout. # Stealth Source: https://docs.browser-use.com/cloud/browser/stealth Managed Chromium with stealth protections and proxy support. See [how we perform in the hardest stealth benchmark](https://browser-use.com/posts/stealth-benchmark). ## What's included Every cloud browser session runs in a hardened Chromium fork with stealth enabled by default — no configuration needed. * **Anti-detect browser fingerprinting** — Canvas, WebGL, fonts, navigator, and other browser fingerprints are randomized per session to appear as a real user. Fingerprint test results vary with browser version and the target site. * **Ad and cookie banner blocking** — Banners are dismissed automatically so the agent sees clean pages and executes faster. * **Anti-bot protections** — Help with sites protected by Cloudflare, PerimeterX, and other detection services. Access to every site is not guaranteed. Cloud browsers also solve CAPTCHAs automatically. See [CAPTCHA handling](/cloud/browser/captcha-handling) for what to do when a solver is still working or a site has a specific anti-bot issue. ## Residential proxies Residential proxies are enabled by default across 195+ countries. This makes browser sessions appear as real users from the target geography. See [Proxies](/cloud/browser/proxies) for details on geo-targeting and custom proxy configuration. Stealth is included in the managed browser. It is not a separate subscription upgrade you must buy to enable these protections. A target site can still reject a browser or require verification; see [troubleshooting](/cloud/guides/troubleshooting). # Choosing an Agent Source: https://docs.browser-use.com/cloud/choosing-an-agent Compare Browser Use Cloud agents on accuracy, speed and price, and pick the right one for your task. Browser Use Cloud offers three agents that can complete your tasks. Use V4 when accuracy matters most, V3 when speed and cost matter most, and V2 only for older integrations. ## At a glance | | V4 Agent | V3 Agent | V2 Agent | | ---------------------- | ------------------------ | ------------------------------------- | ----------------------------------- | | | Most accurate | Fastest | Legacy | | Verdict | Choose for complex tasks | Choose for quick, cost-optimized work | Keep only for existing integrations | | Accuracy on hard tasks | **76%** | 67% | 54% | | Speed | slower, more thorough | **fastest on identical tasks** | quick on small tasks | | Cost level | \$\$\$ | \$\$ | \$ | ### V4 Agent — most accurate V4 writes and runs code to complete long, multi-step browser tasks. It can research across many pages, compare options, follow detailed instructions, collect records, and save the results as a spreadsheet or in any format. **Best for:** * Bulk data collection * Complex tasks that span many pages and sites * Long, difficult instructions **Example tasks:** * "Collect every document with its title, reference, and deadline." * "Create a spreadsheet of all the pricing plans across these five competitors." The most thorough option, but it takes its time. ### V3 Agent — fastest V3 looks at the page and acts step-by-step like a human would. Use only when cost and speed matter more than accuracy. **Best for:** * Simple tasks, such as finding publicly available information * Workloads where cost and speed are essential * Open-ended research **Example tasks:** * "Get a shipping quote for a 3 lb package between two zip codes." * "Check whether this product is in stock and what it costs right now." Faster but less reliable. ### V2 Agent — legacy Our first-generation agent remains available for compatibility. It is based on our open-source repository, but it is not actively maintained and is less accurate. Starting something new? Use V4 instead; it's far more accurate. ## V4 completes the hardest tasks V4 completed 76% of difficult, real-world browser tasks — nine points ahead of V3 and twenty-two ahead of V2. Browser Use Cloud success rate on hard web tasks: V2 Agent 54.25%, V3 Agent 67.25%, V4 Agent 76.47% ## Typical tasks cost \$0.70–\$1.64 Measured with the same model — Claude Opus 4.7 — across the last 30 days of production usage, the typical customer's successful task costs \$0.70 on V2, \$0.87 on V3, and \$1.64 on V4, taking about 1, 2, and 5 minutes respectively. V4 costs more per task because customers use it for more complex work, and V2's tasks run shortest only because it gets the simplest ones. On identical hard tasks, V3 is the fastest agent (about 6 minutes, versus about 10 for V2 and V4). | | V2 Agent | V3 Agent | V4 Agent | | ------------- | -------- | -------- | -------- | | Cost per task | \$0.70 | \$0.87 | \$1.64 | | Time per task | \~1 min | \~2 min | \~5 min | What a task costs with Claude Opus 4.7 from real usage: V2 $0.70, V3 $0.87, V4 $1.64 per successful task ## Comparing accuracy, speed, and cost Comparing agent accuracy, speed, and cost: V4 is most accurate at $1.64 per task, V3 is fastest at $0.87, V2 is cheapest at $0.70 but least accurate ## How we measured this In July 2026, we ran each agent four times on Internal Bench Hard: 106 difficult tasks on real websites. Every run used Claude Opus 4.8, and the same independent judge scored each result. We left out tasks blocked by websites' anti-bot measures. The speed and cost figures are averages; simpler tasks usually run faster and cost less. Real-world cost and speed figures are the median customer's successful task with Claude Opus 4.7 over the last 30 days of production, our own testing excluded. # FAQ Source: https://docs.browser-use.com/cloud/faq Common questions and solutions. ## Which model should I use? * **GPT-5.6 Luna** (`gpt-5.6-luna`, recommended) — the best balance of accuracy, speed, and price for most tasks. * **Claude Opus 5** (`claude-opus-5`) — maximum intelligence for difficult, long-horizon work. * **Grok 4.5** (`grok-4.5`) — a strong general-purpose alternative. * **GPT-5.6 Sol** (`gpt-5.6-sol`) — a higher-cost OpenAI option for complex tasks. * **MiniMax M3** (`minimax-m3`) — inexpensive for simple and high-volume tasks. See [Models](/cloud/agent/models) for the complete V4 picker and pricing. ## How do I get the live browser URL? The V4 run's `browser.ready` event contains `live_view_url`. Embed it in an iframe or open it in a browser. ```python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() created = client.runs.create("Go to example.com") client.runs.wait_for_completion(created.id) events = client.runs.events(created.id, limit=100) ready = next(event for event in events.events if event.type == "browser.ready") print(ready.data["live_view_url"]) ``` Poll events until `browser.ready` appears if you need the URL while the run is still active. See [Human in the loop](/cloud/agent/human-in-the-loop) for a complete flow. ## Getting blocked by a website Stealth and proxies are active by default. If you're still getting blocked: * **Use a profile** with logged-in cookies to bypass login walls. * **Try a different proxy country** to match the target region. If it still doesn't work, contact support inside the [Cloud Dashboard](https://cloud.browser-use.com) — send us a link to the page where you're getting blocked. ## Rate limited (429 errors) HTTP request limits, browser concurrency, and a full session message queue can all return 429. Inspect the response before retrying: increasing browser concurrency does not increase the general request bucket used by V4 event reads. Use bounded workers, stagger polling, and respect `Retry-After` when provided. The SDK retries 429 responses, but a sustained workload needs its own request budget. See [Concurrency and limits](/cloud/guides/concurrency) for account-limit lookup, polling budgets, queue behavior, and error handling. For insufficient-balance or API-key spending-cap errors (402), see [Billing and credits](/cloud/guides/billing). ## V2 or V4 — which should I use? Use **V4** for difficult tasks where accuracy matters. It supports: * Run-focused API with a cheap status polling endpoint * Conversation sessions with follow-ups * Persistent workspaces and turn-scoped file attachments * Incremental events for custom UIs and monitoring * Per-run cost totals, cost caps, and optional judgement Use **V2** when tasks are simple and your priority is very low cost and predictable speed. Its accuracy is substantially lower. See Browser Use at #1 on the [Odysseys benchmark](https://odysseysbench.com/leaderboard). # 1Password & 2FA Source: https://docs.browser-use.com/cloud/guides/1password Auto-fill passwords and TOTP codes from 1Password in API tasks. ## Setup ### 1. Create a dedicated vault Create a new vault in 1Password for Browser Use. Add the credentials you want the agent to access (usernames, passwords, and 2FA/TOTP codes). ### 2. Create a service account token 1. Go to [1Password Developer Tools - Service Accounts](https://my.1password.eu/developer-tools/active/service-accounts) 2. Click **New Service Account**, name it "Browser Use Cloud" 3. Grant **read access** to the dedicated vault 4. Copy the generated token ### 3. Connect to Browser Use Cloud 1. Go to [Browser Use Cloud Settings - Secrets](https://cloud.browser-use.com/settings?tab=secrets) 2. Click **Create Integration** 3. Paste your service account token ## Use a vault in a v4 run Pass the vault id and the domains where its credentials may be typed. Browser Use finds the project's connected 1Password integration and makes the vault's username, password, and TOTP fields available to the run. You do not need the integration, item, or field ids. Every supported credential field in the vault is made available to the run. Use a dedicated vault containing only the accounts the agent needs. ```bash theme={null} curl -X POST https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "task": "Open amazon.com, log in, and check my recent orders", "opVaultId": "", "opVaultAllowedDomains": ["amazon.com"] }' ``` * `opVaultAllowedDomains` is required with `opVaultId`. Use bare hostnames; a hostname also covers its subdomains. * Vault items become aliases based on their titles, with `_username`, `_password`, or `_bu_2fa_code` suffixes. * Vault fields and explicit `secretBindings` share a limit of 10 bindings per run. The API returns `422` if the combined total exceeds 10. * Vault access requires exactly one active 1Password integration on the project and is unavailable for zero-data-retention projects. These fields work through the v4 REST API. The currently published Python and TypeScript SDK types do not expose them yet; use the REST request above until those clients are regenerated. ## Bind individual fields in a v4 run For least-privilege access, use **secret bindings** instead of exposing every credential field in a vault. Each binding names one field of one 1Password item, gives it an alias the agent can ask for, and lists the hosts where it may be typed. The server types the value into the focused field when the agent asks for the alias on an allowed domain. This keeps the raw value out of your task text, but does not make it inaccessible to the agent: once entered, it is available to the page and to browser/CDP access. Bindings are run-scoped, so a follow-up run that needs the same login must send them again. ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse with BrowserUse() as client: run = client.runs.create( "Log into Jira and create a ticket for the Q4 release", secret_bindings=[ { "alias": "jira_password", "source": { "type": "onepassword", "integrationId": "", "vaultId": "", "itemId": "", "fieldId": "password", }, "allowedDomains": ["atlassian.net"], } ], ) print(client.runs.wait_for_completion(run.id).result) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const run = await client.runs.create({ task: "Log into Jira and create a ticket for the Q4 release", secretBindings: [ { alias: "jira_password", source: { type: "onepassword", integrationId: "", vaultId: "", itemId: "", fieldId: "password", }, allowedDomains: ["atlassian.net"], }, ], }); ``` * `integrationId` is the 1Password integration you connected under **Settings → Secrets**. * `vaultId` and `itemId` are the 1Password UUIDs of the vault and item (copy them from 1Password). * `fieldId` is the field's id, not its label: `username`, `password`, or `one-time-password` for a TOTP field, which resolves to the current code. * `allowedDomains` are bare hostnames; a host covers its subdomains. In the chat UI at cloud.browser-use.com both options live under **Run settings → Credentials**. **Use a whole vault** asks for the vault and the allowed sites and sends `opVaultId` + `opVaultAllowedDomains`; **Add a credential** walks vault, item, and field, takes an alias and the domains, and sends one binding. The **Agents** page has a **1Password** section with the same vault + allowed-sites pair, and its REST snippet shows the resulting request. ## Use a vault in a v2 or v3 task ```python Python theme={null} from browser_use_sdk import AsyncBrowserUse client = AsyncBrowserUse() result = await client.run( "Log into my Jira account and create a new ticket", op_vault_id="your-vault-id", allowed_domains=["*.atlassian.net"], ) print(result.output) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk"; const client = new BrowserUse(); const result = await client.run( "Log into my Jira account and create a new ticket", { opVaultId: "your-vault-id", allowedDomains: ["*.atlassian.net"], }, ); console.log(result.output); ``` For SSO/OAuth redirects, include all required domains: ```python Python theme={null} result = await client.run( "Log into Jira and create a ticket for the Q4 release", op_vault_id="your-vault-id", allowed_domains=["*.atlassian.net", "*.okta.com"], ) ``` ```typescript TypeScript theme={null} const result = await client.run( "Log into Jira and create a ticket for the Q4 release", { opVaultId: "your-vault-id", allowedDomains: ["*.atlassian.net", "*.okta.com"], }, ); ``` ## How it works When the agent encounters a login form: 1. It identifies the service (e.g., Twitter, GitHub, LinkedIn) 2. Retrieves matching credentials from your 1Password vault 3. Fills in the username and password 4. If 2FA is required and a TOTP code is stored, it generates and enters the code automatically The agent never sees your actual credentials. The actual username, password, and 2FA codes are filled in programmatically — keeping your secrets hidden from the AI model. # 2FA Source: https://docs.browser-use.com/cloud/guides/2fa Handle two-factor authentication in API V4 runs. The most reliable options are a saved profile or a human checkpoint. ## Reuse a logged-in profile [Sync your local login](/cloud/guides/profile-sync), then load that profile in the run: ```python Python theme={null} run = client.runs.create( "Download my latest invoice", browser_settings={"profileId": "YOUR_PROFILE_ID"}, ) ``` ```typescript TypeScript theme={null} const run = await client.runs.create({ task: "Download my latest invoice", model: "grok-4.5", browserSettings: { profileId: "YOUR_PROFILE_ID", proxyCountryCode: "us", }, }); ``` This avoids another 2FA challenge while the site's cookies remain valid. ## Let a human take over Ask the first run to stop at the 2FA screen, get its `live_view_url` from the [`browser.ready` event](/cloud/agent/human-in-the-loop), and have the user enter the code. Then continue with the same session: ```python Python theme={null} first = client.runs.create( "Open the login page and stop at the 2FA prompt", ) client.runs.wait_for_completion(first.id) next_run = client.runs.create( "Continue after login and download the invoice", session_id=first.session_id, ) ``` ```typescript TypeScript theme={null} const first = await client.runs.create({ task: "Open the login page and stop at the 2FA prompt", model: "grok-4.5", }); await client.runs.waitForCompletion(first.id); const nextRun = await client.runs.create({ task: "Continue after login and download the invoice", model: "grok-4.5", sessionId: first.sessionId, }); ``` See [Human in the loop](/cloud/agent/human-in-the-loop) for retrieving and embedding the live browser URL. Never put passwords or TOTP secrets directly in a prompt. # Profiles Source: https://docs.browser-use.com/cloud/guides/authentication Reuse cookies and browser state in API V4 runs. A profile persists cookies, local storage, and login state across browsers. Log in once, save the profile, then reuse it to start future browsers already logged in. One login saved as a profile and reused by multiple future browsers One login saved as a profile and reused by multiple future browsers Create or select one under [Dashboard → Profiles](https://cloud.browser-use.com/settings?tab=profiles), then pass its ID in V4 browser settings: ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() run = client.runs.create( "Open my account dashboard and summarize it", browser_settings={"profileId": "YOUR_PROFILE_ID"}, ) result = client.runs.wait_for_completion(run.id) print(result.result) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const run = await client.runs.create({ task: "Open my account dashboard and summarize it", model: "grok-4.5", browserSettings: { profileId: "YOUR_PROFILE_ID", proxyCountryCode: "us", }, }); const result = await client.runs.waitForCompletion(run.id); console.log(result.result); ``` ```bash curl theme={null} curl https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"task":"Summarize my account dashboard", "browserSettings":{"profileId":"YOUR_PROFILE_ID"}}' ``` Use one profile per end user. Follow-ups in the same [session](/cloud/agent/sessions) reuse the live browser; later sessions can load the same profile again. For the fastest setup, [sync an existing local login](/cloud/guides/profile-sync). # Billing and credits Source: https://docs.browser-use.com/cloud/guides/billing Pay-as-you-go credits, project balances, BYOK charges, auto recharge, and API-key spending caps. Browser Use Cloud uses pay-as-you-go billing. Add credits to the project that owns your API key, then pay for usage. A recurring Browser Use subscription is not required for custom proxies or supported bring-your-own-provider-key (BYOK) usage. Existing customers can retain legacy allowances and pricing. Use the [pricing page](https://browser-use.com/pricing) for current model, browser, proxy, and network rates. New top-ups have a \$5 minimum and use whole-dollar amounts. ## Which credits does my API key use? Credits belong to a **project**. All keys in that project use its balance. Funding one project does not fund a key from another project, even if both belong to you. ```bash theme={null} curl --fail-with-body https://api.browser-use.com/api/v2/billing/account \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" ``` This endpoint works with the same key you use for V4. Compare `projectId` with the project selected in the dashboard. Read `totalCreditsBalanceUsd` for the reported total and `monthlyCreditsBalanceUsd` / `additionalCreditsBalanceUsd` for its components. A `null` `planInfo` is normal for a pay-as-you-go project; it does not mean the project cannot use paid features. If a payment succeeded but the expected credits are missing, first check the project and the payment's status. When contacting support, include the project ID, payment time, amount, and receipt or invoice reference. Never include API keys or card details. ## Do credits expire or renew? | Credit type | Behavior | | ----------------------------------------- | ----------------------------------------------------- | | Signup credits | A one-time grant, not a recurring monthly allowance. | | Purchased top-up credits | Do not expire. | | Included credits on a legacy monthly plan | Follow the plan's billing cycle and do not roll over. | Concurrency tiers are based on qualifying payments to the project, not credits remaining in its wallet. Using purchased credits does not lower an already-granted concurrency allowance. Credit grants do not count as payments. Higher existing or legacy concurrency grants are preserved; see [Concurrency and limits](/cloud/guides/concurrency). ## What does BYOK pay for? With a supported provider key, the model provider bills token usage to your provider account. Browser Use still charges an orchestration fee of **0.2× the corresponding model token cost**, plus browser and network usage. Keep both accounts funded. Configure supported provider keys in **Settings → API Keys → Bring Your Own Key**. The dashboard requires adding credits before BYOK setup is available. A pay-as-you-go top-up satisfies that requirement; no recurring subscription is needed. Accounts that have never paid also have model eligibility restrictions. A Claude consumer subscription is not an Anthropic API key. BYOK means a supported provider credential, not an arbitrary self-hosted model endpoint. See [Models](/cloud/agent/models) for model selection. ## Proxy and browser charges A [custom proxy](/cloud/browser/proxies) replaces the Browser Use managed proxy. Your proxy provider bills its own usage, and Browser Use browser/network charges still apply. Disabling the managed proxy also leaves browser and direct network usage billable. Run completion does not necessarily stop the browser. A browser may remain available for the next turn. When your application no longer needs it, explicitly stop the owned browser with `PATCH /api/v4/browsers/{id}` and `{"action":"stop"}`. Disconnecting Playwright/CDP or closing the SDK client is not the cloud stop operation. See [browser lifetime](/cloud/guides/concurrency#keep-track-of-browser-lifetime). ## Auto recharge Auto recharge has two separate settings: * **Threshold:** the balance below which another purchase is triggered. * **Recharge amount:** the amount of credits purchased by that recharge. For example, a \$10 threshold with a \$50 recharge amount purchases \$50 when the balance falls below \$10. It does not target a final balance of \$50. Enabling auto recharge while already below the threshold can trigger a charge immediately. Choose an amount that covers your expected workload and check payment history if a recharge fails. Funding and concurrent workloads can change the displayed balance while a payment is being processed. ## API-key monthly spending caps An API key can have a monthly USD spending cap. It is a **soft limit** on Browser Use charges recorded for that key during the current UTC calendar month, shared across that key's applicable usage. When a cap is reached, new run/browser admission can return HTTP 402 with a structured detail: ```json theme={null} { "detail": { "code": "api_key_monthly_spend_limit_reached", "message": "API key monthly spend limit reached", "cap": 50, "spent": 50.25 } } ``` Use `detail.code`, `cap`, and `spent` to distinguish this response from an insufficient project balance. Adding credits does not raise a key's cap. Check the cap in API key settings if the project still has credits. Spend checks use a cached snapshot, and running or simultaneous work can finish above the cap. A cap is not a strict prepaid wallet or a guarantee that costs stop at exactly that amount. BYOK provider charges are billed separately by the provider. Combine caps with bounded concurrency, project balance monitoring, and per-run controls where available. See [Concurrency and limits](/cloud/guides/concurrency#diagnose-errors-before-retrying) for 402, 409, and 429 handling. # Concurrency and limits Source: https://docs.browser-use.com/cloud/guides/concurrency Preserved legacy allowances, spend tiers, browser lifetime, polling budgets, queues, and retry guidance. Browser Use has separate limits for simultaneous browser work and API request frequency. A project can have available browser capacity and still receive a rate-limit response if it sends too many HTTP requests. For reliable batch processing, use a bounded worker pool, track the browsers your application owns, and budget polling requests across the project. ## Check your project's capacity Use your API key to inspect its project: ```bash theme={null} curl --fail-with-body \ https://api.browser-use.com/api/v2/billing/account \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" ``` This billing endpoint is also useful when your application uses V4 runs. Read: | Field | Meaning | | ------------------------ | ------------------------------------------------------------------------------------------------- | | `projectId` | The project this API key accesses. Check this if you funded a different project in the dashboard. | | `apiKeyId` | The internal identifier of the authenticated key. | | `concurrentSessionLimit` | The project's current browser concurrency allowance. | | `activeSessionCount` | Reported active browser count. Track the browser IDs your application owns as well. | | `totalCreditsBalanceUsd` | Its reported available credit balance. | `rateLimit` is a legacy concurrency field. It does not tell you the HTTP request limit. Treat account information as a snapshot rather than a reservation for a future request. Several workers may read the same available capacity before any of them starts a browser. Keys in the same project share its capacity and balance. Creating more keys does not create more browser slots. Free accounts can also have limits across their projects. ## How usage tiers work For projects on current pricing, concurrency increases with the project's net settled lifetime Stripe payments: | Lifetime qualifying spend | Concurrent sessions | | ------------------------: | ------------------: | | \$0 | 10 | | \$200 | 50 | | \$1,000 | 250 | | \$5,000 | 500 | | \$25,000 | 1,000 | Payments count for the project that received them. Refunds and disputed payments do not contribute to qualifying spend. Signup credits and other credit grants are not qualifying purchases. Funding through other billing arrangements may follow different rules. **Your higher concurrency allowance is preserved.** When a project participates in spend tiers, its limit takes the maximum of its applicable legacy-plan allowance, the current pricing floor, and its spend-tier allowance. An already-granted higher limit is not automatically lowered. For example, a legacy allowance of 250 remains 250 when the spend tier would grant 50. Some active legacy or externally billed projects follow a different billing path. Lapsed or cancelling legacy plans can transition onto current pricing while keeping their higher grant. Use `concurrentSessionLimit` and the project’s Billing page as the source of truth. If the tier ladder is absent or differs from your expectations, contact support with your project ID before making a purchase solely to increase concurrency. ## Keep track of browser lifetime A V4 **session** is a conversation. A **run** is one turn in that conversation. A **browser** is the live Chrome instance used for website interaction. A browser can survive the run that created it and be reused by a later turn. A conversation can also have multiple browsers. Additional tabs in the same browser are not additional browser sessions. After all runs in a conversation are inactive, V4 browsers become eligible for cleanup after approximately 20 minutes without recent run activity. Cleanup runs periodically, so this is not an exact shutdown deadline. V4 agent browsers also have a four-hour hard timeout. If your application is finished with a browser, explicitly stop that browser rather than relying on idle cleanup. Only stop browser IDs your application owns and no longer needs. For a standalone browser, the stop request is: ```bash theme={null} curl --fail-with-body -X PATCH \ "https://api.browser-use.com/api/v4/browsers/$BROWSER_SESSION_ID" \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"action":"stop"}' ``` Do not use this against a browser another active run or person still needs. Closing your local CDP connection is not a substitute for checking the cloud browser's lifecycle. If your workflow requires human interaction, deliver the live-view URL from `browser.ready` while the run/browser is active. A conversation remaining in history does not mean its live browser is still available. ## Queue follow-ups within a conversation Only one V4 run can be active in a session. Submitting a direct follow-up while it is busy returns **409**. Choose the behavior you need: * Wait for the active run to finish, then submit the next turn. * Submit through the session message queue to process a later turn. * Intentionally interrupt the active run when you want to change its current work. The session message queue accepts up to **10 pending messages**. A full queue returns **429**. It is a queue for that conversation, not a project-wide batch queue or a reservation of browser capacity. Keep a large batch in your application's own job queue. Admit a bounded number of jobs and leave the rest pending there. ## Budget HTTP requests separately HTTP traffic passes through two separate limits: a shared per-IP edge limit and your project's application limit. Raising one does not raise the other. ### Edge limits On September 9, 2026, the standard edge (WAF) ceilings increased: | Requests from one public source IP | Edge ceiling | Evaluation window | | ---------------------------------- | --------------------: | --------------------------------: | | General API traffic | 1,000 requests/second | 300,000 requests over 300 seconds | | Selected individual status reads | 2,500 requests/second | 750,000 requests over 300 seconds | These are approximate rates evaluated over a five-minute window, not a strict per-second cap or a guaranteed burst allowance. Applications behind the same NAT or proxy share the IP's budget. Account-specific edge rules and certain special endpoints can have different limits. ### Project limits The edge increase did **not** change the standard per-project application buckets: | Requests | Default request budget | | ----------------------------------------------------------------- | ------------------------------------------------: | | Creates, lists, updates, deletes, event reads, and full run reads | 25 requests/second | | Selected individual status reads | `max(25, 2 × stored concurrency)` requests/second | The selected V4 polling endpoints include: * `GET /api/v4/runs/{run_id}/status` * `GET /api/v4/sessions/{session_id}` * `GET /api/v4/browsers/{browser_session_id}` `GET /api/v4/runs/{run_id}/events` and `GET /api/v4/runs/{run_id}` use the general bucket. Their budget does not automatically grow with your concurrency grant. Account overrides and other protections can differ from these defaults; use the response headers and error details when diagnosing throttling. For example, a project with 500 stored concurrent sessions and no request-rate override has a 1,000 RPS status-polling budget and a separate 25 RPS general budget. All API keys in that project share those budgets. Creating a new key does not create more request capacity. The project limiter counts requests in **five-second windows**. A 25 RPS budget therefore permits 125 requests per window. `X-RateLimit-Limit` reports that window allowance, not requests per second: a value of `125` does not mean 125 RPS. `X-RateLimit-Remaining` is the window's remaining request count. `X-RateLimit-Reset` is a relative number of seconds, not a Unix timestamp. A project throttle includes `limit_rps` and `retry_after_seconds` in the JSON body and normally sends `Retry-After: 5`. An edge throttle can instead send `Retry-After: 300` without `limit_rps`. Honor the delay actually returned; a five-second retry loop is inappropriate for a five-minute edge throttle. Capture the response headers and body when asking support to identify which limit applied. A missing limit header is not a promise of unlimited traffic. If you only need the final result, use the SDK's status-based wait helper: ```python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() # run_id is the ID of a run your application already created. result = client.runs.wait_for_completion(run_id) print(result.status, result.result) ``` SDK 3.11.3 defaults to a two-second status interval and a four-hour client wait timeout. Reaching that timeout raises an exception locally; it does not cancel the server-side run. Inspect or cancel the existing run before deciding whether to submit replacement work. ### When you need events Use the returned cursor to continue reading and process every page with `hasMore` before treating the event stream as caught up. After terminal status, drain the remaining event pages; do not exit solely because a separate status request completed. Set a request budget across all event streams in the project. For example, polling events once per second for 50 runs would generate about 50 event requests per second, exceeding the standard general bucket before counting any creates or other requests. Polling each every five seconds would average about 10 event requests per second; pagination and other traffic still need headroom. Spread poll times so that every worker does not send its request on the same clock tick. A per-project limiter in your application is more predictable than each worker independently retrying. For SDK event waits, set `wait_for_event(..., interval=3)` in Python or `waitForEvent(..., { interval: 3_000 })` in TypeScript. This also works on SDK 3.11.3, whose event-wait default is one second. One hundred waits at a three-second interval average about 33 event requests per second, exceeding the standard 25 RPS general budget. For that workload, explicitly use a five-second interval (20 event requests per second) or a shared request scheduler, leaving headroom for creates, pagination, and other general calls. The event-wait helper pauses between pages too. For an old run with a large event history, start from a known cursor or implement paginated reads within your shared request budget; a longer interval also makes backlog traversal slower. ## Diagnose errors before retrying | Response | Likely meaning | What to do | | ------------------------------------------------------------------------ | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | 429 with `error: "rate_limited"` and `limit_rps` | Project HTTP request budget exhausted | Honor `Retry-After`/`retry_after_seconds`; reduce or stagger requests across the project. | | 429 with `error: "rate_limited"`, no `limit_rps`, and `Retry-After: 300` | Edge request protection | Back off for the returned delay; check bursts and traffic sharing the same public IP. | | 429 with a concurrent-session message in `detail` | Active session allowance reached | Inspect capacity, stop unneeded browsers, and reduce admitted jobs. | | 429 with a queue-full message in `detail` | This conversation has 10 pending messages | Wait for its queue to drain or cancel an appropriate pending message. | | 409 with an active-run message | Conversation is already busy | Wait, queue, or intentionally interrupt. | | 402 with `detail.code: "api_key_monthly_spend_limit_reached"` | This key's monthly soft spending cap was reached | Check the cap and recorded spend in the current UTC month. | | Other 402 | May be insufficient project balance or another payment condition | Inspect the complete response and the API key's project. | The SDK retries some transient responses, including 429, but the default is only three retries. SDK 3.11.3 uses exponential delays of roughly 1, 2, and 4 seconds and does not itself apply `Retry-After` or jitter. Add application-level scheduling for sustained workloads. Do not repeatedly submit a new billable run when the outcome of an earlier submission is uncertain. SDK 3.11.3 errors expose the response status and body, but not response headers. Use `retry_after_seconds` when the body includes it. To diagnose an edge throttle or read its exact `Retry-After`, capture the raw HTTP response through your application's HTTP instrumentation or a REST client. API-key monthly spending caps are **soft limits**. They use a cached spend snapshot; already-running or simultaneous work can finish above the cap. They do not replace project balance management or per-run cost controls. BYOK provider charges also remain separate from Browser Use charges. ## A practical batch-processing pattern 1. Resolve the key's project, available balance, and current capacity. 2. Keep jobs in a durable application queue and admit a bounded number of workers. Leave room for browsers used elsewhere in the project and additional browsers created by agents. 3. Track job ID, session ID, run ID, and browser IDs separately. 4. Use cheap status polling for result-only jobs; share one request budget across event streams and general API operations. 5. Handle busy-session, queue, rate, capacity, and spending errors according to their cause. 6. Record terminal outcomes, release unneeded browsers, and retry only when the job's previous outcome is understood. When contacting support, include the UTC time window, endpoint, status code, redacted response body, project ID, SDK version, run/session/browser IDs, and approximate request rate and concurrency. Do not send API keys, proxy passwords, or saved browser credentials. See also [Billing and credits](/cloud/guides/billing), [Sessions](/cloud/agent/sessions), and [Observability](/cloud/agent/observability). # MCP Server Source: https://docs.browser-use.com/cloud/guides/mcp-server Run browser automation tasks from your AI coding assistant. Connect to Claude, Cursor, Windsurf, or any MCP client. ``` https://api.browser-use.com/v3/mcp ``` Get your API key at [cloud.browser-use.com/settings](https://cloud.browser-use.com/settings?tab=api-keys\&new=1). ## Claude Code ```bash theme={null} claude mcp add -t http -H "x-browser-use-api-key: YOUR_API_KEY" browser-use https://api.browser-use.com/v3/mcp ``` ## Claude Desktop Add to `claude_desktop_config.json`: ```json theme={null} { "mcpServers": { "browser-use": { "url": "https://api.browser-use.com/v3/mcp", "headers": { "x-browser-use-api-key": "YOUR_API_KEY" } } } } ``` ## Cursor Add to `.cursor/mcp.json`: ```json theme={null} { "mcpServers": { "browser-use": { "url": "https://api.browser-use.com/v3/mcp", "headers": { "x-browser-use-api-key": "YOUR_API_KEY" } } } } ``` ## Windsurf Add to `~/.codeium/windsurf/mcp_config.json`: ```json theme={null} { "mcpServers": { "browser-use": { "serverUrl": "https://api.browser-use.com/v3/mcp", "headers": { "x-browser-use-api-key": "YOUR_API_KEY" } } } } ``` ## Available Tools | Tool | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `run_session` | Create a session and run a task. Supports `keep_alive`, `model` (`claude-sonnet-4.6`, `claude-opus-4.6`, `gpt-5.4-mini`), `output_schema`, and `profile_id`. | | `get_session` | Poll session status and output. Returns status, step count, cost breakdown, and live URL. | | `send_task` | Send a follow-up task to an idle keep-alive session. | | `stop_session` | Stop a session. `strategy: "task"` stops only the task, `"session"` destroys the sandbox. | | `get_session_messages` | Get the agent's messages — browser actions, reasoning, and results. | | `list_sessions` | List recent sessions with status and cost. | | `list_browser_profiles` | List browser profiles for authenticated tasks. | # Sync local and cloud cookies Source: https://docs.browser-use.com/cloud/guides/profile-sync Sync a local login, then use it in an API V4 run. Run the profile sync helper: ```bash theme={null} export BROWSER_USE_API_KEY=your_key curl -fsSL https://browser-use.com/profile.sh | sh ``` Choose the accounts to sync, then use the returned profile ID: ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse client = BrowserUse() run = client.runs.create( "Check my LinkedIn messages", browser_settings={"profileId": "YOUR_PROFILE_ID"}, ) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const run = await client.runs.create({ task: "Check my LinkedIn messages", model: "grok-4.5", browserSettings: { profileId: "YOUR_PROFILE_ID", proxyCountryCode: "us", }, }); ``` The profile supplies cookies and local storage without putting credentials in the prompt. Re-sync when the site's login expires. A profile ID belongs to a project. Use an API key from that project, and do not substitute a workspace ID or conversation/session ID. Reusing a session may reuse its current browser; to apply a newly synced profile, create a fresh browser or conversation with that profile. Cookie sync does not import a password manager or guarantee that the target site accepts a moved login. Sites can expire cookies, bind sessions to a device or network, or require another verification step. Use a stable proxy location when the site's security policy depends on location. # Secrets Source: https://docs.browser-use.com/cloud/guides/secrets Pass run-scoped credentials to API V4 with secret bindings. ## API V4: inline secret bindings Use `secret_bindings` / `secretBindings` with the explicit V4 client. Give each credential an alias and the hostnames where it may be typed. Refer to the alias in the task, not the credential value. Aliases avoid putting raw credentials in the task text. They do not guarantee that the agent cannot see the value: once typed, it is accessible to the page and to an agent with browser/CDP access. This example fills a password field and stops before submitting. Replace the example portal URL and provide `PORTAL_PASSWORD` through your application's secret store or environment. Do not commit the value or print the request body. ```python Python theme={null} import os from browser_use_sdk.v4 import BrowserUse with BrowserUse() as client: run = client.runs.create( "Open https://portal.example.com/login, enter portal_password " "in the password field, and stop before submitting", secret_bindings=[ { "alias": "portal_password", "source": { "type": "inline", "value": os.environ["PORTAL_PASSWORD"], }, "allowedDomains": ["portal.example.com"], } ], ) print(run.id) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const password = process.env.PORTAL_PASSWORD; if (!password) throw new Error("Set PORTAL_PASSWORD before starting a run"); const client = new BrowserUse(); const run = await client.runs.create({ task: "Open https://portal.example.com/login, enter portal_password in the password field, and stop before submitting", secretBindings: [ { alias: "portal_password", source: { type: "inline", value: password }, allowedDomains: ["portal.example.com"], }, ], }); console.log(run.id); ``` * `allowedDomains` takes bare hostnames, not URLs, ports, paths, or wildcards. A hostname also covers its subdomains. Use the narrowest host that works. * These domains restrict where that credential may be typed. They are **not** a browser-wide navigation or network allowlist. * Bindings belong to one run. A follow-up that needs the same credential must pass the binding again, even when it reuses the same session. * Use a separate alias for each credential field. Up to 10 bindings are allowed per run, with up to 10 allowed domains per binding. * Aliases contain 1–64 lowercase letters, digits, hyphens, or underscores and start with a letter or digit. * The inline limit is 4,096 bytes **after JSON encoding for encryption**, including its wrapper and escaping. It is not a 4,096-character allowance; non-ASCII characters, quotes, and backslashes consume extra space. For credentials stored in a connected vault, use a [1Password field binding](/cloud/guides/1password#bind-individual-fields-in-a-v4-run). See the [Create run reference](/cloud/api-v4/runs/create-run) for the current request schema. ## Legacy API V2 The examples below use the default V2 client and its `secrets` / `allowed_domains` fields. Do not pass those fields to `v4.runs.create()`; use the secret bindings above. Keep this recipe only for an existing V2 integration. Pass credentials to the agent scoped by domain. ```python Python theme={null} from browser_use_sdk import AsyncBrowserUse client = AsyncBrowserUse() result = await client.run( "Log into GitHub and star the browser-use/browser-use repo", secrets={"github.com": "username:password123"}, allowed_domains=["github.com"], ) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk"; const client = new BrowserUse(); const result = await client.run( "Log into GitHub and star the browser-use/browser-use repo", { secrets: { "github.com": "username:password123" }, allowedDomains: ["github.com"], }, ); ``` Use `allowed_domains` to restrict the agent to specific domains. Supports wildcards: `example.com`, `*.example.com`. For SSO/OAuth redirects, include all domains in the auth flow: ```python Python theme={null} result = await client.run( "Log into the company portal and download the Q4 report", secrets={ "portal.example.com": "user@company.com:password123", "okta.com": "user@company.com:password123", }, allowed_domains=["portal.example.com", "*.okta.com"], ) ``` ```typescript TypeScript theme={null} const result = await client.run( "Log into the company portal and download the Q4 report", { secrets: { "portal.example.com": "user@company.com:password123", "okta.com": "user@company.com:password123", }, allowedDomains: ["portal.example.com", "*.okta.com"], }, ); ``` # Troubleshooting Source: https://docs.browser-use.com/cloud/guides/troubleshooting Diagnose account, browser, file, and integration problems before retrying. ## My API key has credits, but a request fails Read `GET /api/v2/billing/account` with the same key. It also works for V4 users. Confirm the `projectId`, balance, and `concurrentSessionLimit` against the project selected in the dashboard. Keys in different projects do not share credits. A pay-as-you-go account can legitimately have `planInfo: null`. Read the complete error response before changing billing settings: | Response | What to check | | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 401 | Send `X-Browser-Use-API-Key`; do not put the API key in a Bearer header. | | 402 | Distinguish insufficient project credits from `api_key_monthly_spend_limit_reached`. A top-up does not raise a key's spending cap. | | 404 for a profile/workspace/session | Check the ID type, API version, and owning project. An ID visible in one dashboard project may be inaccessible to another key. | | 409 | Check the detail: a busy conversation and a workspace-file overwrite conflict require different actions. | | 413 | Check per-file and workspace limits. Retrying the same oversized payload does not make it smaller. | | 422 | Compare the request with the reference for that exact endpoint. Standalone-browser fields and V4 run `browserSettings` differ. | | 429 | Distinguish HTTP request-rate limits from occupied browser slots. Reduce polling for the first; stop unneeded browsers or reduce concurrent work for the second. | See [billing](/cloud/guides/billing), [concurrency](/cloud/guides/concurrency), and [workspaces](/cloud/agent/workspaces) for the relevant limits and remedies. ## Why am I charged when I bring my own key or proxy? BYOK means your model provider bills its tokens. Browser Use still charges orchestration plus browser and network usage. A custom proxy has its own provider charges and does not remove Browser Use browser/network charges. A Claude or ChatGPT consumer subscription is not a provider API key. See the [billing breakdown](/cloud/guides/billing). ## The dashboard works, but the same integration does not Check the API key's project, selected model, profile, proxy settings, and API version. A connected integration in the dashboard does not automatically grant every API run access to it. Pass the documented run-level bindings or grants. For example, see [1Password](/cloud/guides/1password) and [Secrets](/cloud/guides/secrets). A site can also challenge a fresh browser even if it accepts an existing logged-in browser. Reuse an appropriate [profile](/cloud/guides/profile-sync), and compare the actual proxy configuration before changing the agent prompt. ## The browser is idle, but usage or a concurrency slot remains A completed agent run can leave its browser available for follow-ups. Closing your SDK client or disconnecting CDP does not stop the managed browser. Stop an unneeded browser with `PATCH /api/v4/browsers/{id}` and `{"action":"stop"}`. See [browser lifetime](/cloud/guides/concurrency#keep-track-of-browser-lifetime). If a local wait times out, the server may still be running. Fetch the existing run's status before creating a replacement, especially if it might already have submitted a form or performed another external action. ## I cannot find a file or recording Wait for the run to finish before listing generated files, and use its V4 workspace ID. Request a fresh download URL if an old one expired. A session's conversation, workspace files, and browser profile are different resources. See [Workspaces and files](/cloud/agent/workspaces). Recordings are off by default for API browsers. Enable recording when creating the browser, stop it when finished, and allow time for processing. A live preview is not a stored video. See [Live preview and recording](/cloud/browser/live-preview). ## Does a new browser guarantee a new IP or a CAPTCHA bypass? No. Selecting a proxy country chooses a location; it does not promise a particular city or a unique IP on every launch. Websites can still block requests or require human verification. Let the automatic solver work before clicking or refreshing a challenge. See [proxies](/cloud/browser/proxies) and [CAPTCHA handling](/cloud/browser/captcha-handling). ## What should I send support? Include the API version and endpoint, run/session/browser/workspace IDs that apply, the project ID, timestamp with time zone, SDK version, and the complete redacted error response. State what you expected and what actually happened. For billing, include the payment reference and selected project; for a file problem, include the filename, size, and the operation that failed. Do not send API keys, passwords, custom-proxy credentials, session cookies, or active CDP/live-view URLs. A screenshot alone often omits the identifier needed to trace a request. # Webhooks Source: https://docs.browser-use.com/cloud/guides/webhooks Receive V2 task and V3 session notifications and verify the webhook signature. Set up webhooks at [cloud.browser-use.com/settings?tab=webhooks](https://cloud.browser-use.com/settings?tab=webhooks). ## Events | Event | API | When | | -------------------------- | --------- | ----------------------------------------------------- | | `agent.task.status_update` | V2 | Task status changes (`running`, `idle`, or `stopped`) | | `session.status.update` | V3 | Session status changes; inspect `payload.status` | | `test` | Dashboard | Webhook test ping | Match the event type to the API version you use. For V4 run monitoring, see [Observability](/cloud/agent/observability) and [polling guidance](/cloud/guides/concurrency#budget-http-requests-separately). ## Payload A V2 task event includes `task_id`, `session_id`, `status`, and task `metadata`: ```json theme={null} { "type": "agent.task.status_update", "timestamp": "2026-09-09T12:00:00Z", "payload": { "task_id": "task_abc123", "session_id": "session_xyz", "status": "idle", "metadata": {} } } ``` A V3 event uses the session's `id` as `payload.session_id`. `output` is included when available: ```json theme={null} { "type": "session.status.update", "timestamp": "2026-09-09T12:00:00Z", "payload": { "session_id": "session_xyz", "status": "idle", "output": "The agent's result" } } ``` Status-change events are not all completion events. Check the status before continuing your workflow. ## Signature verification Every webhook request includes two headers: * `X-Browser-Use-Signature`: lowercase hexadecimal HMAC-SHA256 signature. * `X-Browser-Use-Timestamp`: Unix timestamp in seconds when this delivery was sent. The signed message is the UTF-8 encoding of `{header_timestamp}.{canonical_payload}`. The canonical payload is exactly Python's `json.dumps(payload, sort_keys=True, separators=(',', ':'), ensure_ascii=True)`, applied to the **entire event**, including its `type`, `timestamp`, and `payload`. This is not a signature over the raw HTTP body. The sender serializes the HTTP body separately. Non-ASCII characters are escaped in the signed representation: for example, `München` becomes `M\u00fcnchen`. JavaScript `JSON.stringify`, even after sorting an object's keys, does not implement this format. Unicode escaping, integer-like object keys, and number formatting can differ from Python. Do not use a generic JavaScript JSON canonicalizer or raw-body HMAC verifier for this contract. The Python verifier below matches the current sender; other implementations must match its serialization exactly and preserve number representations when parsing. Save this as `webhook_verify.py`: ```python theme={null} import hashlib import hmac import json import re import time def verify_webhook(body: bytes, signature: str, timestamp: str, secret: str) -> bool: if not isinstance(timestamp, str) or not re.fullmatch(r"[0-9]{1,12}", timestamp): return False if abs(time.time() - int(timestamp)) > 300: return False if not isinstance(signature, str) or not re.fullmatch(r"[0-9a-f]{64}", signature): return False try: payload = json.loads(body) if not isinstance(payload, dict): return False canonical = json.dumps( payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True, allow_nan=False, ) except (ValueError, TypeError, UnicodeError): return False message = f"{timestamp}.{canonical}".encode("utf-8") expected = hmac.new(secret.encode("utf-8"), message, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature) ``` The five-minute timestamp window limits replay, but does not make delivery unique. Make your business action idempotent so a duplicate callback cannot process the same result twice. Keep your server clock synchronized. ## Example: FastAPI webhook handler Install `fastapi` and `uvicorn`, put this `app.py` beside `webhook_verify.py`, set `WEBHOOK_SECRET` to the signing secret from your webhook settings, and run `uvicorn app:app --port 3000`. ```python theme={null} import json import os from fastapi import FastAPI, HTTPException, Request from webhook_verify import verify_webhook app = FastAPI() WEBHOOK_SECRET = os.environ["WEBHOOK_SECRET"] @app.post("/webhook") async def handle_webhook(request: Request): body = await request.body() signature = request.headers.get("x-browser-use-signature", "") timestamp = request.headers.get("x-browser-use-timestamp", "") if not verify_webhook(body, signature, timestamp, WEBHOOK_SECRET): raise HTTPException(status_code=401, detail="Invalid webhook signature or timestamp") event = json.loads(body) if event.get("type") in {"agent.task.status_update", "session.status.update"}: data = event["payload"] # Persist or enqueue the verified event for your application to process. # Deduplicate before applying business side effects. print(f"Session {data['session_id']} is now {data['status']}") return {"status": "ok"} ``` Return promptly after durably accepting the event. The sender has a 10-second request timeout and retries temporary failures, so slow processing can cause duplicate deliveries. HTTP 400, 401, 403, 404, and 410 are not retried. For local development, expose your local server with a tool such as [ngrok](https://ngrok.com): `ngrok http 3000`. Set the resulting `/webhook` URL in the dashboard and use its test action before starting tasks. # x402 (pay-per-request) Source: https://docs.browser-use.com/cloud/guides/x402 Pay for Browser Use Cloud with crypto (USDC on Base). ~30 seconds from wallet to first request. [x402](https://www.x402.org) is a payment protocol [created by Coinbase](https://www.coinbase.com/developer-platform/discover/launches/x402) that lets APIs, or AI agents, charge for requests directly with crypto. x402 lets your code, or an autonomous AI agent, pay Browser Use Cloud directly with cryptocurrency. No account signup, no credit card, and no API key is needed. Your wallet is your identity. **New to crypto?** Here's the gist: * **USDC** is a stablecoin pegged 1:1 to the US dollar. 1 USDC = \$1. * **Base** is a low-fee blockchain network operated by Coinbase. Sending a payment costs fractions of a cent. * **Wallet** = a public address (your "username") and a private key (your "password"). The private key signs payments. * You'll need at least \$1 of USDC on Base in a wallet you control. The Claude Code quickstart below walks you through everything from scratch. **Four ways to start, ranked by laziness:** One command. Claude does the wallet setup, funding walkthrough, and verification for you. AgentCash or Coinbase's Agentic Wallet. Your coding agent gets a wallet directly and pays as it goes. One line in your Python or TypeScript app. Bring your own wallet. Skip the SDK. Sign EIP-3009, send `X-PAYMENT` header. ## Claude Code quickstart The fastest path. Install the [x402 skill](https://github.com/browser-use/browser-use/tree/main/skills/x402), and Claude walks you through everything: ```bash theme={null} npx skills add https://github.com/browser-use/browser-use --skill x402 ``` Then in Claude Code: ``` > /x402 ``` Claude walks you through creating or importing a wallet outside the chat, loading `BROWSER_USE_X402_PRIVATE_KEY` before Claude starts, installing the SDK, and running a verification task. Already have a Browser Use Cloud account? The skill detects this and switches to **top-up mode**, adding credits to that existing account instead of creating a new, wallet-keyed one. ## SDK quickstart The Browser Use SDK has built-in x402 support. Pass a wallet private key, and you're done. ```bash Python theme={null} pip install "browser-use-sdk[x402]" ``` ```bash TypeScript theme={null} npm install browser-use-sdk @x402/fetch @x402/evm viem ``` ```python Python theme={null} import asyncio from browser_use_sdk.v3 import AsyncBrowserUse async def main(): client = AsyncBrowserUse(x402_max_payment_usd=1.0) result = await client.run( "Go to example.com and tell me the heading.", max_cost_usd=0.75, ) print(result.output) asyncio.run(main()) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v3"; const client = new BrowserUse({ x402MaxPaymentUsd: 1 }); const result = await client.run("Go to example.com and tell me the heading.", { maxCostUsd: 0.75, }); console.log(result.output); ``` Or set `BROWSER_USE_X402_PRIVATE_KEY` in your env, and skip the constructor arg entirely: ```python Python theme={null} client = AsyncBrowserUse() # auto-detects from env ``` ```typescript TypeScript theme={null} const client = new BrowserUse(); // auto-detects from env ``` Python x402 is async-only: use `AsyncBrowserUse`, not `BrowserUse`. ## Raw HTTP quickstart Use this if you're in a language we don't ship an SDK for (Go, Rust, Ruby, etc.), or if you want to use other x402 APIs from the same client library. Hit `https://x402.api.browser-use.com` directly with any [x402 client library](https://github.com/coinbase/x402#all-available-reference-sdks): ```python theme={null} import asyncio import os from x402 import max_amount, x402Client from x402.http.clients import x402HttpxClient from x402.mechanisms.evm import EthAccountSigner from x402.mechanisms.evm.exact.register import register_exact_evm_client from eth_account import Account async def main(): client = x402Client() register_exact_evm_client( client, EthAccountSigner(Account.from_key(os.environ["BROWSER_USE_X402_PRIVATE_KEY"])), policies=[max_amount(1_000_000)], ) async with x402HttpxClient(client, timeout=120.0) as http: response = await http.post( "https://x402.api.browser-use.com/api/v3/sessions", json={"task": "..."}, ) print(response.status_code, response.text[:500]) asyncio.run(main()) ``` `https://x402.api.browser-use.com` exposes the same routes as `https://api.browser-use.com`. It supports every `/api/v2/*` and `/api/v3/*` route, gated by an x402 challenge instead of API key auth. ## What you need * **EVM wallet** (MetaMask, Rabby, Coinbase Wallet, etc.) with its private key available to your app * **USD Coin (USDC) on Base mainnet** * **SDK top-up:** `$1.00` USDC by default, with a configurable hard per-payment cap You do **not** need ETH for gas. We use [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009), so you sign offchain, and the facilitator pays gas. No wallet yet? Jump to [Wallet setup](#wallet-setup) below. ## Pricing and credits The SDK first authenticates requests with a free, single-use wallet signature. If the wallet project does not exist yet or has insufficient credits, the backend marks the response as requiring a top-up, and the SDK makes one x402 payment. The default SDK cap is `$1.00` USDC per payment. **Mid-task drain still terminates the task.** Browser Use sessions run on a worker that doesn't see x402, so once a long-running task starts and burns through its credits, it stops with `INSUFFICIENT_CREDITS` — it does not pause and wait for the next x402 payment. See the [pricing page](https://browser-use.com/pricing) for model and browser costs. ## Topping up an existing account If you already have a Browser Use API key from the dashboard, you can use x402 to add credits to **that** account instead of creating a new project based on your crypto wallet. Send your existing API key alongside the payment: ```python Python theme={null} import asyncio import os from browser_use_sdk.v3 import AsyncBrowserUse client = AsyncBrowserUse( api_key=os.environ["BROWSER_USE_API_KEY"], x402_max_payment_usd=1.0, # hard cap for each top-up base_url="https://x402.api.browser-use.com/api/v3", ) async def main(): result = await client.run("...", max_cost_usd=0.75) print(result.output) asyncio.run(main()) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v3"; const client = new BrowserUse({ apiKey: process.env.BROWSER_USE_API_KEY, x402MaxPaymentUsd: 1, baseUrl: "https://x402.api.browser-use.com/api/v3", }); const result = await client.run("...", { maxCostUsd: 0.75 }); ``` When the backend sees both a payment and a valid API key, the credit goes to the key's project rather than auto-creating a new wallet-keyed one. Useful for: * Agents that ran out of free-tier credits and need to keep going * Adding credits via crypto when you already have a regular Browser Use account * Multi-wallet setups funding one shared account ## Checking your credit balance When you sign up the normal way, Browser Use creates an **account** for you (we call it a "project") that holds your credits and runs your tasks, and you log into it with an API key. When you pay with **only a wallet** (no API key), there's no signup step — so the very first time you pay, Browser Use automatically creates one of these same accounts for you and ties it to your wallet. From then on it behaves exactly like a normal account. The only difference is how you prove it's yours: instead of an API key, you sign with your wallet. This balance is your **Browser Use credit balance** — the prepaid USD you've added to that account through x402 payments, minus what your tasks have spent. To check how much credit that account has left, use the method below: ```python Python theme={null} import asyncio import os from browser_use_sdk.v3 import get_wallet_balance async def main(): balance = await get_wallet_balance(os.environ["BROWSER_USE_X402_PRIVATE_KEY"]) print(balance["total_credits_usd"]) asyncio.run(main()) ``` ```typescript TypeScript theme={null} import { getWalletBalance } from "browser-use-sdk/v3"; const balance = await getWalletBalance(process.env.BROWSER_USE_X402_PRIVATE_KEY!); console.log(balance.total_credits_usd); ``` The response contains: | Field | Description | | ------------------------ | ------------------------------------------------------------------------------- | | `wallet` | The wallet address (lowercased) | | `project_id` | The account (project) tied to your wallet that the credits live in | | `total_credits_usd` | Your remaining Browser Use credit balance, in USD | | `additional_credits_usd` | Of that total, the portion added via x402 top-ups (excludes any plan allowance) | This is for accounts created from a wallet (the default x402 mode). If you're [topping up an existing account](#topping-up-an-existing-account), check that account's balance the normal way with your API key via `client.billing.account()`. A wallet that has never paid yet has no account, so the call returns `404` until the first payment. The SDK signs a fixed, server-defined message ([EIP-191](https://eips.ethereum.org/EIPS/eip-191), the same "Sign-In with Ethereum" mechanism) with your wallet's private key. The signature proves you control the address without moving any funds. The server recovers the signer, matches it to the wallet's project, and returns the balance. ## How it works Your code asks for something. If your Browser Use credit balance needs a top-up, the SDK authorizes up to your configured payment cap; otherwise it proceeds without moving wallet funds. A bit more detail: 1. Your code makes a request (e.g. "run this task"). 2. The SDK signs a free, request-bound wallet authentication message. 3. If the server explicitly requests a top-up, the SDK signs one capped payment and retries. 4. Coinbase moves the USDC on-chain, and we add the same amount to your project's credit balance. 5. Status polls use free wallet authentication while the task runs. ## Wallet setup If you don't have a wallet ready, here's an easy way to set one up using **MetaMask**. It's a popular crypto wallet. Any other EVM-compatible wallet works equally well: [Rabby](https://rabby.io), [Coinbase Wallet](https://www.coinbase.com/wallet), [Frame](https://frame.sh), [Trust Wallet](https://trustwallet.com), [Phantom](https://phantom.com), etc. Pick whichever you prefer. Get the [MetaMask browser extension](https://metamask.io) via the official site only. Create a new wallet, save the seed phrase somewhere offline, set a password. By default, most wallets only show Ethereum. You need to add **Base** (the network we accept payments on) so your wallet can hold USDC there. Click **"Buy"** inside MetaMask. Pick **USDC**, set network to **Base**, and pay with credit card, bank, etc. The USDC lands directly in your wallet. In MetaMask: click the account menu → **Account details** → **Private keys** → enter your password → copy. That string (starts with `0x`) is your `BROWSER_USE_X402_PRIVATE_KEY`. Other wallets have similar export options in their account settings. Wallets hold real money, and anyone with the private key can drain it. Be careful with your keys. ## Advanced: bring your own x402 client For custom signers, multi-network setups, or non-EVM wallets, build the x402 client yourself, and pass it as `x402` instead of `x402_private_key`: ```python Python theme={null} import os from x402 import max_amount, x402Client from x402.mechanisms.evm import EthAccountSigner from x402.mechanisms.evm.exact.register import register_exact_evm_client from eth_account import Account from browser_use_sdk.v3 import AsyncBrowserUse key = os.environ["BROWSER_USE_X402_PRIVATE_KEY"] x402 = x402Client() register_exact_evm_client( x402, EthAccountSigner(Account.from_key(key)), policies=[max_amount(1_000_000)], ) client = AsyncBrowserUse(x402=x402, x402_private_key=key) ``` ```typescript TypeScript theme={null} import { x402Client } from "@x402/fetch"; import { ExactEvmScheme } from "@x402/evm"; import { privateKeyToAccount } from "viem/accounts"; import { BrowserUse } from "browser-use-sdk/v3"; const key = process.env.BROWSER_USE_X402_PRIVATE_KEY! as `0x${string}`; const x402 = new x402Client(); x402.register("eip155:*", new ExactEvmScheme(privateKeyToAccount(key))); x402.registerPolicy((_version, requirements) => requirements.filter(({ amount }) => BigInt(amount) <= 1_000_000n), ); const client = new BrowserUse({ x402, x402PrivateKey: key }); ``` ## Troubleshooting Two likely causes: * **Wallet has no USDC on Base.** Check your balance. If empty, top it up. * **Your HTTP client isn't x402-aware.** Plain `requests` / `fetch` just sees a 402 and stops; it doesn't know how to read the payment instructions and sign a payment. Use the SDK (which handles this automatically), or wrap your HTTP client with one of the [x402 client libraries](https://github.com/coinbase/x402#all-available-reference-sdks). You haven't installed the optional x402 deps. Run `pip install "browser-use-sdk[x402]"` (Python) or `npm install @x402/fetch @x402/evm viem` (TypeScript). We verified your payment request but couldn't credit your project, so we deliberately did not settle on-chain. No USDC was moved, so just retry. This is rare. Wait a few seconds. Settlement and credit grant happen in the same request, but the response may be sent before the credit grant fully commits. If credits still show `$0` after a few minutes, contact support with your wallet address. (Conversely, if a payment settles but the request itself then fails, we automatically reclaim the credits so you aren't charged for nothing.) `eip155:8453` is Base mainnet; `eip155:84532` is Base Sepolia testnet. Browser Use Cloud only accepts mainnet. Withdrawing USDC to Sepolia from Coinbase is **not** the same as Base mainnet, even though both use the same wallet address. ## Related * [x402 protocol spec](https://www.x402.org) * [Agent wallets quickstart](/cloud/guides/x402-agent-wallets) — pay from AgentCash or a Coinbase Agentic Wallet * [Standard API key auth](/cloud/quickstart) — alternative if you don't want pay-per-use * [`x402` Claude Code skill source](https://github.com/browser-use/browser-use/tree/main/skills/x402) # x402 agent wallets Source: https://docs.browser-use.com/cloud/guides/x402-agent-wallets Pay for Browser Use Cloud from an agent-native wallet: AgentCash (local wallet, MCP) or Coinbase's Agentic Wallet (hosted wallet, CLI). Give your agent a wallet with a few dollars of USDC in it, and it can run Browser Use tasks and pay as it goes. Pick a wallet: A wallet that lives on your machine, built for coding agents: Claude Code, Cursor, Codex. A wallet Coinbase hosts for you. Sign in with email, fund with Apple Pay or card, pay from the terminal. Payments to Browser Use are **credit top-ups, not per-call fees**: each x402 payment (\$5 default, \$1 minimum) buys prepaid credit for a project tied to your wallet, and unused credit carries over. See [pricing and credits](/cloud/guides/x402#pricing-and-credits). ## AgentCash quickstart [AgentCash](https://agentcash.dev) gives your coding agent a wallet and the tools to spend from it: check prices, pay for API calls, watch the balance. The wallet is a file on your machine. ```bash theme={null} npx agentcash install ``` The interactive installer detects your agent client. Or add it directly: ```bash theme={null} claude mcp add agentcash --scope user -- npx -y agentcash@latest # Claude Code codex mcp add agentcash -- npx -y agentcash@latest # Codex npx agentcash install --client cursor # Cursor ``` The first run creates a wallet at `~/.agentcash/wallet.json`. Get its deposit address and send it USDC on **Base mainnet** (\$5 covers the default top-up; \$1 is the minimum): ```bash theme={null} npx agentcash@latest accounts ``` Or just ask your agent — the `list_accounts` and `get_balance` tools do the same thing. Prompt your agent: ```text theme={null} Use AgentCash to POST to https://x402.api.browser-use.com/api/v3/sessions with body {"task": "Go to example.com and tell me the page title"}. Pay the $1 minimum option, then poll the returned session id at GET /api/v3/sessions/{id} until status is "stopped" and show me the output. ``` The `fetch` tool handles the whole 402 → sign → retry loop. Use `check_endpoint_schema` first if you want to see the price and input schema without paying. ## Coinbase Agentic Wallet quickstart [Agentic Wallet](https://docs.cdp.coinbase.com/agentic-wallet/welcome) is a wallet Coinbase hosts for you, driven from the terminal with the [`awal`](https://www.npmjs.com/package/awal) CLI. Sign in with your email — no keys to manage — fund it with Apple Pay or a card, and pay for API calls with one command. The [agentic-wallet-skills](https://github.com/coinbase/agentic-wallet-skills) package teaches your coding agent the same commands. ```bash theme={null} npx skills add coinbase/agentic-wallet-skills # optional: teach your agent npx awal auth login you@example.com # sends an OTP to your email npx awal auth verify 123456 # paste the code npx awal status ``` ```bash theme={null} npx awal show # opens the wallet UI → Fund → Apple Pay / card / Coinbase npx awal balance ``` Or send USDC on Base directly to the address from `npx awal address`. ```bash theme={null} npx awal x402 details https://x402.api.browser-use.com/api/v3/sessions ``` shows our payment options (\$5 default / \$1 minimum, USDC on Base) and the task input schema, without paying. Then: ```bash theme={null} npx awal x402 pay https://x402.api.browser-use.com/api/v3/sessions \ -X POST -d '{"task": "Go to example.com and tell me the page title"}' \ --max-amount 5000000 --json ``` `--max-amount` is in atomic USDC units: `1000000` = \$1.00. The response contains the session `id`. Poll `https://x402.api.browser-use.com/api/v3/sessions/{id}` until `status` is `"stopped"`, then read `output`. ## Which one? | | AgentCash | Coinbase Agentic Wallet | | --------- | --------------------------------- | ------------------------------------------------------------------------------------------------- | | Wallet | Local file, self-custody | Coinbase-hosted, email OTP | | Funding | Send USDC on Base to your address | Onramp (Apple Pay / card / bank) or direct USDC | | Interface | MCP tools + CLI | CLI (+ skill for agents, [MCP variant](https://docs.cdp.coinbase.com/agentic-wallet/mcp/welcome)) | | Best for | Autonomous coding agents | Humans who want fiat funding; CDP-ecosystem apps | Building a production app instead of driving a CLI? Use [`CdpX402Client`](https://docs.cdp.coinbase.com/x402/quickstart-for-buyers) from the CDP SDK (a CDP-managed server wallet) or [bring your own x402 client](/cloud/guides/x402#advanced-bring-your-own-x402-client). ## Related * [x402 overview](/cloud/guides/x402) — SDK, Claude Code skill, raw HTTP, pricing, troubleshooting * [AgentCash docs](https://agentcash.dev) · [agentcash-skills](https://github.com/Merit-Systems/agentcash-skills) * [Agentic Wallet CLI quickstart](https://docs.cdp.coinbase.com/agentic-wallet/cli/quickstart) (Coinbase) # Agent (v2) Source: https://docs.browser-use.com/cloud/legacy/agent V2 agent models and file handling. ## Models | Model | API String | Cost per Step | | ------------------------- | ---------------------------- | ------------- | | Browser Use 2.0 (default) | `browser-use-2.0` | \$0.006 | | O3 | `o3` | \$0.03 | | Gemini Flash Latest | `gemini-flash-latest` | \$0.0075 | | Gemini Flash Lite Latest | `gemini-flash-lite-latest` | \$0.005 | | Claude Sonnet 4.5 | `claude-sonnet-4-5-20250929` | \$0.05 | | Claude Sonnet 4.6 | `claude-sonnet-4.6` | \$0.05 | Pass `llm` explicitly to select a model: ```python Python theme={null} result = await client.run("...", llm="browser-use-2.0") ``` ```typescript TypeScript theme={null} const result = await client.run("...", { llm: "browser-use-2.0" }); ``` *** ## Files Upload images, PDFs, documents, and text files (10 MB max) to sessions, and download output files from completed tasks. ### Upload a file Get a presigned URL, then upload via POST. ```python Python theme={null} import httpx from browser_use_sdk import AsyncBrowserUse client = AsyncBrowserUse() session = await client.sessions.create() upload = await client.files.session_url( session.id, file_name="input.pdf", content_type="application/pdf", size_bytes=1024, ) with open("input.pdf", "rb") as f: async with httpx.AsyncClient() as http: await http.post(upload.url, content=f.read(), headers={"Content-Type": "application/pdf"}) result = await client.run("Summarize the uploaded PDF", session_id=session.id) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk"; import { readFileSync } from "fs"; const client = new BrowserUse(); const session = await client.sessions.create(); const upload = await client.files.sessionUrl(session.id, { fileName: "input.pdf", contentType: "application/pdf", sizeBytes: 1024, }); await fetch(upload.url, { method: "POST", body: readFileSync("input.pdf"), headers: { "Content-Type": "application/pdf" }, }); const result = await client.run("Summarize the uploaded PDF", { sessionId: session.id }); ``` Presigned URLs expire after 120 seconds. Max file size: 10 MB. ### Download task output files ```python Python theme={null} result = await client.tasks.get(task_id) for file in result.output_files: output = await client.files.task_output(task_id, file.id) print(output.download_url) # download URL ``` ```typescript TypeScript theme={null} const result = await client.tasks.get(taskId); for (const file of result.outputFiles) { const output = await client.files.taskOutput(taskId, file.id); console.log(output.downloadUrl); } ``` *** ## Streaming steps Use `async for` to yield steps as the agent works. ```python Python theme={null} from browser_use_sdk import AsyncBrowserUse client = AsyncBrowserUse() run = client.run("Find the most upvoted post on Reddit r/technology today") async for step in run: print(f"Step {step.number}: {step.next_goal}") print(f" URL: {step.url}") print(run.result.output) # final result after iteration ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk"; const client = new BrowserUse(); const run = client.run("Find the most upvoted post on Reddit r/technology today"); for await (const step of run) { console.log(`Step ${step.number}: ${step.nextGoal}`); console.log(` URL: ${step.url}`); } console.log(run.result?.output); // final result after iteration ``` *** ## Key parameters | Parameter | Type | Description | | -------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------- | | `task` | `str` | What you want the agent to do. 1-50,000 characters. | | `llm` | `str` | Model override. Default: Browser Use 2.0. | | `output_schema` / `schema` | Pydantic / Zod | Schema for structured output. | | `session_id` | `str` | Reuse an existing session. Omit for auto-session. | | `start_url` | `str` | Initial page URL. Saves steps — send the agent directly there. | | `secrets` | `dict` | Domain-specific credentials. See [Authentication](/cloud/guides/authentication). | | `allowed_domains` | `list[str]` | Restrict agent to these domains only. | | `session_settings` | `SessionSettings` | Proxy, profile, browser config. See [Profiles](/cloud/guides/authentication). | | `flash_mode` | `bool` | Faster but less careful navigation. | | `thinking` | `bool` | Enable legacy agent thinking behavior. This does not set model reasoning depth. | | `thinkingLevel` (REST) | `str` | Provider reasoning depth. The generated SDK parameter is pending; see [Thinking levels](/cloud/agent/thinking-levels). | | `vision` | `bool \| str` | Enable screenshots for the agent. | | `highlight_elements` | `bool` | Highlight interactive elements on the page. | | `system_prompt_extension` | `str` | Append custom instructions to the system prompt. | | `judge` | `bool` | Enable quality judge to verify output. | | `skill_ids` | `list[str]` | Skills the agent can use during the task. | | `op_vault_id` | `str` | 1Password vault ID for auto-fill credentials and 2FA. | | `metadata` | `dict[str, str]` | Custom metadata attached to the task. | # Public share links (v2) Source: https://docs.browser-use.com/cloud/legacy/public-share Generate shareable URLs for agent sessions using the v2 API. Generate a public URL that anyone can open to watch the entire agent session — no API key needed. Useful for sharing with teammates, stakeholders, or embedding in dashboards. ```python Python theme={null} share = await client.sessions.create_share(session.id) print(share.share_url) ``` ```typescript TypeScript theme={null} const share = await client.sessions.createShare(session.id); console.log(share.shareUrl); ``` # Skills Source: https://docs.browser-use.com/cloud/legacy/skills Retired. Existing skills still run; new skills are no longer created. Skills are retired. You can no longer create or refine a skill: `skills.create`, `skills.refine`, and manual recording return `410 Gone`. Existing skills can still be viewed and run, and Marketplace skills can still be cloned and executed. For new work, use [rerunnable scripts](/cloud/agent/scripts). A Cloud agent learns the task once and saves the working code in its workspace, so every later run reuses it. ## Run an existing skill ```python Python theme={null} result = await client.skills.execute( skill_id, parameters={"X": 10}, ) print(result) ``` ```typescript TypeScript theme={null} const result = await client.skills.execute(skillId, { parameters: { X: 10 }, }); console.log(result); ``` ## Marketplace Browse, clone, and run community-created skills. Creating new skills is no longer available. ```python Python theme={null} skills = await client.marketplace.list() my_skill = await client.marketplace.clone(skill_id) result = await client.marketplace.execute(skill_id, parameters={...}) ``` ```typescript TypeScript theme={null} const skills = await client.marketplace.list(); const mySkill = await client.marketplace.clone(skillId); const result = await client.marketplace.execute(skillId, { parameters: { ... } }); ``` See [Pricing](https://browser-use.com/pricing) for skill costs. # Quick start Source: https://docs.browser-use.com/cloud/quickstart Run a hosted agent or launch a cloud browser. Give an agent a task and get the result. Launch a cloud browser and connect to it from your code. Both Cloud paths use API V4. Choose whether Browser Use drives the browser or your Playwright/Puppeteer code connects directly over CDP. For a local agent, use the [open-source library](/open-source/quickstart). Get an [API key](https://cloud.browser-use.com/settings?tab=api-keys\&new=1) and export it: ```bash theme={null} export BROWSER_USE_API_KEY=your_key ``` ## Install the SDK Use Python 3.10 or newer for the Python SDK. Skip installation if you use curl. ```bash Python theme={null} pip install --upgrade browser-use-sdk playwright ``` ```bash TypeScript theme={null} npm install browser-use-sdk@latest puppeteer-core ``` ## Run Browser Use Agents ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse with BrowserUse() as client: run = client.runs.create("Find the top Hacker News story") result = client.runs.wait_for_completion(run.id) print(result.result) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; const client = new BrowserUse(); const run = await client.runs.create({ task: "Find the top Hacker News story", }); const result = await client.runs.waitForCompletion(run.id); console.log(result.result); ``` ```bash curl theme={null} curl https://api.browser-use.com/api/v4/runs \ -H "X-Browser-Use-API-Key: $BROWSER_USE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"task":"Find the top Hacker News story"}' ``` ## Use Browser Infrastructure These examples connect to the managed browser's existing context and stop it in `finally`, including when connecting or navigating fails. ```python Python theme={null} from browser_use_sdk.v4 import BrowserUse from playwright.sync_api import sync_playwright with BrowserUse() as client: session = client.browsers.create(proxy_country_code="us") try: if not session.cdp_url: raise RuntimeError("The browser did not return a CDP URL") with sync_playwright() as p: browser = p.chromium.connect_over_cdp(session.cdp_url) context = browser.contexts[0] page = context.pages[0] if context.pages else context.new_page() page.goto("https://example.com") print(page.title()) finally: client.browsers.stop(session.id) ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v4"; import puppeteer from "puppeteer-core"; const client = new BrowserUse(); const session = await client.browsers.create({ proxyCountryCode: "us" }); try { if (!session.cdpUrl) throw new Error("The browser did not return a CDP URL"); const browser = await puppeteer.connect({ browserWSEndpoint: session.cdpUrl }); try { const context = browser.defaultBrowserContext(); const page = (await context.pages())[0] ?? await context.newPage(); await page.goto("https://example.com"); console.log(await page.title()); } finally { await browser.disconnect(); } } finally { await client.browsers.stop(session.id); } ``` Disconnecting CDP does not stop the managed browser. Use `client.browsers.stop(session.id)` as above, or call `PATCH /api/v4/browsers/{id}` with `{"action":"stop"}`. For curl and additional connection options, follow the [Browser Infrastructure quickstart](/cloud/browser/quickstart#launch-and-connect). Give your coding agent the compact API V4 context. # Chat UI Source: https://docs.browser-use.com/cloud/tutorials/chat-ui Full end-to-end example. Build a chat UI with live browser preview, follow-up tasks, recording, and streaming messages. Clone and run in minutes. Next.js + Browser Use SDK v3. This tutorial walks through the [chat-ui-example](https://github.com/browser-use/chat-ui-example) — a Next.js app that lets users chat with a Browser Use agent in real time. We focus on the SDK integration, not the UI components. The app has two pages: 1. **Home** — the user types a task, the app creates a session and sends the task. 2. **Session** — live browser preview, streaming messages, follow-ups, and recording download. All SDK calls live in a single file: `src/lib/api.ts`. ## Setup ```typescript api.ts theme={null} import { BrowserUse } from "browser-use-sdk/v3"; // Server-only — no NEXT_PUBLIC_ prefix, never exposed to the browser const apiKey = process.env.BROWSER_USE_API_KEY ?? ""; export const client = new BrowserUse({ apiKey }); ``` The API key uses `BROWSER_USE_API_KEY` (no `NEXT_PUBLIC_` prefix) so it stays server-side. All SDK calls go through [server actions](https://nextjs.org/docs/app/guides/forms) — never call the SDK directly from client components. *** ## 1. Create a session ```typescript actions.ts theme={null} "use server"; import { client } from "./api"; export async function createSession() { const session = await client.sessions.create({ keepAlive: true, enableRecording: true, }); return { id: session.id, liveUrl: session.liveUrl, status: session.status }; } ``` * **`keepAlive: true`** keeps the session open after each task so the user can send follow-ups (default is `false`). * **`enableRecording: true`** produces an MP4 video of the browser session. * **`liveUrl`** is returned immediately — no waiting or extra call needed. The home page creates the session, navigates to the session page (passing `liveUrl` and the initial task via URL params), and the session page takes over from there: ```typescript page.tsx theme={null} async function handleSend(message: string) { const session = await createSession(); router.push( `/session/${session.id}?liveUrl=${encodeURIComponent(session.liveUrl)}&task=${encodeURIComponent(message)}` ); } ``` *** ## 2. Stream messages with `for await` Instead of polling `sessions.get()` and `sessions.messages()` separately, use `client.run()` — it streams messages and resolves when the task completes: ```typescript session-context.tsx theme={null} const streamTask = useCallback(async (task: string) => { const run = client.run(task, { sessionId }); for await (const msg of run) { setMessages((prev) => [...prev, msg]); } // Iterator done — task reached terminal state setSession(run.result); }, [sessionId]); ``` The `for await` loop yields each message as it arrives. When the loop ends, `run.result` contains the final session state (status, output, etc.). No separate status polling needed. Wire it up in a `useEffect` to auto-run the initial task from URL params: ```typescript session-context.tsx theme={null} useEffect(() => { if (!initialTask) return; sendMessage(initialTask); }, []); ``` *** ## 3. Follow-up tasks Follow-ups call the same `streamTask` function — the stream already includes the user message, so no optimistic insert is needed: ```typescript session-context.tsx theme={null} const sendMessage = useCallback(async (task: string) => { await streamTask(task); }, [streamTask]); ``` The SDK auto-sets `keepAlive: true` when targeting an existing session, so follow-up tasks work without extra config. *** ## 4. Recording Fetch the MP4 URL after the session ends (recording was enabled in step 1): ```typescript session-context.tsx theme={null} useEffect(() => { if (!isTerminal) return; client.sessions.waitForRecording(sessionId).then((urls) => { if (urls.length) setRecordingUrls(urls); }); }, [isTerminal, sessionId]); ``` `waitForRecording` polls for up to 15 seconds and returns presigned MP4 download URLs. Returns an empty array if the agent answered without opening a browser. *** ## 5. Stop a task ```typescript actions.ts theme={null} export async function stopTask(id: string) { await client.sessions.stop(id, { strategy: "task" }); } ``` Using `strategy: "task"` stops only the current task, keeping the session alive for follow-ups. *** ## 6. Session page The session page consumes everything through a context provider: ```typescript session/[id]/page.tsx theme={null} function SessionPage() { const { session, turns, isBusy, isTerminal, recordingUrls, sendMessage, stopTask } = useSession(); return (
{/* Chat column */}
{/* Live browser view — liveUrl available from session creation */}
); } ``` *** ## Summary | Method | Purpose | | ------------------------------------ | ------------------------------------------------ | | `client.sessions.create()` | Create a session (returns `liveUrl` immediately) | | `client.run()` | Send a task and stream messages with `for await` | | `client.sessions.stop()` | Stop the current task | | `client.sessions.waitForRecording()` | Get MP4 recording URLs | # Grow Therapy provider search Source: https://docs.browser-use.com/cloud/tutorials/grow-therapy-compare Search Grow Therapy for therapists by location, insurance, and specialty — with cached reruns. This tutorial builds a provider search tool for [Grow Therapy](https://www.growtherapy.com) — a therapy marketplace that handles insurance credentialing for providers. We combine [structured output](/cloud/agent/structured-output) with [saved scripts](/cloud/agent/scripts) to build a fast, repeatable search pipeline. ## What you'll build A script that: 1. Searches Grow Therapy's provider directory with filters (location, insurance, specialty) 2. Extracts therapist profiles with ratings and availability 3. Caches a successful search flow for reruns with different locations and specialties *** ## Setup ```python Python theme={null} import asyncio import json from pydantic import BaseModel from browser_use_sdk.v3 import AsyncBrowserUse client = AsyncBrowserUse() ``` ```typescript TypeScript theme={null} import { BrowserUse } from "browser-use-sdk/v3"; import { z } from "zod"; const client = new BrowserUse(); ``` ## 1. Define the output schema ```python Python theme={null} class Provider(BaseModel): name: str title: str specialties: list[str] insurance_plans: list[str] rating: float | None = None next_available: str | None = None class ProviderSearch(BaseModel): providers: list[Provider] total_found: int | None = None location: str specialty: str ``` ```typescript TypeScript theme={null} const ProviderSearch = z.object({ providers: z.array(z.object({ name: z.string(), title: z.string(), specialties: z.array(z.string()), insurancePlans: z.array(z.string()), rating: z.number().nullable(), nextAvailable: z.string().nullable(), })), totalFound: z.number().nullable(), location: z.string(), specialty: z.string(), }); ``` ## 2. Create a workspace ```python Python theme={null} workspace = await client.workspaces.create(name="grow-therapy-search") ``` ```typescript TypeScript theme={null} const workspace = await client.workspaces.create({ name: "grow-therapy-search" }); ``` ## 3. Search for providers This tutorial uses **API V3** automatic script caching. Mark changing values with `@{{value}}`; plain `{{value}}` does not activate caching. Keep the rest of the task text and the workspace unchanged so later requests can find the same cached script. The first successful generation uses the agent and incurs normal usage charges. ```python Python theme={null} result = await client.run( "Go to growtherapy.com and search for therapists in @{{New York}} " "who specialize in @{{anxiety}} and accept insurance. " "Return the first 5 provider profiles as JSON.", workspace_id=str(workspace.id), output_schema=ProviderSearch, ) for p in result.output.providers: print(f"{p.name} ({p.title})") print(f" Specialties: {', '.join(p.specialties)}") print(f" Rating: {p.rating}") print(f" Next available: {p.next_available}") print() ``` ```typescript TypeScript theme={null} const result = await client.run( "Go to growtherapy.com and search for therapists in @{{New York}} " + "who specialize in @{{anxiety}} and accept insurance. " + "Return the first 5 provider profiles as JSON.", { workspaceId: workspace.id, schema: ProviderSearch }, ); for (const p of result.output.providers) { console.log(`${p.name} (${p.title})`); console.log(` Specialties: ${p.specialties.join(", ")}`); console.log(` Rating: ${p.rating}`); console.log(` Next available: ${p.nextAvailable}`); } ``` ## 4. Sweep across locations and specialties After a successful first run caches the search flow, change only the marked values. A successful cached script execution avoids agent LLM inference, but browser and network usage still apply. Cache misses, validation, and automatic repair can invoke the agent and incur LLM charges; a rerun is not a guarantee of zero LLM cost. ```python Python theme={null} locations = ["Los Angeles", "Chicago", "Houston", "Miami"] specialties = ["depression", "trauma", "ADHD"] for location in locations: for specialty in specialties: result = await client.run( f"Go to growtherapy.com and search for therapists in @{{{{{location}}}}} " f"who specialize in @{{{{{specialty}}}}} and accept insurance. " f"Return the first 5 provider profiles as JSON.", workspace_id=str(workspace.id), output_schema=ProviderSearch, ) count = len(result.output.providers) print(f"{location} / {specialty}: {count} providers found") ``` ```typescript TypeScript theme={null} const locations = ["Los Angeles", "Chicago", "Houston", "Miami"]; const specialties = ["depression", "trauma", "ADHD"]; for (const location of locations) { for (const specialty of specialties) { const result = await client.run( `Go to growtherapy.com and search for therapists in @{{${location}}} ` + `who specialize in @{{${specialty}}} and accept insurance. ` + `Return the first 5 provider profiles as JSON.`, { workspaceId: workspace.id, schema: ProviderSearch }, ); console.log(`${location} / ${specialty}: ${result.output.providers.length} providers`); } } ``` *** ## Summary | Step | What happens | Cost | | --------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------ | | First search or cache miss | Agent creates and validates the cached flow | Normal agent, browser, and network charges | | Successful cached execution | Script reruns with new parameters | No agent LLM inference for script execution; browser and network charges apply | | Validation or repair | Agent validates or repairs a flow that needs attention | LLM usage plus browser and network charges | Therapy platforms have dynamic UIs that can change frequently. V3 can automatically repair a cached flow when it fails. For new V4 integrations, use [saved scripts](/cloud/agent/scripts); the V3 `@{{value}}` convention is specific to this tutorial’s API version. ## Next steps * [Structured output](/cloud/agent/structured-output) — Learn more about extracting typed data with Pydantic and Zod schemas. * [Human in the loop](/cloud/agent/human-in-the-loop) — Let a human review or interact with the browser mid-task, useful for auth flows or approving results before continuing. * [Scripts](/cloud/agent/scripts) — Save, reuse, and repair browser workflows. # Claude Code Source: https://docs.browser-use.com/cloud/tutorials/integrations/claude-code Give Claude Code cloud browser automation with Browser Use. [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) is Anthropic's agentic coding tool that runs in the terminal. Add Browser Use and it gets full cloud browser automation — anti-detect profiles, CAPTCHA solving, residential proxies in 195+ countries, persistent profiles, and stealth browsing. ## Setup **1. Install the CLI** ```bash theme={null} uv tool install browser-use ``` **2. Verify the installation** ```bash theme={null} browser-use doctor ``` **3. Register the skill** Register the Browser Use skill with the installed CLI: ```bash theme={null} browser-use skill install ``` **4. Authenticate for cloud browsers** Create an [API key](https://cloud.browser-use.com/settings?tab=api-keys\&new=1), then authenticate: ```bash theme={null} browser-use auth login ``` **5. Use it** Claude Code uses its bash tool to run CLI commands directly: ``` > Use browser-use to open github.com/trending and summarize the top repos ``` For the complete CLI reference and Python execution examples, see the [Browser Use CLI docs](https://docs.browser-use.com/open-source/browser-use-cli). # Claude Managed Agents Source: https://docs.browser-use.com/cloud/tutorials/integrations/claude-managed-agents Give Anthropic's Claude Managed Agents a stealth cloud browser via the Browser Use CLI. [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents) run on Anthropic's hosted platform. Install the `browser-use` CLI in the agent's environment and it can drive a stealth cloud browser — with proxies, CAPTCHA solving, live view, and recording. Your API key stays in a credential vault; the model never sees it. The sandbox can't run a local browser, so the agent starts a named Browser Use Cloud browser and drives it with `browser-use <<'PY'` Python snippets. ## 1. Create an environment Pre-install the CLI so it's ready at session start (no runtime install). ```yaml theme={null} name: browser-env config: type: cloud packages: pip: - browser-use networking: type: limited allowed_hosts: ["*.browser-use.com"] allow_package_managers: true ``` ## 2. Create a credential vault Store your key as an environment variable so the CLI reads it and the model never does. Get one at [cloud.browser-use.com/settings](https://cloud.browser-use.com/settings?tab=api-keys\&new=1). | Field | Value | | ----- | --------------------- | | Type | Environment variable | | Name | `BROWSER_USE_API_KEY` | | Value | `bu_...` | ## 3. Create the agent Tell it to use the CLI in cloud mode. ```yaml theme={null} name: browser agent model: id: claude-opus-4-8 description: Drives a stealth cloud browser with the Browser Use CLI. system: | You are a browser agent. Use the `browser-use` CLI to complete web tasks. Never launch a local browser in this sandbox. Start a named cloud browser: browser-use <<'PY' start_remote_daemon("managed") PY Then run browser work through the same name: BU_NAME=managed browser-use <<'PY' new_tab("https://example.com") print(page_info()) PY Your BROWSER_USE_API_KEY is in the environment; never print it. tools: - type: agent_toolset_20260401 # shell access so the agent can run the CLI default_config: enabled: true permission_policy: type: always_allow ``` ## 4. Start a session and send a task The Console only observes; kick the agent off with a `user.message` event. ```bash theme={null} curl -sS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "anthropic-beta: managed-agents-2026-04-01" \ -H "content-type: application/json" \ -d '{"events":[{"type":"user.message","content":[{"type":"text", "text":"Get the top 5 Hacker News stories with their links."}]}]}' ``` ## 5. Watch it run The agent starts a named cloud browser, runs Python helper snippets through `browser-use`, then returns the result. The session shows up in [cloud.browser-use.com](https://cloud.browser-use.com) → **Remote Browsers** with a **Live View** and an **mp4 recording**. Always use a cloud browser — the Managed Agents sandbox has no GUI, so a local browser won't start. Cloud mode also gives you stealth, residential proxies, live view, and recording. # Hermes Agent Source: https://docs.browser-use.com/cloud/tutorials/integrations/hermes-agent Give Hermes Agent cloud browser automation with Browser Use. [Hermes Agent](https://github.com/nousresearch/hermes-agent) is an open-source, self-improving AI agent by Nous Research. It has built-in browser automation tools that work with local Chromium out of the box. Add Browser Use and those tools run on cloud browsers with anti-detect profiles, residential proxies in 195+ countries, and stealth browsing. Two ways to set it up: configure Browser Use as Hermes's cloud browser backend, or install the Browser Use CLI and let Hermes drive it directly. ## Option 1: Cloud Browser Backend Hermes has built-in browser tools (`browser_navigate`, `browser_click`, `browser_snapshot`, etc.) that default to local Chromium. Point them at Browser Use cloud browsers instead — no extra dependencies, same Hermes experience. ### Setup **1. Get your API key** Create an API key from [Settings → API Keys](https://cloud.browser-use.com/settings?tab=api-keys\&new=1). Running Hermes without a human? Use [x402 pay-per-request](/cloud/guides/x402) instead; it needs no account or API key. **2. Configure Hermes** Run the setup wizard: ```bash theme={null} hermes setup tools ``` Select **Browser Automation**, then **Browser Use**, and paste your API key when prompted. Or configure manually — add your key to `~/.hermes/.env`: ```bash theme={null} BROWSER_USE_API_KEY=your_key_here ``` And set the provider in `~/.hermes/config.yaml`: ```yaml theme={null} browser: cloud_provider: browser-use ``` **3. Use it** Just chat with Hermes — any browsing tasks automatically route through Browser Use cloud browsers: ``` > Find the top trending repositories on GitHub today and summarize them ``` ## Option 2: Browser Use CLI The [Browser Use CLI](https://docs.browser-use.com/open-source/browser-use-cli) is a standalone tool that gives Hermes browser automation through terminal commands. Hermes drives the browser directly via its terminal tool, using Browser Harness and Python helpers through the `browser-use` command. ### Setup **1. Install the CLI** ```bash theme={null} uv tool install browser-use ``` **2. Verify the installation** ```bash theme={null} browser-use doctor ``` **3. Register the skill** Register the Browser Use skill with the installed CLI: ```bash theme={null} browser-use skill install ``` Or ask Hermes directly in chat to install it. **4. Authenticate for cloud browsers** Authenticate with your API key: ```bash theme={null} browser-use auth login ``` **5. Use it** Once the skill is loaded, Hermes can drive the browser through CLI commands via its terminal tool: ``` > Use browser-use to open github.com/trending and summarize the top repos ``` For the complete CLI reference and Python execution examples, see the [Browser Use CLI docs](https://docs.browser-use.com/open-source/browser-use-cli). # n8n Source: https://docs.browser-use.com/cloud/tutorials/integrations/n8n Use Browser Use as an HTTP node in n8n workflows. Browser Use works with [n8n](https://n8n.io) as a standard HTTP integration — no custom nodes needed. ## 1. Create a credential In n8n, go to **Credentials → Add Credential → Header Auth** and set: | Field | Value | | ----- | ------------------------------------------ | | Name | `X-Browser-Use-API-Key` | | Value | `YOUR_API_KEY` (without a `Bearer` prefix) | Get your API key at [cloud.browser-use.com/settings](https://cloud.browser-use.com/settings?tab=api-keys\&new=1). ## 2. Start a session Add an **HTTP Request** node: | Setting | Value | | -------------- | --------------------------------------------- | | Method | `POST` | | URL | `https://api.browser-use.com/api/v3/sessions` | | Authentication | Header Auth (from step 1) | | Body Type | JSON | Body: ```json theme={null} { "task": "Find the top 3 trending repos on GitHub today" } ``` The response includes an `id` you'll use to poll for results. Save it in your workflow so later nodes can still reference this session. ## 3. Poll for completion Add a second **HTTP Request** node in a loop: | Setting | Value | | -------------- | ------------------------------------------------------------ | | Method | `GET` | | URL | `https://api.browser-use.com/api/v3/sessions/{{ $json.id }}` | | Authentication | Header Auth (from step 1) | Check the `status` field. The session is done when status is `idle`, `stopped`, `error`, or `timed_out`. Use an **If** node to loop back with a **Wait** node (5–10 seconds) until complete. The final response contains `output` with the agent's result. ## Event-driven alternative Instead of polling, use [Webhooks](/cloud/guides/webhooks) to receive status changes. Configure your webhook endpoint in the [dashboard](https://cloud.browser-use.com/settings?tab=webhooks), then add a **Webhook** trigger node in n8n to receive V3 `session.status.update` events. Match `payload.session_id` to the `id` saved from the create response, then check `payload.status` for completion. The `agent.task.status_update` event belongs to V2 tasks. ```json theme={null} { "type": "session.status.update", "timestamp": "2026-09-09T12:00:00Z", "payload": { "session_id": "the-id-from-your-create-response", "status": "idle", "output": "The agent's result" } } ``` Validate the signature before accepting a webhook. See the [verification example](/cloud/guides/webhooks#signature-verification); use a verified receiver in front of n8n if your workflow cannot perform that verification itself. This pattern works with any workflow tool that supports HTTP requests — Make, Zapier, Pipedream, or custom orchestrators. # OpenClaw Source: https://docs.browser-use.com/cloud/tutorials/integrations/openclaw Give OpenClaw agents browser automation with Browser Use — via CDP or the CLI skill. [OpenClaw](https://openclaw.ai) is a self-hosted gateway that connects chat apps like WhatsApp, Telegram, and Discord to AI coding agents. Add Browser Use and those agents get full browser automation — anti-detect profiles, CAPTCHA solving, residential proxies in 195+ countries, and stealth browsing out of the box. Two ways to set it up: connect a Browser Use cloud browser to OpenClaw's native browser tool via CDP, or install the Browser Use CLI as a skill. ## Option 1: Cloud Browser via CDP OpenClaw has a built-in browser tool with its own CLI commands (`openclaw browser`). By default, it controls a local Chromium instance. You can point it at a Browser Use cloud browser instead by configuring a remote CDP profile. Browser Use exposes a WebSocket CDP URL. OpenClaw connects to it like any remote browser — no SDK or extra dependencies needed. ### Setup **1. Get your API key** Sign up at [cloud.browser-use.com](https://cloud.browser-use.com) and copy your API key from [Settings → API Keys](https://cloud.browser-use.com/settings?tab=api-keys\&new=1). **2. Add a Browser Use profile** Open `~/.openclaw/openclaw.json` and add a `browser-use` profile: ```json5 theme={null} { browser: { enabled: true, defaultProfile: "browser-use", remoteCdpTimeoutMs: 3000, remoteCdpHandshakeTimeoutMs: 5000, profiles: { "browser-use": { cdpUrl: "wss://connect.browser-use.com?apiKey=&proxyCountryCode=us", color: "#ff750e", }, }, }, } ``` Replace `` with your actual key. All Browser Use session parameters can be passed as query params in the `cdpUrl`: * `timeout` — session duration in minutes (max 240) * `profileId` — load a saved browser profile with persistent cookies and localStorage * `proxyCountryCode` — route traffic through a specific country (e.g. `us`, `de`, `jp`) **3. Use it** OpenClaw's browser commands now run against a Browser Use cloud browser: ```bash theme={null} openclaw browser --browser-profile browser-use open https://example.com openclaw browser --browser-profile browser-use snapshot openclaw browser --browser-profile browser-use screenshot ``` If you set `defaultProfile` to `"browser-use"` in the config (as shown above), you can drop the `--browser-profile` flag: ```bash theme={null} openclaw browser open https://example.com openclaw browser snapshot openclaw browser screenshot ``` ## Option 2: Browser Use CLI The Browser Use CLI is a standalone tool that gives any OpenClaw agent browser automation through a SKILL.md file. The agent reads the skill and learns to use the CLI commands directly. It's available on [skills.sh](https://skills.sh/browser-use/browser-use/browser-use) and [ClawHub](https://clawhub.ai/ShawnPana/browser-use). ### Setup **1. Install the CLI** ```bash theme={null} uv tool install browser-use ``` **2. Verify the installation** ```bash theme={null} browser-use doctor ``` **3. Set up the agent** Paste this setup prompt into your OpenClaw agent: ```text theme={null} Install or upgrade browser-use with `uv tool install --python 3.12 --upgrade --force 'browser-use @ git+https://github.com/browser-use/browser-use.git'`, run `browser-use skill install`, and connect it to my browser. Follow https://github.com/browser-use/browser-use if setup or connection fails. ``` Once the skill is loaded, OpenClaw agents can use the `browser-use` CLI to drive pages through Browser Harness and Python helpers. For the complete CLI reference, see the [Browser Use CLI docs](https://docs.browser-use.com/open-source/browser-use-cli). # Prompt for Vibecoders Source: https://docs.browser-use.com/cloud/vibecoding Current Cloud documentation, API schemas, concurrency, and billing guidance for AI coding agents. Give your coding agent (Cursor, Claude Code, Windsurf, or another tool) the current documentation index: ```text theme={null} https://docs.browser-use.com/llms.txt ``` Ask it to use the **Cloud** pages and **API V4** for a new hosted agent integration. The open-source library has a separate API. The index links to Markdown pages and API specifications. For a single bundle of documentation, use [llms-full.txt](https://docs.browser-use.com/.well-known/llms-full.txt). These managed exports include guidance on [concurrency](/cloud/guides/concurrency) and [billing](/cloud/guides/billing). Full bundles can be cached for up to 24 hours. For the freshest guidance, have your agent follow the index to the linked `.md` pages. Use these managed exports instead of the older `/cloud/llms*.txt` or `/open-source/llms*.txt` URLs. If you need a full snapshot immediately after a docs update, fetch the bundle with a fresh query value: ```bash theme={null} curl --fail --location \ "https://docs.browser-use.com/llms-full.txt?updated=$(date +%s)" \ --output browser-use-docs.txt ``` This requests a fresh cache entry; it does not update a copy your coding agent already downloaded. Replace that copy or ask the agent to fetch it again. # Which Browser Use do I need? Source: https://docs.browser-use.com/cloud/which-product Run Browser Use on your machine, run the full stack in our cloud, or bring your own agent and use only the hosted browser. Pick where your agent and browser should run. On your computer, choose Claude Code, Codex, OpenCode or Hermes Agent, then Playwright CLI or Browser Use CLI. Either CLI can use real Chrome or the single Cloud Browser. A separate Profile Sync step copies selected cookies from real Chrome to a cloud profile. Hosted V4 runs OpenCode, Browser Use CLI and Cloud Browser: send a prompt, get a result. On your computer, choose Claude Code, Codex, OpenCode or Hermes Agent, then Playwright CLI or Browser Use CLI. Either CLI can use real Chrome or the single Cloud Browser. A separate Profile Sync step copies selected cookies from real Chrome to a cloud profile. Hosted V4 runs OpenCode, Browser Use CLI and Cloud Browser: send a prompt, get a result. ## On your computer Claude Code, Codex, OpenCode, Hermes Agent, or another local agent can run the [Browser Use CLI](https://docs.browser-use.com/open-source/browser-use-cli) on your computer. Local computer-use CLIs such as Playwright CLI and Agent Browser fit the same shape. They can control real Chrome, with its tabs and logins, or use the single hosted browser shown in our cloud. Start with real Chrome. Connect to our cloud when a site blocks you or you need many browsers at once. Run `browser-use auth login` once before the first cloud connection. [Profile Sync](/cloud/guides/profile-sync) copies selected cookies from real Chrome into a cloud profile. Run it explicitly for the accounts you want to use in the cloud; the dashed line does not mean automatic synchronization. ## In our cloud [Cloud v4](/cloud/quickstart) runs OpenCode, Browser Use CLI and Cloud Browser. You send a prompt to our API and get the result back, plus a live preview and any files it produced. Nothing runs on your computer. Use the full hosted stack for scheduled work, tasks your users trigger, and work that should keep running without your computer. ## Only the browser Use the [Browser API](/cloud/browser/quickstart) when you only need the hosted browser. Browser Use CLI or a custom computer-use CLI can connect to the same Cloud Browser from your computer or your own cloud; Browser API does not add a hosted agent. One API key from [cloud.browser-use.com/settings](https://cloud.browser-use.com/settings?tab=api-keys\&new=1) works for all three. Credits and profiles live in the same project. # Browser Use CLI Source: https://docs.browser-use.com/open-source/browser-use-cli Direct browser control for coding agents. The Browser Use CLI (`browser-use`) gives coding agents a direct browser-control surface backed by [Browser Harness](https://github.com/browser-use/browser-harness): * **Direct browser control** — agents run Python to do actions in the browser. * **Three browser modes** — you can use with local Chrome or Chromium with your existing tabs, cookies, extensions, and logins; Browser Use cloud browsers; or any browser reachable through a CDP endpoint. * **Agent-ready setup** — install the skill into Claude Code, Codex, and other coding agents so they know when and how to call the CLI. Try out Browser Use CLI with an agent in [Browser Use Cloud](https://cloud.browser-use.com?utm_source=docs\&utm_medium=browser-use-cli\&utm_campaign=v4), or install the skill to try it yourself locally. ## Install the CLI ```bash theme={null} uv tool install browser-use browser-use --help ``` For one-off runs without a permanent tool install, use `uvx browser-use`. ## Set Up Your Agent Paste this setup prompt into Claude Code, Codex, or another coding agent: ```text theme={null} Install or upgrade browser-use with `uv tool install --python 3.12 --upgrade --force 'browser-use @ git+https://github.com/browser-use/browser-use.git'`, run `browser-use skill install`, and connect it to my browser. Follow https://github.com/browser-use/browser-use if setup or connection fails. ``` The skill is discoverable from [skills.sh](https://skills.sh/browser-use/browser-use/browser-use). ## Use Python Directly Pass Python to the CLI. Use `uvx browser-use` for one-off runs, or `browser-use` if you installed it: ```bash theme={null} uvx browser-use <<'PY' new_tab("https://example.com") print(page_info()) PY ``` The `<<'PY'` form is for Unix-compatible shells such as bash, zsh, Git Bash, or WSL. In PowerShell, pipe a here-string instead: ```powershell theme={null} @' new_tab("https://example.com") print(page_info()) '@ | uvx browser-use ``` ## Local Browser Setup The default local flow attaches to your running Chrome or Chromium through CDP. This preserves the browser state the user already has locally: open tabs, cookies, extensions, and logged-in sessions. It is the right default for desktop work where the user can approve Chrome's remote-debugging prompt. You can also point the CLI at any existing CDP browser by setting `BU_CDP_URL` or `BU_CDP_WS` before running `browser-use`. Use that for managed Chrome instances, Playwright-launched browsers, or infrastructure that already exposes a DevTools endpoint. If the CLI cannot connect, run: ```bash theme={null} browser-use --doctor ``` If Chrome asks whether to allow remote debugging, approve it and rerun the command. ## Cloud Browsers Use Browser Use cloud browsers when the agent runs on a headless machine, needs an isolated browser, needs parallel browser sessions, or needs Browser Use Cloud features such as persistent cloud profiles, proxy routing, CAPTCHA handling, and live browser viewing. The Browser Harness-backed CLI keeps cloud sessions explicit. You authenticate once, start a named cloud browser, then use that name for later commands: ```bash theme={null} browser-use auth login ``` ```bash theme={null} browser-use <<'PY' start_remote_daemon("work") PY BU_NAME=work browser-use <<'PY' new_tab("https://example.com") print(page_info()) PY ``` Use short, task-specific names when you have multiple agents or sub-agents running in parallel. Each name maps to its own remote browser daemon. Remote browsers bill until they stop or time out. When the task is done, ask whether to close the browser; if yes, run: ```bash theme={null} BU_NAME=work browser-use <<'PY' stop_remote_daemon("work") PY ``` ## Useful Commands ```bash theme={null} browser-use --help browser-use --doctor browser-use auth login browser-use auth status browser-use skill show browser-use telemetry status ``` # Browser Use Terminal Source: https://docs.browser-use.com/open-source/browser-use-terminal Use the terminal app for browser agents, or use the same runtime from Python. Browser Use Terminal is a Codex-style assistant for the browser. Coding assistants like Claude Code and Codex work well because they give the model autonomy and a real environment to operate in. They provide tools, files, shell commands, history, resumable sessions, and a terminal UI where you can watch and steer the work. Browser Use Terminal brings that pattern to browser automation. It gives an AI agent its own browser environment: it can control Chrome, see screenshots, inspect pages, run page JavaScript, save files, use browser profiles, handle secrets, and keep a history of what happened. Learn more on the [Browser Use Terminal site](https://browser-use.com/terminal?utm_source=docs) or view the [Terminal GitHub repo](https://github.com/browser-use/terminal?utm_source=docs). In practice, this means you can ask for browser work from your terminal: ```text theme={null} Find the cancellation policy for my current hotel reservation. ``` ```text theme={null} Give this employee admin permission in Azure. ``` ```text theme={null} Research the top Hacker News stories and summarize the points. ``` You can use Browser Use Terminal in four ways: | Surface | What it is for | | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **TUI** | The interactive terminal app you launch with `browser`. Best for normal, hands-on use. | | **CLI** | Scriptable commands such as `browser-use-terminal run-openai`. Best for automation and one-off tasks. | | **Python** | `browser_use.beta.Agent`, backed by the same Rust runtime. Best when you want browser-use code with terminal runtime behavior. | | **Coding assistants** | A skill plus the `browser-use-terminal browser` commands let Claude Code, Codex, OpenCode, and other agents drive the browser. See [Use From Coding Assistants](#use-from-coding-assistants). | Browser Use Terminal is separate from the lower-level `browser-use` CLI. Use `browser` for the Terminal TUI, `browser-use-terminal` for Terminal task commands, and `browser-use` for direct browser-control commands. ## Terms * **TUI**: terminal user interface. This is the interactive app you open with `browser`. * **CLI**: command-line interface. These are scriptable commands such as `browser-use-terminal run-openai`. * **Headless browser**: a browser that runs without a visible window. * **CDP**: Chrome DevTools Protocol. This lets Browser Use connect to an existing Chrome instance. * **MCP**: Model Context Protocol. This lets the agent use tools from external MCP servers. * **TOTP**: time-based one-time password. This is the six-digit code generated by authenticator apps for 2FA. ## Install Install Browser Use Terminal: ```bash theme={null} curl -fsSL https://browser-use.com/terminal/install.sh | sh ``` Restart your shell or source your shell profile if the installer updates your `PATH`. Verify the two terminal executables: ```bash theme={null} which browser which browser-use-terminal browser-use-terminal --help ``` Open the TUI: ```bash theme={null} browser ``` Set up Python usage in the same project where you will write your agent code: ```bash theme={null} uv venv --python 3.12 source .venv/bin/activate uv pip install browser-use ``` ## TUI Commands In the TUI, type `/` to open the command palette. These are the main commands: | Command | What it does | | ------------------- | ----------------------------------------------------------------- | | `/task` | Start a new task. | | `/model` | Choose the model and provider. | | `/auth` | Sign in to a provider. | | `/browser` | Change the browser backend. | | `/profile` | Choose the default Chrome profile. | | `/history` | Browse previous tasks. | | `/context` | Inspect context window attribution. | | `/secrets` | Save passwords and 2FA setup keys for website logins. | | `/import-passwords` | Import saved logins from 1Password. | | `/domains` | Allow or block which sites the agent can visit. | | `/sync-cookies` | Sync local cookies. | | `/email` | Give the agent a disposable inbox for sign-ups, links, and codes. | | `/goal` | Set or view the goal for a long-running task. | | `/feedback` | Report a bug or share feedback. | | `/update` | Install the latest release. | | `/reload` | Restart the UI in this terminal. | | `/exit` | Quit Browser Use Terminal. | ## Run Browser Tasks ### TUI The TUI is the main Browser Use Terminal experience: ```bash theme={null} browser ``` Type a browser task. You can watch the agent work, interrupt it, steer it, and continue the same session later. ```text theme={null} Open my company's dashboard and summarize failed jobs from today. ``` ```text theme={null} Log in to the vendor portal and download the latest invoice. ``` ### CLI Use one-shot commands for scripts, automation, and quick tasks: ```bash theme={null} browser-use-terminal --help ``` ```bash theme={null} browser-use-terminal run-openai --model gpt-5.5 \ "Go to https://news.ycombinator.com and return the top stories with points" ``` Other provider commands: ```bash theme={null} browser-use-terminal run-anthropic --model claude-sonnet-4-6 "Research Browser Use" browser-use-terminal run-openrouter --model openai/gpt-5.5 "Research Browser Use" browser-use-terminal run-deepseek --model deepseek-v4-pro "Research Browser Use" ``` ### Python Use `browser_use.beta.Agent` to run Python code on top of the Rust terminal runtime: ```bash theme={null} uv pip install browser-use ``` ```python theme={null} import asyncio from browser_use.beta import Agent, BrowserProfile, ChatOpenAI async def main(): agent = Agent( task="Go to https://news.ycombinator.com and return the top stories with points.", llm=ChatOpenAI(model="gpt-5.5"), browser_profile=BrowserProfile( headless=True, window_size={"width": 1440, "height": 900}, allowed_domains=["news.ycombinator.com"], ), ) history = await agent.run(max_steps=12) print(history.final_result()) asyncio.run(main()) ``` ## Use From Coding Assistants Browser Use Terminal plugs into any coding assistant that can run shell commands. A skill teaches the assistant the CLI, and the CLI gives it the full browser runtime: page interaction through Python helpers, screenshots saved as files, profiles, and more. The fastest setup is to paste this URL into your assistant and let it bootstrap everything (install, skill registration, browser setup, verification): ```text theme={null} https://browser-use.com/skill ``` To set it up manually instead: ```bash theme={null} curl -fsSL https://browser-use.com/terminal/install.sh | sh browser-use-terminal skill install ``` `skill install` writes the skill into every detected assistant home: `~/.claude/skills/` (Claude Code, also read by OpenCode), `~/.codex/skills/` (Codex), `~/.config/opencode/skills/`, and `~/.agents/skills/`. For other assistants, persist the output of `browser-use-terminal skill show` wherever that assistant reads instructions. The assistant then drives the browser with commands: ```bash theme={null} browser-use-terminal browser exec <<'PY' new_tab("https://example.com") wait_for_load() print(page_info()["title"]) print(capture_screenshot()) PY ``` How it works: * The browser auto-connects per the remembered preference (`browser-use-terminal browser preference use local|cloud|managed-headless`) and persists across invocations; Python variables do not. * Screenshots are saved as files and the absolute path is printed, so assistants view them with their native file tools: Claude Code `Read`, Codex `view_image`, OpenCode `read`, Gemini CLI `read_file`. * `--session ` gives parallel workstreams isolated artifact dirs, event logs, and managed browsers. * Stop persistent browsers with `browser-use-terminal browser recover stop-owned-browser` (managed) or `browser-use-terminal browser recover stop-owned-remote` (cloud). * Everything is recorded in the same event log as the TUI: `browser-use-terminal events browser-cli-`. ## Configure Models ### TUI Use `/auth` to sign in and configure credentials, then `/model` to choose the model: ```text theme={null} /auth /model ``` This is the recommended setup path for normal interactive use. ### CLI For one-shot commands and CI, set the provider key for the provider command you are using: ```bash theme={null} export OPENAI_API_KEY=... export ANTHROPIC_API_KEY=... export OPENROUTER_API_KEY=... export DEEPSEEK_API_KEY=... export BROWSER_USE_API_KEY=... ``` Then choose a provider command and model: ```bash theme={null} browser-use-terminal run-openai --model gpt-5.5 "Research Browser Use" browser-use-terminal run-anthropic --model claude-sonnet-4-6 "Research Browser Use" browser-use-terminal run-openrouter --model openai/gpt-5.5 "Research Browser Use" browser-use-terminal run-deepseek --model deepseek-v4-pro "Research Browser Use" ``` ### Python Pass a supported browser-use model class to `Agent`: ```python theme={null} from browser_use.beta import Agent, ChatAnthropic agent = Agent( task="Find the latest Browser Use docs update", llm=ChatAnthropic(model="claude-sonnet-4-6"), ) ``` Supported Python model classes: | Python model class | Example | | ------------------ | ------------------------------------------ | | `ChatOpenAI` | `ChatOpenAI(model="gpt-5.5")` | | `ChatAnthropic` | `ChatAnthropic(model="claude-sonnet-4-6")` | | `ChatLiteLLM` | `ChatLiteLLM(model="openai/gpt-5.5")` | | `ChatDeepSeek` | `ChatDeepSeek(model="deepseek-v4-pro")` | | `ChatOpenRouter` | `ChatOpenRouter(model="openai/gpt-5.5")` | ## Choose A Browser ### TUI Use `/browser` to choose how the agent should run the browser: ```text theme={null} /browser ``` Common choices are your local Chrome, a headless browser, or Browser Use Cloud. Browser Use Cloud is the easiest option to get started: it gives you a free, one-click browser setup without managing a local Chrome profile. Use `/profile` to choose the default Chrome profile: ```text theme={null} /profile ``` ### CLI Use terminal config or one-off config overrides for browser settings: ```bash theme={null} browser-use-terminal \ --config 'browser.headless=true' \ --config 'browser.window_size={width=1440,height=900}' \ run-openai "Open Hacker News" ``` ### Python Managed local browser: ```python theme={null} from browser_use.beta import Agent, BrowserProfile, ChatOpenAI agent = Agent( task="Open Hacker News", llm=ChatOpenAI(model="gpt-5.5"), browser_profile=BrowserProfile( headless=True, user_data_dir="./browser-profile", window_size={"width": 1280, "height": 900}, ), ) ``` Headed browser: ```python theme={null} agent = Agent( task="Open Hacker News", llm=ChatOpenAI(model="gpt-5.5"), browser_profile=BrowserProfile(headless=False), ) ``` Browser Use Cloud: Browser Use Cloud is the simplest remote browser option for Python too. Set `BROWSER_USE_API_KEY`, pass `use_cloud=True`, and the terminal runtime handles the cloud browser session. ```python theme={null} from browser_use.beta import Agent, BrowserSession, ChatOpenAI agent = Agent( task="Open Hacker News", llm=ChatOpenAI(model="gpt-5.5"), browser_session=BrowserSession( use_cloud=True, cloud_profile_id="profile-id", cloud_proxy_country_code="US", keep_alive=False, ), ) ``` Existing browser over CDP: ```python theme={null} from browser_use.beta import Agent, BrowserProfile, ChatOpenAI agent = Agent( task="Open Hacker News", llm=ChatOpenAI(model="gpt-5.5"), browser_profile=BrowserProfile( cdp_url="http://127.0.0.1:9333", user_agent="BrowserUseRemoteCDP/1.0", ), ) ``` ## Use Secrets And Login Secrets let the agent fill credentials without exposing raw values to the model. ### TUI Use `/auth` for Browser Use account and model-provider authentication: ```text theme={null} /auth ``` Use `/secrets` to save website login secrets and 2FA setup keys: ```text theme={null} /secrets ``` Inside `/secrets`, press `Ctrl-O` to import logins from 1Password. You can also run: ```text theme={null} /import-passwords ``` Name a TOTP secret `otp` and paste the authenticator setup key, not the current six-digit code. ### CLI Store a password: ```bash theme={null} printf '%s' "$PASSWORD" | browser-use-terminal secrets set \ --domain example.com \ --name password \ --stdin ``` Store a TOTP seed for 2FA: ```bash theme={null} printf '%s' "$TOTP_SEED" | browser-use-terminal secrets set \ --domain example.com \ --name otp \ --totp \ --stdin ``` Import saved logins from 1Password: ```bash theme={null} browser-use-terminal secrets import browser-use-terminal secrets list ``` The import command uses the `op` CLI. `secrets list` prints metadata only, never secret values. ### Python Python runs use the same terminal runtime and terminal state. Configure website secrets with the terminal commands above, then run the Python agent against the matching domain. ```python theme={null} from browser_use.beta import Agent, BrowserProfile, ChatOpenAI agent = Agent( task="Log in to example.com and summarize my dashboard.", llm=ChatOpenAI(model="gpt-5.5"), browser_profile=BrowserProfile(allowed_domains=["example.com"]), ) ``` ## Restrict Domains Domain policy controls where the browser can navigate. ### TUI Use `/domains` to allow or block sites from the TUI: ```text theme={null} /domains ``` ### CLI ```bash theme={null} browser-use-terminal domains allow news.ycombinator.com browser-use-terminal domains deny '*.tracking.example' browser-use-terminal domains list browser-use-terminal domains clear ``` ### Python Pass domain policy through `BrowserProfile`: ```python theme={null} from browser_use.beta import BrowserProfile profile = BrowserProfile( allowed_domains=["news.ycombinator.com"], prohibited_domains=["*.tracking.example"], ) ``` ## Use MCP Servers MCP servers add external tools to the agent. ### TUI Add MCP servers to terminal config, then run the TUI normally: ```bash theme={null} browser ``` ### CLI Create an MCP config: ```toml theme={null} [mcp_servers.local] transport = "stdio" command = "python" args = ["./server.py"] ``` Pass it when you run a task: ```bash theme={null} browser-use-terminal --mcp-config ./mcp.toml run-openai \ "Use the browser and MCP tools to complete this task" ``` ### Python Python terminal-backed runs can use terminal MCP configuration through the same terminal runtime. Configure MCP servers in terminal config before starting the Python run. ## Use Profiles And Config Profiles let you keep separate terminal settings for different accounts, browsers, models, or workflows. ### TUI Use `/profile` to choose the default Chrome profile: ```text theme={null} /profile ``` Open the terminal with a named terminal config profile: ```bash theme={null} browser --profile work ``` ### CLI The terminal reads config from: ```text theme={null} $BROWSER_USE_TERMINAL_HOME/config.toml ``` If `BROWSER_USE_TERMINAL_HOME` is not set, it uses: ```text theme={null} ~/.browser-use-terminal ``` Use a named profile to layer `$BROWSER_USE_TERMINAL_HOME/.config.toml` on top of the base config: ```bash theme={null} browser-use-terminal --profile work run-openai "Research this pricing page" ``` Use `--state-dir` for an isolated run: ```bash theme={null} browser-use-terminal --state-dir /tmp/browser-use-terminal run-openai \ "Open Hacker News and summarize the front page" ``` ### Python Point Python at a terminal state directory: ```bash theme={null} export BROWSER_USE_TERMINAL_HOME=/path/to/state ``` Use a specific terminal binary: ```bash theme={null} export BROWSER_USE_TERMINAL_BINARY=/path/to/browser-use-terminal ``` ## Troubleshooting Setup ### `browser` Or `browser-use-terminal` Not Found Restart your terminal, then check whether the install location is on your `PATH`: ```bash theme={null} which browser which browser-use-terminal echo "$PATH" ``` If the commands are still missing, rerun the installer and copy any `PATH` instructions it prints: ```bash theme={null} curl -fsSL https://browser-use.com/terminal/install.sh | sh ``` ### Which Command Should I Use? | Goal | Command | | ----------------------------------------- | --------------------------------------- | | Open the interactive Terminal app | `browser` | | Run a one-shot Terminal agent task | `browser-use-terminal run-openai "..."` | | Use Terminal runtime from Python | `browser_use.beta.Agent` | | Drive the browser from a coding assistant | `browser-use-terminal browser exec` | | Register the skill with coding assistants | `browser-use-terminal skill install` | | Directly control a browser with Python | `browser-use <<'PY' ... PY` | ### Python Cannot Find `browser_use.beta` Install or upgrade the Python package in the same environment you are running: ```bash theme={null} uv pip install --upgrade browser-use python -c "from browser_use.beta import Agent; print('ok')" ``` ## Forwarded Python Settings When you use `browser_use.beta.Agent`, these Python settings are forwarded to the Rust terminal SDK: | Python setting | Terminal SDK field | | ------------------------------------------------ | ---------------------------- | | `llm.provider`, inferred from the chat model | `llm.provider` | | `llm.model` | `llm.model` | | `llm_timeout` | `llm.timeout` | | `headless` | `browser.headless` | | `keep_alive` | `browser.keep_alive` | | `cloud_profile_id`, `profile_id` | `browser.profile_id` | | `cloud_proxy_country_code`, `proxy_country_code` | `browser.proxy_country_code` | | `cdp_url` | `browser.cdp_url` | | `headers` / CDP headers | `browser.cdp_headers` | | `user_agent` | `browser.user_agent` | | `viewport` | `browser.viewport` | | `window_size` | `browser.window_size` | | `storage_state` | `browser.storage_state` | | `downloads_path` | `browser.downloads_path` | | `allowed_domains` | `browser.allowed_domains` | | `prohibited_domains` | `browser.blocked_domains` | | `state_dir` | `browser.state_dir` | | `no_viewport` | `browser.no_viewport` | | `accept_downloads` | `browser.accept_downloads` | | `output_model_schema` | `output_schema` | | `calculate_cost` | `calculate_cost` | | `max_actions_per_step` | `max_actions_per_step` | The same Python `Agent` instance can continue a terminal-backed session across follow-up runs. ### Cloud Browser Setup Set `BROWSER_USE_API_KEY` and pass the cloud profile or proxy settings through `BrowserSession`. ### 1Password Import Setup Install and sign in to the 1Password CLI: ```bash theme={null} op account list ``` Then import: ```bash theme={null} browser-use-terminal secrets import ``` ### TOTP Setup Store the base32 TOTP seed with `--totp`, not the current six-digit code: ```bash theme={null} printf '%s' "$BASE32_TOTP_SEED" | browser-use-terminal secrets set \ --domain example.com \ --name otp \ --totp \ --stdin ``` # All Parameters Source: https://docs.browser-use.com/open-source/customize/agent/all-parameters Complete reference for all agent configuration options — LLM settings, prompts, tools, output format, and callbacks. ## Available Parameters ### Core Settings * `tools`: Registry of tools the agent can call. Example * `skills` (or `skill_ids`): List of skill IDs to load (e.g., `['skill-uuid']` or `['*']` for all). Requires `BROWSER_USE_API_KEY`. Docs * `browser`: Browser object where you can specify the browser settings. * `output_model_schema`: Pydantic model class for structured output validation. [Example](https://github.com/browser-use/browser-use/blob/main/examples/features/custom_output.py) ### Vision & Processing * `use_vision` (default: `True`): Vision mode - `"auto"` includes screenshot tool but only uses vision when requested, `True` always includes screenshots, `False` never includes screenshots and excludes screenshot tool * `vision_detail_level` (default: `'auto'`): Screenshot detail level - `'low'`, `'high'`, or `'auto'` * `page_extraction_llm`: Separate LLM model for page content extraction. You can choose a small & fast model because it only needs to extract text from the page (default: same as `llm`) ### Fallback & Resilience * `fallback_llm`: Backup LLM to use when the primary LLM fails. The primary LLM will first exhaust its own retry logic (typically 5 attempts with exponential backoff), and only then switch to the fallback. Triggers on rate limits (429), authentication errors (401), payment/credit errors (402), or server errors (500, 502, 503, 504). Once switched, the fallback is used for the rest of the run. [Example](https://github.com/browser-use/browser-use/blob/main/examples/features/fallback_model.py) ### Actions & Behavior * `initial_actions`: List of actions to run before the main task without LLM. [Example](https://github.com/browser-use/browser-use/blob/main/examples/features/initial_actions.py) * `max_actions_per_step` (default: `5`): Maximum actions per step, e.g. for form filling the agent can output 4 fields at once. We execute the actions until the page changes. * `max_failures` (default: `5`): Maximum retries for steps with errors * `final_response_after_failure` (default: `True`): If True, attempt to force one final model call with intermediate output after max\_failures is reached * `use_thinking` (default: `True`): Controls whether the agent uses its internal "thinking" field for explicit reasoning steps. * `flash_mode` (default: `False`): Fast mode that skips evaluation, next goal and thinking and only uses memory. If `flash_mode` is enabled, it overrides `use_thinking` and disables the thinking process entirely. [Example](https://github.com/browser-use/browser-use/blob/main/examples/getting_started/05_fast_agent.py) ### System Messages * `override_system_message`: Completely replace the default system prompt. * `extend_system_message`: Add additional instructions to the default system prompt. [Example](https://github.com/browser-use/browser-use/blob/main/examples/features/custom_system_prompt.py) ### File & Data Management * `save_conversation_path`: Path to save complete conversation history * `save_conversation_path_encoding` (default: `'utf-8'`): Encoding for saved conversations * `available_file_paths`: List of file paths the agent can access * `sensitive_data`: Dictionary of sensitive data to handle carefully. [Example](https://github.com/browser-use/browser-use/blob/main/examples/features/sensitive_data.py) ### Visual Output * `generate_gif` (default: `False`): Generate GIF of agent actions. Set to `True` or string path * `include_attributes`: List of HTML attributes to include in page analysis ### Performance & Limits * `max_history_items`: Maximum number of last steps to keep in the LLM memory. If `None`, we keep all steps. * `llm_timeout` (default: `None`): Timeout in seconds for LLM calls. When omitted, the agent selects a model-dependent timeout of 30, 75, or 90 seconds * `step_timeout` (default: `180`): Timeout in seconds for each step * `directly_open_url` (default: `True`): If we detect a url in the task, we directly open it. ### Advanced Options * `calculate_cost` (default: `False`): Calculate and track API costs * `display_files_in_done_text` (default: `True`): Show file information in completion messages ### Backwards Compatibility * `controller`: Alias for `tools` for backwards compatibility. * `browser_session`: Alias for `browser` for backwards compatibility. *** ## Environment Variables These environment variables can be used to tune agent and browser behavior without code changes. They are particularly useful for debugging, slow networks, or deployment-level tuning. ### Agent Timeouts | Variable | Default | Description | | --------------------------- | ------- | ------------------------------------------------------------------------------------------------- | | `TIMEOUT_AgentEventBusStop` | `3.0` | Timeout in seconds for the agent's event bus to finish processing pending events during shutdown. | ### Browser Action Timeouts | Variable | Default | Description | | ----------------------------------- | ------- | ------------------------------------------------- | | `TIMEOUT_NavigateToUrlEvent` | `30.0` | Timeout for page navigation | | `TIMEOUT_ClickElementEvent` | `15.0` | Timeout for clicking elements | | `TIMEOUT_ClickCoordinateEvent` | `15.0` | Timeout for clicking at coordinates | | `TIMEOUT_TypeTextEvent` | `60.0` | Timeout for typing text (longer for large inputs) | | `TIMEOUT_ScrollEvent` | `8.0` | Timeout for scrolling | | `TIMEOUT_ScrollToTextEvent` | `15.0` | Timeout for scrolling to find text | | `TIMEOUT_SendKeysEvent` | `60.0` | Timeout for sending keyboard shortcuts | | `TIMEOUT_UploadFileEvent` | `30.0` | Timeout for file uploads | | `TIMEOUT_GetDropdownOptionsEvent` | `15.0` | Timeout for fetching dropdown options | | `TIMEOUT_SelectDropdownOptionEvent` | `8.0` | Timeout for selecting dropdown option | | `TIMEOUT_GoBackEvent` | `15.0` | Timeout for browser back navigation | | `TIMEOUT_GoForwardEvent` | `15.0` | Timeout for browser forward navigation | | `TIMEOUT_RefreshEvent` | `15.0` | Timeout for page refresh | | `TIMEOUT_WaitEvent` | `60.0` | Timeout for explicit wait actions | | `TIMEOUT_ScreenshotEvent` | `15.0` | Timeout for taking screenshots | | `TIMEOUT_BrowserStateRequestEvent` | `30.0` | Timeout for fetching browser state/DOM | ### Browser Lifecycle Timeouts | Variable | Default | Description | | ------------------------------- | ------- | ---------------------------------------- | | `TIMEOUT_BrowserStartEvent` | `30.0` | Timeout for starting browser session | | `TIMEOUT_BrowserStopEvent` | `45.0` | Timeout for stopping browser session | | `TIMEOUT_BrowserLaunchEvent` | `30.0` | Timeout for launching browser process | | `TIMEOUT_BrowserKillEvent` | `30.0` | Timeout for killing browser process | | `TIMEOUT_BrowserConnectedEvent` | `30.0` | Timeout for CDP connection | | `TIMEOUT_BrowserStoppedEvent` | `30.0` | Timeout for browser stopped confirmation | | `TIMEOUT_BrowserErrorEvent` | `30.0` | Timeout for browser error events | ### Tab Management Timeouts | Variable | Default | Description | | -------------------------------- | ------- | ------------------------------- | | `TIMEOUT_SwitchTabEvent` | `10.0` | Timeout for switching tabs | | `TIMEOUT_CloseTabEvent` | `10.0` | Timeout for closing tabs | | `TIMEOUT_TabCreatedEvent` | `30.0` | Timeout for tab creation events | | `TIMEOUT_TabClosedEvent` | `10.0` | Timeout for tab closed events | | `TIMEOUT_AgentFocusChangedEvent` | `10.0` | Timeout for focus change events | | `TIMEOUT_TargetCrashedEvent` | `10.0` | Timeout for crash events | ### Navigation Event Timeouts | Variable | Default | Description | | --------------------------------- | ------- | -------------------------------------- | | `TIMEOUT_NavigationStartedEvent` | `30.0` | Timeout for navigation started events | | `TIMEOUT_NavigationCompleteEvent` | `30.0` | Timeout for navigation complete events | ### Storage & Download Timeouts | Variable | Default | Description | | --------------------------------- | ------- | --------------------------------------- | | `TIMEOUT_SaveStorageStateEvent` | `45.0` | Timeout for saving cookies/localStorage | | `TIMEOUT_StorageStateSavedEvent` | `30.0` | Timeout for storage save confirmation | | `TIMEOUT_LoadStorageStateEvent` | `45.0` | Timeout for loading storage state | | `TIMEOUT_StorageStateLoadedEvent` | `30.0` | Timeout for storage load confirmation | | `TIMEOUT_FileDownloadedEvent` | `30.0` | Timeout for file download events | ### Example Usage ```bash theme={null} # Increase timeouts for slow network or complex pages export TIMEOUT_NavigateToUrlEvent=30.0 export TIMEOUT_TypeTextEvent=120.0 export TIMEOUT_BrowserStateRequestEvent=60.0 # Increase agent shutdown timeout export TIMEOUT_AgentEventBusStop=10.0 ``` # Configuration Source: https://docs.browser-use.com/open-source/customize/agent/basics Configure the agent — set your LLM, define tasks, add custom tools, and control behavior with Python. ```python theme={null} from browser_use import Agent, ChatBrowserUse agent = Agent( task="Search for latest news about AI", llm=ChatBrowserUse(), ) async def main(): history = await agent.run(max_steps=100) ``` * `task`: The task you want to automate. * `llm`: Your favorite LLM. See Supported Models. The agent is executed using the async `run()` method: * `max_steps` (default: `100`): Maximum number of steps an agent can take. Check out all customizable parameters here. # Output Format Source: https://docs.browser-use.com/open-source/customize/agent/output-format Control agent output — structured results, step history, action logs, and extracted content from browser automation tasks. ## Agent History The `run()` method returns an `AgentHistoryList` object with the complete execution history: ```python theme={null} history = await agent.run() # Access useful information history.urls() # List of visited URLs history.screenshot_paths() # List of screenshot paths history.screenshots() # List of screenshots as base64 strings history.action_names() # Names of executed actions history.extracted_content() # List of extracted content from all actions history.errors() # List of errors (with None for steps without errors) history.model_actions() # All actions with their parameters history.model_outputs() # All model outputs from history history.last_action() # Last action in history # Analysis methods history.final_result() # Get the final extracted content (last step) history.is_done() # Check whether the agent emitted a done action history.is_successful() # Agent-reported success (None if not done) history.has_errors() # Check if any errors occurred history.model_thoughts() # Get the agent's reasoning process (AgentBrain objects) history.action_results() # Get all ActionResult objects from history history.action_history() # Get truncated action history with essential fields history.number_of_steps() # Get the number of steps in the history history.total_duration_seconds() # Get total duration of all steps in seconds # Structured output (when using output_model_schema) history.structured_output # Property that returns parsed structured output ``` See all helper methods in the [AgentHistoryList source code](https://github.com/browser-use/browser-use/blob/main/browser_use/agent/views.py#L301). ## Structured Output For structured output, use the `output_model_schema` parameter with a Pydantic model. [Example](https://github.com/browser-use/browser-use/blob/main/examples/features/custom_output.py). `is_done()` does not imply success. `is_successful()` reports the agent's own assessment; verify important external outcomes, such as a submitted form or a completed purchase, against the destination system. # Prompting Guide Source: https://docs.browser-use.com/open-source/customize/agent/prompting-guide Write effective prompts for the agent. Tips for task descriptions, multi-step workflows, and getting reliable structured output. Prompting can drastically improve performance and solve existing limitations of the library. ### 1. Be Specific vs Open-Ended **✅ Specific (Recommended)** ```python theme={null} task = """ 1. Go to https://quotes.toscrape.com/ 2. Use extract action with the query "first 3 quotes with their authors" 3. Save results to quotes.csv using write_file action 4. Do a google search for the first quote and find when it was written """ ``` **❌ Open-Ended** ```python theme={null} task = "Go to web and make money" ``` ### 2. Name Actions Directly When you know exactly what the agent should do, reference actions by name: ```python theme={null} task = """ 1. Use search action to find "Python tutorials" 2. Use click to open first result in a new tab 3. Use scroll action to scroll down 2 pages 4. Use extract to extract the names of the first 5 items 5. Wait for 2 seconds if the page is not loaded, refresh it and wait 10 sec 6. Use send_keys action with "Tab Tab ArrowDown Enter" """ ``` See [Available Tools](/open-source/customize/tools/available) for the complete list of actions. ### 3. Handle interaction problems via keyboard navigation Sometimes buttons cannot be clicked (you may have found a bug in the library - open an issue). In many cases, you can work around this with keyboard navigation. ```python theme={null} task = """ If the submit button cannot be clicked: 1. Use send_keys action with "Tab Tab Enter" to navigate and activate 2. Or use send_keys with "ArrowDown ArrowDown Enter" for form submission """ ``` ### 4. Custom Actions Integration ```python theme={null} # When you have custom actions @controller.action("Get 2FA code from authenticator app") async def get_2fa_code(): # Your implementation pass task = """ Login with 2FA: 1. Enter username/password 2. When prompted for 2FA, use get_2fa_code action 3. NEVER try to extract 2FA codes from the page manually 4. ALWAYS use the get_2fa_code action for authentication codes """ ``` ### 5. Error Recovery ```python theme={null} task = """ Robust data extraction: 1. Go to openai.com to find their CEO 2. If navigation fails due to anti-bot protection: - Use google search to find the CEO 3. If page times out, use go_back and try alternative approach """ ``` The key to effective prompting is being specific about actions. # All Parameters Source: https://docs.browser-use.com/open-source/customize/browser/all-parameters Complete reference for all browser configuration options — launch args, proxy, viewport, user data, and CDP settings. The `Browser` instance also provides all [Actor](/open-source/legacy/actor/all-parameters) methods for direct browser control (page management, element interactions, etc.). ## Core Settings * `cdp_url`: CDP URL for connecting to existing browser instance (e.g., `"http://localhost:9222"`) ## Display & Appearance * `headless` (default: `None`): Run browser without UI. Auto-detects based on display availability (`True`/`False`/`None`) * `window_size`: Browser window size for headful mode. Use dict `{'width': 1920, 'height': 1080}` or `ViewportSize` object * `window_position` (default: `{'width': 0, 'height': 0}`): Window position from top-left corner in pixels * `viewport`: Content area size, same format as `window_size`. Use `{'width': 1280, 'height': 720}` or `ViewportSize` object * `no_viewport` (default: `None`): Disable viewport emulation, content fits to window size * `device_scale_factor`: Device scale factor (DPI). Set to `2.0` or `3.0` for high-resolution screenshots ## Browser Behavior * `keep_alive` (default: `None`): Keep browser running after agent completes * `allowed_domains`: Restrict navigation to specific domains. Domain pattern formats: * `'example.com'` - Matches only `https://example.com/*` * `'*.example.com'` - Matches `https://example.com/*` and any subdomain `https://*.example.com/*` * `'http*://example.com'` - Matches both `http://` and `https://` protocols * `'chrome-extension://*'` - Matches any Chrome extension URL * **Security**: Wildcards in TLD (e.g., `example.*`) are **not allowed** for security * Use list like `['*.google.com', 'https://example.com', 'chrome-extension://*']` * **Performance**: Lists with 100+ domains are automatically optimized to sets for O(1) lookup. Pattern matching is disabled for optimized lists. Both `www.example.com` and `example.com` variants are checked automatically. * `prohibited_domains`: Block navigation to specific domains. Uses same pattern formats as `allowed_domains`. When both `allowed_domains` and `prohibited_domains` are set, `allowed_domains` takes precedence. Examples: * `['pornhub.com', '*.gambling-site.net']` - Block specific sites and all subdomains * `['https://explicit-content.org']` - Block specific protocol/domain combination * **Performance**: Lists with 100+ domains are automatically optimized to sets for O(1) lookup (same as `allowed_domains`) * `enable_default_extensions` (default: `True`): Load automation extensions (uBlock Origin, cookie handlers, ClearURLs) * `cross_origin_iframes` (default: `False`): Enable cross-origin iframe support (may cause complexity) * `is_local` (default: `True`): Whether this is a local browser instance. Set to `False` for remote browsers. If we have a `executable_path` set, it will be automatically set to `True`. This can affect your download behavior. ## User Data & Profiles * `user_data_dir` (default: auto-generated temp): Directory for browser profile data. Use `None` for incognito mode * `profile_directory` (default: `'Default'`): Chrome profile subdirectory name (`'Profile 1'`, `'Work Profile'`, etc.) * `storage_state`: Browser storage state (cookies, localStorage). Can be file path string or dict object ## Network & Security * `proxy`: Proxy configuration using `ProxySettings(server='http://host:8080', bypass='localhost,127.0.0.1', username='user', password='pass')` * `permissions` (default: `['clipboardReadWrite', 'notifications']`): Browser permissions to grant. Use list like `['camera', 'microphone', 'geolocation']` * `headers`: Additional HTTP headers for connect requests (remote browsers only) ## Browser Launch * `executable_path`: Path to browser executable for custom installations. Platform examples: * macOS: `'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'` * Windows: `'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'` * Linux: `'/usr/bin/google-chrome'` * `channel`: Browser channel (`'chromium'`, `'chrome'`, `'chrome-beta'`, `'msedge'`, etc.) * `args`: Additional command-line arguments for the browser. Use list format: `['--disable-gpu', '--custom-flag=value', '--another-flag']` * `env`: Environment variables for browser process. Use dict like `{'DISPLAY': ':0', 'LANG': 'en_US.UTF-8', 'CUSTOM_VAR': 'test'}` * `chromium_sandbox` (default: `True` except in Docker): Enable Chromium sandboxing for security * `devtools` (default: `False`): Open DevTools panel automatically (requires `headless=False`) * `ignore_default_args`: List of default args to disable, or `True` to disable all. Use list like `['--enable-automation', '--disable-extensions']` ## Timing & Performance * `minimum_wait_page_load_time` (default: `0.25`): Minimum time to wait before capturing page state in seconds * `wait_for_network_idle_page_load_time` (default: `0.5`): Time to wait for network activity to cease in seconds * `wait_between_actions` (default: `0.1`): Time to wait between agent actions in seconds ## AI Integration * `highlight_elements` (default: `True`): Highlight interactive elements for AI vision * `paint_order_filtering` (default: `True`): Enable paint order filtering to optimize DOM tree by removing elements hidden behind others. Slightly experimental ## Downloads & Files * `accept_downloads` (default: `True`): Automatically accept all downloads * `downloads_path`: Directory for downloaded files. Use string like `'./downloads'` or `Path` object * `auto_download_pdfs` (default: `True`): Automatically download PDFs instead of viewing in browser ## Device Emulation * `user_agent`: Custom user agent string. Example: `'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)'` * `screen`: Screen size information, same format as `window_size` ## Recording & Debugging Video recording requires additional optional dependencies. If these are not installed, no video will be saved and no error will be raised. Install with: ```bash theme={null} pip install "browser-use[video]" ``` or: ```bash theme={null} pip install "imageio[ffmpeg]" numpy ``` * `record_video_dir`: Directory to save video recordings as `.mp4` files * `record_video_size` (default: `ViewportSize`): The frame size (width, height) of the video recording. * `record_video_framerate` (default: `30`): The framerate to use for the video recording. * `record_har_path`: Path to save network trace files as `.har` format * `traces_dir`: Directory to save complete trace files for debugging * `record_har_content` (default: `'embed'`): HAR content mode (`'omit'`, `'embed'`, `'attach'`) * `record_har_mode` (default: `'full'`): HAR recording mode (`'full'`, `'minimal'`) ## Advanced Options * `disable_security` (default: `False`): ⚠️ **NOT RECOMMENDED** - Disables all browser security features * `deterministic_rendering` (default: `False`): ⚠️ **NOT RECOMMENDED** - Forces consistent rendering but reduces performance *** ## Outdated BrowserProfile For backward compatibility, you can pass all the parameters from above to the `BrowserProfile` and then to the `Browser`. ```python theme={null} from browser_use import BrowserProfile profile = BrowserProfile(headless=False) browser = Browser(browser_profile=profile) ``` ## Browser vs BrowserSession `Browser` is an alias for `BrowserSession` - they are exactly the same class: Use `Browser` for cleaner, more intuitive code. *** ## Class Methods ### `Browser.from_system_chrome()` Creates a Browser instance using your system's Chrome installation and profile. Automatically detects Chrome executable and user data directory for your platform. ```python theme={null} from browser_use import Browser # Auto-select first available profile browser = Browser.from_system_chrome() # Or specify a profile browser = Browser.from_system_chrome(profile_directory='Profile 1') ``` **Parameters:** * `profile_directory` (default: `None`): Chrome profile to use (`'Default'`, `'Profile 1'`, etc.). If `None`, auto-selects the first available profile. * `**kwargs`: Additional arguments passed to `Browser()` constructor (e.g., `headless=False`) **Returns:** `Browser` instance configured to use your system Chrome **Raises:** `RuntimeError` if Chrome is not found on your system **Note:** You may need to fully close Chrome before using this, so Chrome profiles aren't used by multiple instances simultaneously. *** ### `Browser.list_chrome_profiles()` Lists available Chrome profiles on the system. ```python theme={null} from browser_use import Browser profiles = Browser.list_chrome_profiles() for p in profiles: print(f"{p['directory']}: {p['name']}") # Output: # Profile 1: Work # Profile 5: Personal ``` **Returns:** List of dicts with `'directory'` and `'name'` keys **Example return value:** ```python theme={null} [ {'directory': 'Default', 'name': 'Person 1'}, {'directory': 'Profile 1', 'name': 'Work'}, {'directory': 'Profile 5', 'name': 'Personal'} ] ``` # Authentication Source: https://docs.browser-use.com/open-source/customize/browser/authentication Log into websites using real browser profiles, saved storage state, and 2FA codes for authenticated automation. Browser-Use supports multiple authentication strategies depending on your use case: | Approach | Best For | Setup Effort | | ------------------------------------------- | ------------------------------------ | ------------ | | [Real Browser](#real-browser-profiles) | Personal automation, existing logins | Low | | [Storage State](#storage-state-persistence) | Production, CI/CD, headless | Medium | | [TOTP 2FA](#totp-2fa) | Sites with authenticator apps | Low | | [Email and SMS 2FA](#email-and-sms-2fa) | Sites with email/SMS verification | Medium | *** ## Real Browser Profiles Connect to your existing Chrome browser to reuse your authenticated sessions. No need to handle logins, cookies, or 2FA - if you are logged in on Chrome, the agent is, too. See [Real Browser](/open-source/customize/browser/real-browser) for more details and platform paths. You may need to close Chrome completely before running. Browser-Use launches Chrome in debug mode, which can conflict with existing Chrome processes. ```python theme={null} from browser_use import Agent, Browser, ChatBrowserUse # Auto-detect Chrome and profile (cross-platform) browser = Browser.from_system_chrome() agent = Agent( task='Check my Gmail inbox', browser=browser, llm=ChatBrowserUse(), ) await agent.run() ``` *** ## Storage State Persistence Export cookies and localStorage from an authenticated browser, then load them in headless mode. Useful for production/CI where you cannot use a real browser profile. See [Browser Parameters](/open-source/customize/browser/all-parameters#user-data-&-profiles) for all storage options. ### Export from Real Browser ```python theme={null} from browser_use import Browser browser = Browser.from_system_chrome() await browser.start() await browser.export_storage_state('auth.json') await browser.stop() ``` ### Load in Headless Mode ```python theme={null} from browser_use import Agent, Browser, ChatBrowserUse browser = Browser(storage_state='auth.json') agent = Agent( task='Check my notifications', browser=browser, llm=ChatBrowserUse(), ) await agent.run() ``` ### Auto-Save and Load When you provide a `storage_state` path, Browser-Use automatically: * Loads cookies from the file on startup (if it exists) * Saves cookies to the file periodically and on shutdown ```python theme={null} browser = Browser(storage_state='session.json') ``` The file is created if it does not exist, and new cookies are merged with existing ones on each save. ### Storage State Format The JSON file follows Playwright's format: ```json theme={null} { "cookies": [ { "name": "session_id", "value": "abc123", "domain": ".example.com", "path": "/", "expires": 1704067200, "httpOnly": true, "secure": true, "sameSite": "Lax" } ], "origins": [ { "origin": "https://example.com", "localStorage": [ {"name": "auth_token", "value": "xyz789"} ] } ] } ``` *** ## TOTP 2FA For sites using authenticator apps (Google Authenticator, 1Password, etc.), Browser-Use can generate TOTP codes automatically. See [Sensitive Data](/open-source/examples/templates/sensitive-data) for more on credential handling. ### How It Works 1. Get the TOTP secret key when setting up 2FA (usually shown as "manual entry" or "cannot scan QR code") 2. Pass the secret with `bu_2fa_code` suffix in `sensitive_data` 3. When the agent inputs `bu_2fa_code`, it generates a fresh 6-digit code ```python theme={null} from browser_use import Agent, ChatBrowserUse # TOTP secret from your authenticator setup # (NOT the 6-digit code - the secret key itself) totp_secret = 'JBSWY3DPEHPK3PXP' agent = Agent( task=''' 1. Go to https://example.com/login 2. Enter username x_user and password x_pass 3. When prompted for 2FA, enter bu_2fa_code ''', sensitive_data={ 'x_user': 'myusername', 'x_pass': 'mypassword', 'bu_2fa_code': totp_secret, # suffix must be bu_2fa_code }, llm=ChatBrowserUse(), ) await agent.run() ``` The placeholder name must end with `bu_2fa_code`. You can use any prefix: `google_bu_2fa_code`, `github_bu_2fa_code`, etc. ### Where to Find TOTP Secrets * **1Password**: Edit item → One-Time Password → Show secret * **Google Authenticator**: During setup, click "Can't scan it?" to see the key * **Authy**: Export via desktop app settings * **Most sites**: Look for "manual entry" or "setup key" during 2FA enrollment *** ## Email and SMS 2FA For sites that send verification codes via email or SMS, use follow-up tasks to retrieve the code. ### With AgentMail [AgentMail](https://agentmail.to) provides disposable inboxes for email verification: ```python theme={null} from agentmail import AsyncAgentMail from browser_use import Agent, ChatBrowserUse, Tools, ActionResult email_client = AsyncAgentMail() inbox = await email_client.inboxes.create() tools = Tools() @tools.registry.action('Get email address for signup') async def get_email_address(): return ActionResult(extracted_content=inbox.inbox_id) @tools.registry.action('Get verification code from email') async def get_verification_code(): emails = await email_client.inboxes.messages.list(inbox_id=inbox.inbox_id) if emails.messages: return ActionResult(extracted_content=emails.messages[0].text) return ActionResult(error='No emails found') agent = Agent( task='Sign up at example.com, get verification code from email', tools=tools, llm=ChatBrowserUse(), ) await agent.run() ``` See [`examples/integrations/agentmail/`](https://github.com/browser-use/browser-use/tree/main/examples/integrations/agentmail) for a more complete implementation with email waiting and parsing. ### With 1Password SDK Retrieve codes from your password manager: ```python theme={null} import os from onepassword.client import Client from browser_use import Agent, Tools, ActionResult, ChatBrowserUse tools = Tools() @tools.registry.action('Get 2FA code from 1Password', domains=['*.google.com']) async def get_1password_2fa(): client = await Client.authenticate( auth=os.environ['OP_SERVICE_ACCOUNT_TOKEN'], integration_name='Browser-Use', integration_version='v1.0.0', ) code = await client.secrets.resolve('op://Private/Google/One-time passcode') return ActionResult(extracted_content=code) agent = Agent( task='Login to Google and check email', tools=tools, llm=ChatBrowserUse(), ) await agent.run() ``` See [`examples/custom-functions/onepassword_2fa.py`](https://github.com/browser-use/browser-use/tree/main/examples/custom-functions/onepassword_2fa.py) for the full example. ### With Gmail API Built-in Gmail integration for reading 2FA codes from your inbox: ```python theme={null} from browser_use import Agent, ChatBrowserUse, Tools from browser_use.integrations.gmail import GmailService, register_gmail_actions gmail_service = GmailService() tools = Tools() register_gmail_actions(tools, gmail_service=gmail_service) agent = Agent( task='Login to example.com, then get the verification code from Gmail', tools=tools, llm=ChatBrowserUse(), ) await agent.run() ``` Requires Gmail API setup: 1. Enable Gmail API in [Google Cloud Console](https://console.cloud.google.com/) 2. Create OAuth 2.0 credentials (Desktop app) 3. Save credentials to `~/.config/browseruse/gmail_credentials.json` See [`examples/integrations/gmail_2fa_integration.py`](https://github.com/browser-use/browser-use/tree/main/examples/integrations/gmail_2fa_integration.py) for setup with automatic credential validation. *** ## Security Best Practices See [Secure Setup](/open-source/examples/templates/secure) for enterprise security with Azure OpenAI. ### Restrict Domains Limit where the browser can navigate to prevent credential leaks: ```python theme={null} browser = Browser( allowed_domains=['*.example.com', 'auth.example.com'], ) ``` ### Disable Vision for Sensitive Pages Prevent screenshots from being sent to the LLM: ```python theme={null} agent = Agent( task='Login and check balance', use_vision=False, # No screenshots sent to LLM sensitive_data={'password': 'secret123'}, llm=ChatBrowserUse(), ) ``` ### Domain-Specific Credentials Route credentials to specific domains only: ```python theme={null} sensitive_data = { 'https://*.work.com': { 'work_user': 'alice@work.com', 'work_pass': 'work_password', }, 'https://personal.com': { 'personal_user': 'alice@gmail.com', 'personal_pass': 'personal_password', }, } ``` *** ## Cloud Browser Profiles For production deployments, consider [Browser Use Cloud](https://cloud.browser-use.com), which provides: * Persistent browser profiles in the cloud * Pre-authenticated sessions * No local Chrome installation required * Built-in proxy and fingerprint management ```python theme={null} from browser_use import Agent, Browser, ChatBrowserUse browser = Browser(cdp_url='wss://cloud.browser-use.com/...') agent = Agent( task='Check my orders', browser=browser, llm=ChatBrowserUse(), ) await agent.run() ``` # Configuration Source: https://docs.browser-use.com/open-source/customize/browser/basics Configure the browser instance — headless mode, viewport size, proxy settings, and Playwright launch options. *** ```python theme={null} from browser_use import Agent, Browser, ChatBrowserUse browser = Browser( headless=False, # Show browser window window_size={'width': 1000, 'height': 700}, # Set window size ) agent = Agent( task='Search for Browser Use', browser=browser, llm=ChatBrowserUse(), ) async def main(): await agent.run() ``` # Real Browser Source: https://docs.browser-use.com/open-source/customize/browser/real-browser Connect to your existing Chrome browser to preserve login sessions, cookies, and extensions for authenticated tasks. This allows you to automate your existing Chrome browser, so you are already logged into your websites. You may need to fully close Chrome before running these examples. Additionally, if Google search blocks automated browsers, use DuckDuckGo or other search engines instead. ## Basic Example ```python theme={null} import asyncio from browser_use import Agent, Browser, ChatOpenAI # Auto-selects first available Chrome profile browser = Browser.from_system_chrome() agent = Agent( task='Visit https://duckduckgo.com and search for "browser-use founders"', browser=browser, llm=ChatOpenAI(model='gpt-4.1-mini'), ) async def main(): await agent.run() if __name__ == "__main__": asyncio.run(main()) ``` ## Choosing a Profile Chrome supports multiple profiles. List and select the one you want: ```python theme={null} from browser_use import Browser # List available profiles profiles = Browser.list_chrome_profiles() for p in profiles: print(f"{p['directory']}: {p['name']}") # Output: # Profile 1: Work # Profile 5: Personal # Use a specific profile browser = Browser.from_system_chrome(profile_directory='Profile 5') ``` ## Manual Path Configuration If auto-detection does not work, specify paths manually: ```python theme={null} browser = Browser( executable_path='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', user_data_dir='~/Library/Application Support/Google/Chrome', profile_directory='Default', ) ``` ## How it Works `Browser.from_system_chrome()` automatically detects: | Platform | Executable Path | User Data Directory | | -------- | -------------------------------------------------------------- | --------------------------------------------- | | macOS | `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` | `~/Library/Application Support/Google/Chrome` | | Windows | `C:\Program Files\Google\Chrome\Application\chrome.exe` | `%LocalAppData%\Google\Chrome\User Data` | | Linux | `/usr/bin/google-chrome` or `/usr/bin/chromium` | `~/.config/google-chrome` | # Remote Browser Source: https://docs.browser-use.com/open-source/customize/browser/remote Connect to remote browsers via CDP — use with the Cloud, Browserbase, or any remote Chrome instance. ### Browser-Use Cloud Browser or CDP URL The easiest way to use a cloud browser is with the built-in Browser-Use cloud service: ```python theme={null} from browser_use import Agent, Browser, ChatBrowserUse # Simple: Use Browser-Use cloud browser service browser = Browser( use_cloud=True, # Automatically provisions a cloud browser ) # Advanced: Configure cloud browser parameters # Managed browsers include CAPTCHA handling; some sites may still block automation browser = Browser( cloud_profile_id='your-profile-id', # Optional: specific browser profile cloud_proxy_country_code='us', # Optional: proxy location (us, uk, fr, it, jp, au, de, fi, ca, in) cloud_timeout=30, # Optional: requested session timeout in minutes (up to 240) ) # Or use a CDP URL from any cloud browser provider browser = Browser( cdp_url="http://remote-server:9222" # Get a CDP URL from any provider ) agent = Agent( task="Your task here", llm=ChatBrowserUse(), browser=browser, ) ``` **Prerequisites:** 1. Get an API key from [cloud.browser-use.com](https://cloud.browser-use.com/new-api-key) 2. Set BROWSER\_USE\_API\_KEY environment variable **Cloud Browser Parameters:** * `cloud_profile_id`: UUID of a browser profile (optional, uses default if not specified) * `cloud_proxy_country_code`: Country code for proxy location - supports: us, uk, fr, it, jp, au, de, fi, ca, in * `cloud_timeout`: Requested session timeout in minutes, up to 240. Credit balance and account eligibility still apply; this is not a promise of free usage for that duration. **Benefits:** * ✅ No local browser setup required * ✅ Scalable and fast cloud infrastructure * ✅ Automatic provisioning and teardown * ✅ Built-in authentication handling * ✅ Optimized for browser automation * ✅ Global proxy support for geo-restricted content ### Third-Party Cloud Browsers You can pass in a CDP URL from any remote browser ### Proxy Connection ```python theme={null} from browser_use import Agent, Browser, ChatBrowserUse from browser_use.browser import ProxySettings browser = Browser( headless=False, proxy=ProxySettings( server="http://proxy-server:8080", username="proxy-user", password="proxy-pass" ), cdp_url="http://remote-server:9222" ) agent = Agent( task="Your task here", llm=ChatBrowserUse(), browser=browser, ) ``` # Lifecycle Hooks Source: https://docs.browser-use.com/open-source/customize/hooks Hook into agent lifecycle events — before/after actions, navigation, extraction, and error handling callbacks. Browser-Use provides lifecycle hooks that allow you to execute custom code at specific points during the agent's execution. Hook functions can be used to read and modify agent state while running, implement custom logic, change configuration, integrate the Agent with external applications. ## Available Hooks Currently, Browser-Use provides the following hooks: | Hook | Description | When it is called | | --------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------- | | `on_step_start` | Executed at the beginning of each agent step | Before the agent processes the current state and decides on the next action | | `on_step_end` | Executed at the end of each agent step | After the agent has executed all the actions for the current step, before it starts the next step | ```python theme={null} await agent.run(on_step_start=..., on_step_end=...) ``` Each hook should be an `async` callable function that accepts the `agent` instance as its only parameter. ### Basic Example ```python theme={null} import asyncio from pathlib import Path from browser_use import Agent, ChatOpenAI from browser_use.browser.events import ScreenshotEvent async def my_step_hook(agent: Agent): # inside a hook you can access all the state and methods under the Agent object: # agent.settings, agent.state, agent.task # agent.tools, agent.llm, agent.browser_session # agent.pause(), agent.resume(), agent.add_new_task(...), etc. # You also have direct access to the browser state state = await agent.browser_session.get_browser_state_summary() current_url = state.url visit_log = agent.history.urls() previous_url = visit_log[-2] if len(visit_log) >= 2 else None print(f'Agent was last on URL: {previous_url} and is now on {current_url}') cdp_session = await agent.browser_session.get_or_create_cdp_session() # Example: Get page HTML content doc = await cdp_session.cdp_client.send.DOM.getDocument(session_id=cdp_session.session_id) html_result = await cdp_session.cdp_client.send.DOM.getOuterHTML( params={'nodeId': doc['root']['nodeId']}, session_id=cdp_session.session_id ) page_html = html_result['outerHTML'] # Example: Take a screenshot using the event system screenshot_event = agent.browser_session.event_bus.dispatch(ScreenshotEvent(full_page=False)) await screenshot_event result = await screenshot_event.event_result(raise_if_any=True, raise_if_none=True) # Example: pause agent execution and resume it based on some custom code if '/finished' in current_url: agent.pause() Path('result.txt').write_text(page_html) input('Saved "finished" page content to result.txt, press [Enter] to resume...') agent.resume() async def main(): agent = Agent( task='Search for the latest news about AI', llm=ChatOpenAI(model='gpt-5-mini'), ) await agent.run( on_step_start=my_step_hook, # on_step_end=... max_steps=10, ) if __name__ == '__main__': asyncio.run(main()) ``` ## Data Available in Hooks When working with agent hooks, you have access to the entire `Agent` instance. Here are some useful data points you can access: * `agent.task` provides the main task, `agent.add_new_task(...)` allows you to queue up a new one * `agent.tools` gives access to the `Tools()` object and `Registry()` containing the available actions * `agent.tools.registry.execute_action('click', {'index': 123}, browser_session=agent.browser_session)` * `agent.sensitive_data` contains the sensitive data dict, which can be updated in-place to add/remove/modify items * `agent.settings` contains all the configuration options passed to the `Agent(...)` at init time * `agent.llm` provides direct access to the main LLM object (e.g. `ChatOpenAI`) * `agent.state` provides access to internal state, including agent thoughts, outputs, actions, and more * `agent.history` gives access to historical data from the agent's execution: * `agent.history.model_thoughts()`: Reasoning from Browser Use's model. * `agent.history.model_outputs()`: Raw outputs from the Browser Use's model. * `agent.history.model_actions()`: Actions taken by the agent * `agent.history.extracted_content()`: Content extracted from web pages * `agent.history.urls()`: URLs visited by the agent * `agent.browser_session` provides direct access to the `BrowserSession` and CDP interface * `agent.browser_session.agent_focus_target_id`: Get the current target ID the agent is focused on * `agent.browser_session.get_or_create_cdp_session()`: Get the current CDP session for browser interaction * `agent.browser_session.get_tabs()`: Get all tabs currently open * `agent.browser_session.get_current_page_url()`: Get the URL of the current active tab * `agent.browser_session.get_current_page_title()`: Get the title of the current active tab ## Tips for Using Hooks * **Avoid blocking operations**: Since hooks run in the same execution thread as the agent, keep them efficient and avoid blocking operations. * **Use custom tools instead**: Hooks are fairly advanced. Most use cases can be implemented with [custom tools](/open-source/customize/tools/basics) instead. * **Increase step\_timeout**: If your hook is doing something that takes a long time, you can increase the `step_timeout` parameter in the `Agent(...)` constructor. *** # MCP Server Source: https://docs.browser-use.com/open-source/customize/integrations/mcp-server Run browser-use as a local Model Context Protocol server. Connect AI models to browser automation through the MCP standard. Browser Use can run as a local **Model Context Protocol (MCP)** server on your machine via stdio. This is the **free, open-source option** that gives you direct, low-level control over browser automation but requires your own LLM API keys. Looking for a hosted solution? Use the [Cloud MCP Server](/cloud/guides/mcp-server) instead — no setup required, just an API key. ## Quick Start ```bash theme={null} uvx --from 'browser-use[cli]' browser-use --mcp ``` The server will start in stdio mode, ready to accept MCP connections. ## Client Setup ```bash theme={null} claude mcp add browser-use -- uvx --from 'browser-use[cli]' browser-use --mcp ``` Add to your Claude Desktop config file: **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json` ```json theme={null} { "mcpServers": { "browser-use": { "command": "/Users/your-username/.local/bin/uvx", "args": ["--from", "browser-use[cli]", "browser-use", "--mcp"], "env": { "OPENAI_API_KEY": "your-openai-api-key-here" } } } } ``` **Windows:** `%APPDATA%\Claude\claude_desktop_config.json` ```json theme={null} { "mcpServers": { "browser-use": { "command": "uvx", "args": ["--from", "browser-use[cli]", "browser-use", "--mcp"], "env": { "OPENAI_API_KEY": "your-openai-api-key-here" } } } } ``` Restart Claude Desktop after saving. **macOS/Linux PATH Issue:** Claude Desktop may not find `uvx` in your PATH. Use the full path to `uvx` instead: * Run `which uvx` in your terminal to find the location (usually `/Users/username/.local/bin/uvx` or `~/.local/bin/uvx`) * Replace `"command": "uvx"` with the full path, e.g., `"command": "/Users/your-username/.local/bin/uvx"` * Replace `your-username` with your actual username **CLI Extras Required:** The `--from browser-use[cli]` flag installs the CLI extras needed for MCP server support. Add to `~/.cursor/mcp.json`: ```json theme={null} { "mcpServers": { "browser-use": { "command": "uvx", "args": ["--from", "browser-use[cli]", "browser-use", "--mcp"], "env": { "OPENAI_API_KEY": "your-openai-api-key-here" } } } } ``` Add to `~/.codeium/windsurf/mcp_config.json`: ```json theme={null} { "mcpServers": { "browser-use": { "command": "uvx", "args": ["--from", "browser-use[cli]", "browser-use", "--mcp"], "env": { "OPENAI_API_KEY": "your-openai-api-key-here" } } } } ``` ## Environment Variables * `OPENAI_API_KEY` - Your OpenAI API key (required) * `ANTHROPIC_API_KEY` - Your Anthropic API key (alternative to OpenAI) * `BROWSER_USE_HEADLESS` - Set to `false` to show browser window * `BROWSER_USE_DISABLE_SECURITY` - Set to `true` to disable browser security features ## Available Tools The local MCP server exposes these low-level browser automation tools for direct control: #### Autonomous Agent Tools * **`retry_with_browser_use_agent`** - Run a complete browser automation task with an AI agent (use as last resort when direct control fails) #### Direct Browser Control * **`browser_navigate`** - Navigate to a URL * **`browser_click`** - Click on an element by index * **`browser_type`** - Type text into an element * **`browser_get_state`** - Get current page state and interactive elements * **`browser_scroll`** - Scroll the page * **`browser_go_back`** - Go back in browser history #### Tab Management * **`browser_list_tabs`** - List all open browser tabs * **`browser_switch_tab`** - Switch to a specific tab * **`browser_close_tab`** - Close a tab #### Content Extraction * **`browser_extract_content`** - Extract structured content from the current page * **`browser_get_html`** - Return the full page HTML or one CSS-selected element * **`browser_screenshot`** - Capture the viewport or the full scrollable page #### Session Management * **`browser_list_sessions`** - List all active browser sessions with details * **`browser_close_session`** - Close a specific browser session by ID * **`browser_close_all`** - Close all active browser sessions ## Example Usage Once configured, you can ask your AI to perform browser automation tasks: ``` "Please navigate to example.com and take a screenshot" "Search for 'browser automation' on Google and summarize the first 3 results" "Go to GitHub, find the browser-use repository, and tell me about the latest release" ``` ## Programmatic Usage You can also connect to the MCP server programmatically: ```python theme={null} import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def use_browser_mcp(): # Connect to browser-use MCP server server_params = StdioServerParameters( command="uvx", args=["--from", "browser-use[cli]", "browser-use", "--mcp"] ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() # Navigate to a website result = await session.call_tool( "browser_navigate", arguments={"url": "https://example.com"} ) print(result.content[0].text) # Get page state result = await session.call_tool( "browser_get_state", arguments={"include_screenshot": True} ) print("Page state retrieved!") asyncio.run(use_browser_mcp()) ``` ## Troubleshooting **"CLI addon is not installed" Error** Make sure you are using `--from 'browser-use[cli]'` in your uvx command: ```bash theme={null} uvx --from 'browser-use[cli]' browser-use --mcp ``` **"spawn uvx ENOENT" Error (macOS/Linux)** Claude Desktop cannot find `uvx` in its PATH. Use the full path in your config: * Run `which uvx` in terminal to find the location * Update your config to use the full path (e.g., `/Users/your-username/.local/bin/uvx`) **Browser doesn't start** * Check that you have Chrome/Chromium installed * Try setting `BROWSER_USE_HEADLESS=false` to see browser window * Ensure no other browser instances are using the same profile **API Key Issues** * Verify your `OPENAI_API_KEY` is set correctly * Check API key permissions and billing status * Try using `ANTHROPIC_API_KEY` as an alternative **Connection Issues in Claude Desktop** * Restart Claude Desktop after config changes * Check the config file syntax is valid JSON * Verify the file path is correct for your OS * Check logs at `~/Library/Logs/Claude/` (macOS) or `%APPDATA%\Claude\Logs\` (Windows) **Debug Mode** Enable debug logging by setting: ```bash theme={null} export BROWSER_USE_LOGGING_LEVEL=DEBUG uvx --from 'browser-use[cli]' browser-use --mcp ``` ## Security Considerations * The MCP server has access to your browser and file system * Only connect trusted MCP clients * Be cautious with sensitive websites and data * Consider running in a sandboxed environment for untrusted automation ## Next Steps * Explore the [examples directory](https://github.com/browser-use/browser-use/tree/main/examples) for more usage patterns * Check out [MCP documentation](https://modelcontextprotocol.io/) to learn more about the protocol * Join our [Discord](https://link.browser-use.com/discord) for support and discussions # Add Tools Source: https://docs.browser-use.com/open-source/customize/tools/add Extend agents with custom Python functions. Add API calls, file operations, or any custom logic as agent tools. Examples: * deterministic clicks * file handling * calling APIs * human-in-the-loop * browser interactions * calling LLMs * get 2fa codes * send emails * Playwright integration (see [GitHub example](https://github.com/browser-use/browser-use/blob/main/examples/browser/playwright_integration.py)) * ... Simply add `@tools.action(...)` to your function. ```python theme={null} from browser_use import Tools, Agent, ActionResult tools = Tools() @tools.action(description='Ask human for help with a question') async def ask_human(question: str) -> ActionResult: answer = input(f'{question} > ') return ActionResult(extracted_content=f'The human responded with: {answer}') ``` ```python theme={null} agent = Agent(task='...', llm=llm, tools=tools) ``` * **`description`** *(required)* - What the tool does, the LLM uses this to decide when to call it. * **`allowed_domains`** - List of domains where tool can run (e.g. `['*.example.com']`), defaults to all domains The Agent fills your function parameters based on their names, type hints, & defaults. **Common Pitfall**: Parameter names must match exactly! Use `browser_session: BrowserSession` (not `browser: Browser`). The agent injects special parameters by **name matching**, so using incorrect names will cause your tool to fail silently. See [Available Objects](#available-objects) below for the correct parameter names. ## Available Objects Your function has access to these objects: * **`browser_session: BrowserSession`** - Current browser session for CDP access * **`cdp_client`** - Direct Chrome DevTools Protocol client * **`page_extraction_llm: BaseChatModel`** - The LLM you pass into agent. This can be used to do a custom llm call here. * **`file_system: FileSystem`** - File system access * **`available_file_paths: list[str]`** - Available files for upload/processing * **`has_sensitive_data: bool`** - Whether action contains sensitive data ## Browser Interaction Examples You can use `browser_session` to directly interact with page elements using CSS selectors: ```python theme={null} from browser_use import Tools, Agent, ActionResult, BrowserSession tools = Tools() @tools.action(description='Click the submit button using CSS selector') async def click_submit_button(browser_session: BrowserSession): # Get the current page page = await browser_session.must_get_current_page() # Get element(s) by CSS selector elements = await page.get_elements_by_css_selector('button[type="submit"]') if not elements: return ActionResult(extracted_content='No submit button found') # Click the first matching element await elements[0].click() return ActionResult(extracted_content='Submit button clicked!') ``` Available methods on `Page`: * `get_elements_by_css_selector(selector: str)` - Returns list of matching elements * `get_element_by_prompt(prompt: str, llm)` - Returns element or None using LLM * `must_get_element_by_prompt(prompt: str, llm)` - Returns element or raises error Available methods on `Element`: * `click()` - Click the element * `type(text: str)` - Type text into the element * `get_text()` - Get element text content * See `browser_use/actor/element.py` for more methods ## Pydantic Input You can use Pydantic for the tool parameters: ```python theme={null} import json from pydantic import BaseModel, Field from browser_use import Tools tools = Tools() class Cars(BaseModel): name: str = Field(description='The name of the car, e.g. "Toyota Camry"') price: int = Field(description='The price of the car as int in USD, e.g. 25000') @tools.action(description='Save cars to file') def save_cars(cars: list[Cars]) -> str: with open('cars.json', 'w') as f: json.dump(cars, f) return f'Saved {len(cars)} cars to file' task = "find cars and save them to file" ``` ## Domain Restrictions Limit tools to specific domains: ```python theme={null} @tools.action( description='Fill out banking forms', allowed_domains=['https://mybank.com'] ) def fill_bank_form(account_number: str) -> str: # Only works on mybank.com return f'Filled form for account {account_number}' ``` ## Advanced Example For a comprehensive example of custom tools with Playwright integration, see: **[Playwright Integration Example](https://github.com/browser-use/browser-use/blob/main/examples/browser/playwright_integration.py)** This shows how to create custom actions that use Playwright's precise browser automation alongside Browser-Use. ## Common Pitfalls The agent injects special parameters **by name**, not by type. Using incorrect parameter names is the most common cause of tools failing silently. ### ❌ Wrong: Using `browser: Browser` ```python theme={null} from browser_use import Tools, ActionResult, Browser @tools.action('My action') def my_action(browser: Browser) -> ActionResult: # WRONG! # This will NOT receive the browser session pass ``` ### ✅ Correct: Using `browser_session: BrowserSession` ```python theme={null} from browser_use import Tools, ActionResult, BrowserSession @tools.action('My action') async def my_action(browser_session: BrowserSession) -> ActionResult: # CORRECT! page = await browser_session.must_get_current_page() # Now you have access to the browser return ActionResult(extracted_content='Done') ``` ### Key Points 1. **Use `browser_session: BrowserSession`** - not `browser: Browser` 2. **Use `async` functions** - recommended for consistency with browser operations 3. **Return `ActionResult`** - not plain strings (though strings work, `ActionResult` provides more control) 4. **Parameter names must match exactly** - see [Available Objects](#available-objects) for the full list of injectable parameters # Available Tools Source: https://docs.browser-use.com/open-source/customize/tools/available Built-in tools — click, type, scroll, extract, navigate, and more. Full list of default agent actions. ### Navigation & Browser Control * **`search`** - Search queries (DuckDuckGo, Google, Bing) * **`navigate`** - Navigate to URLs * **`go_back`** - Go back in browser history * **`wait`** - Wait for specified seconds ### Page Interaction * **`click`** - Click elements by their index * **`input`** - Input text into form fields * **`upload_file`** - Upload files to file inputs * **`scroll`** - Scroll the page up/down * **`find_text`** - Scroll to specific text on page * **`send_keys`** - Send special keys (Enter, Escape, etc.) ### JavaScript Execution * **`evaluate`** - Execute custom JavaScript code on the page (for advanced interactions, shadow DOM, custom selectors, data extraction) ### Tab Management * **`switch`** - Switch between browser tabs * **`close`** - Close browser tabs ### Content Extraction * **`extract`** - Extract data from webpages using LLM ### Visual Analysis * **`screenshot`** - Request a screenshot in your next browser state for visual confirmation ### Form Controls * **`dropdown_options`** - Get dropdown option values * **`select_dropdown`** - Select dropdown options ### File Operations * **`write_file`** - Write content to files * **`read_file`** - Read file contents * **`replace_file`** - Replace text in files ### Task Completion * **`done`** - Complete the task (always available) # Overview Source: https://docs.browser-use.com/open-source/customize/tools/basics Learn about built-in browser actions and custom tools. ## Quick Example ```python theme={null} from browser_use import Agent, Tools, ActionResult, BrowserSession tools = Tools() @tools.action('Ask human for help with a question') async def ask_human(question: str, browser_session: BrowserSession) -> ActionResult: answer = input(f'{question} > ') return ActionResult(extracted_content=f'The human responded with: {answer}') agent = Agent( task='Ask human for help', llm=llm, tools=tools, ) ``` **Important**: The parameter must be named exactly `browser_session` with type `BrowserSession` (not `browser: Browser`). The agent injects parameters by name matching, so using the wrong name will cause your tool to fail silently. Use `browser_session` parameter in tools for deterministic [Actor](/open-source/legacy/actor/basics) actions. # Remove Tools Source: https://docs.browser-use.com/open-source/customize/tools/remove Exclude default tools to restrict agent capabilities. ```python theme={null} from browser_use import Tools tools = Tools(exclude_actions=['search', 'wait']) agent = Agent(task='...', llm=llm, tools=tools) ``` # Response Format Source: https://docs.browser-use.com/open-source/customize/tools/response Customize how tools return data to the agent. Control response formatting, extraction, and content filtering. Tools return results using `ActionResult` or simple strings. ## Return Types ```python theme={null} @tools.action('My tool') def my_tool() -> str: return "Task completed successfully" @tools.action('Advanced tool') def advanced_tool() -> ActionResult: return ActionResult( extracted_content="Main result", long_term_memory="Remember this info", error="Something went wrong", is_done=True, success=True, attachments=["file.pdf"], ) ``` ## ActionResult Properties * `extracted_content` (default: `None`) - Main result passed to LLM, this is equivalent to returning a string. * `include_extracted_content_only_once` (default: `False`) - Set to `True` for large content to include it only once in the LLM input. * `long_term_memory` (default: `None`) - This is always included in the LLM input for all future steps. * `error` (default: `None`) - Error message, we catch exceptions and set this automatically. This is always included in the LLM input. * `is_done` (default: `False`) - Tool completes entire task * `success` (default: `None`) - Task success (only valid with `is_done=True`) * `attachments` (default: `None`) - Files to show user * `metadata` (default: `None`) - Debug/observability data ## Why `extracted_content` and `long_term_memory`? With this you control the context for the LLM. ### 1. Include short content always in context ```python theme={null} def simple_tool() -> str: return "Hello, world!" # Keep in context for all future steps ``` ### 2. Show long content once, remember subset in context ```python theme={null} return ActionResult( extracted_content="[500 lines of product data...]", # Shows to LLM once include_extracted_content_only_once=True, # Never show full output again long_term_memory="Found 50 products" # Only this in future steps ) ``` We save the full `extracted_content` to files which the LLM can read in future steps. ### 3. Don't show long content, remember subset in context ```python theme={null} return ActionResult( extracted_content="[500 lines of product data...]", # The LLM never sees this because `long_term_memory` overrides it and `include_extracted_content_only_once` is not used long_term_memory="Saved user's favorite products", # This is shown to the LLM in future steps ) ``` ## Terminating the Agent Set `is_done=True` to stop the agent completely. Use when your tool finishes the entire task: ```python theme={null} @tools.action(description='Complete the task') def finish_task() -> ActionResult: return ActionResult( extracted_content="Task completed!", is_done=True, # Stops the agent success=True # Task succeeded ) ``` # Get Help Source: https://docs.browser-use.com/open-source/development/get-help Get help — join 20k+ developers on Discord, search GitHub issues, or ask the community. 1. Check our [GitHub Issues](https://github.com/browser-use/browser-use/issues) 2. Ask in our [Discord community](https://link.browser-use.com/discord) 3. Get support for your enterprise with [support@browser-use.com](mailto:support@browser-use.com) # Costs Source: https://docs.browser-use.com/open-source/development/monitoring/costs Track token usage and API costs for your browser automation tasks ## Cost Tracking To track token usage and costs, enable cost calculation: ```python theme={null} from browser_use import Agent, ChatBrowserUse agent = Agent( task="Search for latest news about AI", llm=ChatBrowserUse(), calculate_cost=True # Enable cost tracking ) history = await agent.run() # Get usage from history print(f"Token usage: {history.usage}") # Or get from usage summary usage_summary = await agent.token_cost_service.get_usage_summary() print(f"Usage summary: {usage_summary}") ``` # Observability Source: https://docs.browser-use.com/open-source/development/monitoring/observability Trace agent execution steps and capture browser session recordings. ## Overview Browser Use has a native integration with [Laminar](https://laminar.sh) - open-source platform for monitoring and analyzing error patterns in AI agents. Laminar SDK automatically captures **agent execution steps, costs and browser session recordings** of Browser Use agent. Browser session recordings allow developers to see full video replay of the browser session, which is useful for debugging Browser Use agent. ## Setup Install Laminar python SDK. ```bash theme={null} pip install lmnr ``` Register on [Laminar Cloud](https://laminar.sh) or [self-host Laminar](https://github.com/lmnr-ai/lmnr), create a project and get the project API key from your project settings. Set the `LMNR_PROJECT_API_KEY` environment variable. ```bash theme={null} export LMNR_PROJECT_API_KEY= ``` ## Usage Initialize Laminar at the top of your project, and both Browser Use agent traces and session recordings will be automatically captured. ```python {7-9} theme={null} from browser_use import Agent, ChatGoogle import asyncio from lmnr import Laminar import os # At initialization time, Laminar auto-instruments # Browser Use and any browser you use (local or remote) Laminar.initialize(project_api_key=os.getenv('LMNR_PROJECT_API_KEY')) async def main(): agent = Agent( task="go to ycombinator.com, summarize 3 startups from the latest batch", llm=ChatGoogle(model="gemini-2.5-flash"), ) await agent.run() asyncio.run(main()) ``` ## Viewing Traces You can view traces in the Laminar UI by going to the traces tab in your project. When you select a trace, you can see both the browser session recording and the agent execution steps. Timeline of the browser session is synced with the agent execution steps. In the trace view, you can also see the agent's current step, the tool it is using, and the tool's input and output. Laminar ## Laminar To learn more about how you can trace and evaluate your Browser Use agent with Laminar, check out [Laminar docs](https://docs.lmnr.ai). ## Browser Use Cloud Authentication Browser Use can sync your agent runs to the cloud for easy viewing and sharing. Authentication is required to protect your data. ### Quick Setup ```bash theme={null} # Authenticate once to enable cloud sync for all future runs browser-use auth # Or if using module directly: python -m browser_use.cli auth ``` **Note**: Cloud sync is enabled by default. If you have disabled it, you can re-enable with `export BROWSER_USE_CLOUD_SYNC=true`. ### Manual Authentication ```python theme={null} # Authenticate from code after task completion from browser_use import Agent agent = Agent(task="your task") await agent.run() # Later, authenticate for future runs await agent.authenticate_cloud_sync() ``` ### Reset Authentication ```bash theme={null} # Force re-authentication with a different account rm ~/.config/browseruse/cloud_auth.json browser-use auth ``` **Note**: Authentication uses OAuth Device Flow - you must complete the auth process while the command is running. Links expire when the polling stops. # OpenLIT Source: https://docs.browser-use.com/open-source/development/monitoring/openlit Complete observability with OpenLIT tracing. ## Overview Browser Use has native integration with [OpenLIT](https://github.com/openlit/openlit) - an open-source opentelemetry-native platform that provides complete, granular traces for every task your browser-use agent performs—from high-level agent invocations down to individual browser actions. Read more about OpenLIT in the [OpenLIT docs](https://docs.openlit.io). ## Setup Install OpenLIT alongside Browser Use: ```bash theme={null} pip install openlit browser-use ``` ## Usage OpenLIT provides automatic, comprehensive instrumentation with **zero code changes** beyond initialization: ```python {5-6} theme={null} from browser_use import Agent, Browser, ChatOpenAI import asyncio import openlit # Initialize OpenLIT - that's it! openlit.init() async def main(): browser = Browser() llm = ChatOpenAI( model="gpt-4o", ) agent = Agent( task="Find the number trending post on Hacker news", llm=llm, browser=browser, ) history = await agent.run() return history if __name__ == "__main__": history = asyncio.run(main()) ``` ## Viewing Traces OpenLIT provides a powerful dashboard where you can: ### Monitor Execution Flows See the complete execution tree with timing information for every span. Click on any `invoke_model` span to see the exact prompt sent to the LLM and the complete response with agent reasoning. ### Track Costs and Token Usage * Cost breakdown by agent, task, and model * Token usage per LLM call with full input/output visibility * Compare costs across different LLM providers * Identify expensive prompts and optimize them ### Debug Failures with Agent Thoughts When an automation fails, you can: * See exactly which step failed * Read the agent's thinking at the failure point * Check the browser state and available elements * Analyze whether the failure was due to bad reasoning or bad information * Fix the root cause with full context ### Performance Optimization * Identify slow steps (LLM calls vs browser actions vs HTTP requests) * Compare execution times across runs * Optimize max\_steps and max\_actions\_per\_step * Track HTTP request latency for page navigations ## Configuration ### Custom OpenTelemetry Endpoint Configuration ```python theme={null} import openlit # Configure custom OTLP endpoints openlit.init( otlp_endpoint="http://localhost:4318", application_name="my-browser-automation", environment="production" ) ``` ### Environment Variables You can also configure OpenLIT via environment variables: ```bash theme={null} export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318" export OTEL_SERVICE_NAME="browser-automation" export OTEL_ENVIRONMENT="production" ``` ### Self-Hosted OpenLIT If you prefer to keep your data on-premises: ```bash theme={null} # Using Docker docker run -d \ -p 4318:4318 \ -p 3000:3000 \ openlit/openlit:latest # Access dashboard at http://localhost:3000 ``` ## Integration with Existing Tools OpenLIT uses OpenTelemetry internally, so it integrates seamlessly with: * **Jaeger** - Distributed tracing visualization * **Prometheus** - Metrics collection and alerting * **Grafana** - Custom dashboards and analytics * **Datadog** - APM and log management * **New Relic** - Full-stack observability * **Elastic APM** - Application performance monitoring Simply configure OpenLIT to export to your existing OTLP-compatible endpoint. # Telemetry Source: https://docs.browser-use.com/open-source/development/monitoring/telemetry Understanding Browser Use's telemetry ## Overview Browser Use is free under the MIT license. To help us continue improving the library, we collect usage telemetry with [PostHog](https://posthog.com). Telemetry may include usage metadata and task-level data such as task instructions, URLs visited, action traces, errors, and final results. We may use this information to operate, debug, secure, analyze, develop, and improve Browser Use and related offerings, including by creating aggregated, de-identified, or sanitized datasets and workflow patterns in accordance with our Terms of Service and Privacy Policy. ## Opting Out You can disable telemetry by setting the environment variable: ```bash .env theme={null} ANONYMIZED_TELEMETRY=false ``` Or in your Python code: ```python theme={null} import os os.environ["ANONYMIZED_TELEMETRY"] = "false" ``` Even when enabled, telemetry has zero impact on the library's performance. Code is available in [Telemetry Service](https://github.com/browser-use/browser-use/tree/main/browser_use/telemetry). ## Legal By using Browser Use services, you agree to our [Terms of Service](https://browser-use.com/legal/terms-of-service) and [Privacy Policy](https://browser-use.com/privacy/). # Contribution Guide Source: https://docs.browser-use.com/open-source/development/setup/contribution-guide How to contribute — code standards, PR process, testing requirements, and submitting your first pull request. ## Mission * Make developers happy * Do more clicks than human * Tell your computer what to do, and it gets it done. * Make agents faster and more reliable. ## What to work on? * This space is moving fast. We have 10 ideas daily. Let us exchange some. * Browse our [GitHub Issues](https://github.com/browser-use/browser-use/issues) * Check out our most active issues on [Discord](https://discord.gg/zXJJHtJf3k) * Get inspiration in [`#showcase-your-work`](https://discord.com/channels/1303749220842340412/1305549200678850642) channel ## What makes a great PR? 1. Why do we need this PR? 2. Include a demo screenshot/gif 3. Make sure the PR passes all CI tests 4. Keep your PR focused on a single feature ## How? 1. Fork the repository 2. Create a new branch for your feature 3. Submit a PR We are overwhelmed with Issues. Feel free to bump your issues/PRs with comments periodically if you need faster feedback. # Local Development Setup Source: https://docs.browser-use.com/open-source/development/setup/local-setup Set up for local development. Clone the repo, install dependencies, and run tests to start contributing. ## Welcome to Browser Use Development! ```bash theme={null} git clone https://github.com/browser-use/browser-use cd browser-use uv sync --all-extras --dev # or pip install -U git+https://github.com/browser-use/browser-use.git@main ``` ## Configuration Set up your environment variables: ```bash theme={null} # Copy the example environment file cp .env.example .env # set logging level # BROWSER_USE_LOGGING_LEVEL=debug ``` ## Helper Scripts For common development tasks ```bash theme={null} # Complete setup script - installs uv, creates a venv, and installs dependencies ./bin/setup.sh # Run all pre-commit hooks (formatting, linting, type checking) ./bin/lint.sh # Run the core test suite that's executed in CI ./bin/test.sh ``` ## Run examples ```bash theme={null} uv run examples/simple.py ``` # Ad-Use (Ad Generator) Source: https://docs.browser-use.com/open-source/examples/apps/ad-use Generate Instagram image ads and TikTok video ads from landing pages using browser agents, Google's Nano Banana 🍌, and Veo3. This demo requires browser-use v0.7.6+.