Repository Public
1---2title: Browser use tool3url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool4description: Let Claude navigate, read, and interact with webpages in your own browser environment with the browser use tool.5---67## Compatibility8- [ZDR](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention): eligible (excludes [Covered Models](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#model-specific-data-retention-requirements))9- Supported models: `claude-fable-5-1`, `claude-mythos-5-1`, `claude-fable-5`, `claude-mythos-5`, `claude-opus-5`, `claude-sonnet-5`, `claude-opus-4-8`10- Platforms: Claude API, Google Cloud; not available on Claude Platform on AWS, Amazon Bedrock, Microsoft Foundry1112The browser use tool lets Claude navigate, read, and interact with webpages in a browser that your application runs. Claude works with the page both through its structure (the accessibility tree, elements, forms, and tabs) and through screenshots and viewport coordinates.1314The tool is an Anthropic-defined [client toolset](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference#client-toolsets): one `browser_toolset_20260801` entry in `tools` gives Claude 27 member tools by default, such as `navigate`, `read_page`, `left_click`, and `screenshot`, plus four more when you [enable them](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#enable-optional-member-tools). Your application runs every call against its own browser automation; nothing runs on Anthropic's side. The tool isn't currently available in [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/tools).1516Choose browser use when the task stays inside webpages and means acting on them, or when pages build their content with JavaScript. When a task needs a whole desktop, use the [computer use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool), which works through screenshots and coordinates alone. For reading pages you can point Claude to, or finding sources on the web, the [web fetch tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool) and [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) are lighter. They're [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools) that the API runs for you, with no browser to operate.1718With browser use, Claude reads and acts on live webpages, so everything a page supplies is untrusted input and the actions Claude takes can have real effects. See [Security considerations](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#security-considerations) before you deploy.1920## Quick start2122The browser use tool is available on the Claude API and [Google Cloud](https://platform.claude.com/docs/en/build-with-claude/claude-on-vertex-ai): add one entry of type `browser_toolset_20260801`, with no `name`, to the `tools` array of a [Messages API](https://platform.claude.com/docs/en/api/messages/create) request.2324<CodeGroup>25 ```bash cURL26 curl https://api.anthropic.com/v1/messages \27 -H "content-type: application/json" \28 -H "x-api-key: $ANTHROPIC_API_KEY" \29 -H "anthropic-version: 2023-06-01" \30 -d '{31 "model": "claude-opus-5",32 "max_tokens": 2048,33 "tools": [34 {35 "type": "browser_toolset_20260801"36 }37 ],38 "messages": [39 {40 "role": "user",41 "content": "Open example.com/docs and tell me how to get started."42 }43 ]44 }'45 ```4647 ```bash CLI48 ant messages create <<'YAML'49 model: claude-opus-550 max_tokens: 204851 tools:52 - type: browser_toolset_2026080153 messages:54 - role: user55 content: Open example.com/docs and tell me how to get started.56 YAML57 ```5859 ```python Python60 client = anthropic.Anthropic()6162 response = client.messages.create(63 model="claude-opus-5",64 max_tokens=2048,65 tools=[{"type": "browser_toolset_20260801"}],66 messages=[67 {68 "role": "user",69 "content": "Open example.com/docs and tell me how to get started.",70 }71 ],72 )73 print(response)74 ```7576 ```typescript TypeScript77 const client = new Anthropic();7879 const response = await client.messages.create({80 model: "claude-opus-5",81 max_tokens: 2048,82 tools: [{ type: "browser_toolset_20260801" }],83 messages: [84 {85 role: "user",86 content: "Open example.com/docs and tell me how to get started."87 }88 ]89 });9091 console.log(response);92 ```9394 ```csharp C#95 var client = new AnthropicClient();9697 var parameters = new MessageCreateParams98 {99 Model = Model.ClaudeOpus5,100 MaxTokens = 2048,101 Tools = [new BrowserToolset20260801()],102 Messages =103 [104 new MessageParam105 {106 Role = Role.User,107 Content = "Open example.com/docs and tell me how to get started.",108 },109 ],110 };111112 var response = await client.Messages.Create(parameters);113 Console.WriteLine(response);114 ```115116 ```go Go117 client := anthropic.NewClient()118119 response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{120 Model: anthropic.ModelClaudeOpus5,121 MaxTokens: 2048,122 Tools: []anthropic.ToolUnionParam{123 {OfBrowserToolset20260801: &anthropic.BrowserToolset20260801Param{}},124 },125 Messages: []anthropic.MessageParam{126 anthropic.NewUserMessage(anthropic.NewTextBlock("Open example.com/docs and tell me how to get started.")),127 },128 })129 if err != nil {130 log.Fatal(err)131 }132 fmt.Println(response.RawJSON())133 ```134135 ```java Java136 import com.anthropic.models.messages.BrowserToolset20260801;137 // ...138139 void main() {140 AnthropicClient client = AnthropicOkHttpClient.fromEnv();141142 MessageCreateParams params = MessageCreateParams.builder()143 .model(Model.CLAUDE_OPUS_5)144 .maxTokens(2048L)145 .addTool(BrowserToolset20260801.builder().build())146 .addUserMessage("Open example.com/docs and tell me how to get started.")147 .build();148149 Message response = client.messages().create(params);150 IO.println(response);151 }152 ```153154 ```php PHP155 $client = new Client();156157 $response = $client->messages->create(158 maxTokens: 2048,159 messages: [160 ['role' => 'user', 'content' => 'Open example.com/docs and tell me how to get started.'],161 ],162 model: 'claude-opus-5',163 tools: [164 ['type' => 'browser_toolset_20260801'],165 ],166 );167168 echo $response;169 ```170171 ```ruby Ruby172 client = Anthropic::Client.new173174 response = client.messages.create(175 model: "claude-opus-5",176 max_tokens: 2048,177 tools: [178 { type: "browser_toolset_20260801" }179 ],180 messages: [181 {182 role: "user",183 content: "Open example.com/docs and tell me how to get started."184 }185 ]186 )187188 puts response189 ```190</CodeGroup>191192Claude's first response ends with `stop_reason: "tool_use"` and carries one or more member `tool_use` blocks, each naming a member tool in `name` and carrying `"toolset_name": "browser"`:193194```json Output195{196 "id": "msg_01HCDu4XSTLzTAcodEQ58vDo",197 "type": "message",198 "role": "assistant",199 "model": "claude-opus-5",200 "content": [201 {202 "type": "text",203 "text": "I'll open the documentation and read the page to find the getting-started instructions."204 },205 {206 "type": "tool_use",207 "id": "toolu_01NRLabsLyVHZPKxbKvkfSMn",208 "name": "navigate",209 "toolset_name": "browser",210 "input": { "url": "https://example.com/docs" }211 },212 {213 "type": "tool_use",214 "id": "toolu_01UvHU5cDyTZ2vXKf5wCkPqR",215 "name": "read_page",216 "toolset_name": "browser",217 "input": { "filter": "interactive" }218 }219 ],220 "stop_reason": "tool_use",221 "stop_sequence": null222}223```224225Your executor (the part of your application that drives the browser and produces tool results) runs `navigate`, then `read_page`. Your application returns one `tool_result` per block in its next request, echoing `toolset_name` on each. The `navigate` result reports the tab it loaded in a `browser_state` block; the `read_page` result is text in which every element carries a reference:226227```json228{229 "role": "user",230 "content": [231 {232 "type": "tool_result",233 "tool_use_id": "toolu_01NRLabsLyVHZPKxbKvkfSMn",234 "toolset_name": "browser",235 "content": [236 { "type": "text", "text": "Navigated to https://example.com/docs" },237 {238 "type": "browser_state",239 "tabs": [240 {241 "tab_id": "tab-1",242 "title": "Documentation",243 "url": "https://example.com/docs",244 "active": true245 }246 ]247 }248 ]249 },250 {251 "type": "tool_result",252 "tool_use_id": "toolu_01UvHU5cDyTZ2vXKf5wCkPqR",253 "toolset_name": "browser",254 "content": [255 {256 "type": "text",257 "text": "link \"Documentation\" [ref_1]\nlink \"Getting started\" [ref_2]\ntextbox \"Search docs\" [ref_3]\nbutton \"Search\" [ref_4]\nlink \"Pricing\" [ref_5]"258 }259 ]260 }261 ]262}263```264265Claude now holds references it can act on, so its next turn can click `ref_2` to open the getting-started page, with no need to locate the link in a screenshot first.266267## How browser use works268269Browser use runs as an agent loop in your application: Claude returns member tool calls, your executor runs them against the browser, and you return the results until Claude answers in text.270271<Steps>272 <Step title="Provide Claude with the browser use tool and a user prompt" icon="tool">273 * Add the `browser_toolset_20260801` entry, and optionally other tools, to your API request.274 * Include a user prompt that calls for working with webpages, for example, "Open example.com/docs and tell me how to get started."275 </Step>276277 <Step title="Claude responds with member tool calls" icon="wrench">278 * Claude returns one or more `tool_use` blocks in a single assistant turn; several in one turn form a batch action, for example, `left_click`, then `type`, then `key`.279 * Each block's `name` is the member name, each carries `"toolset_name": "browser"`, and `input` holds only that member's parameters, with no `action` field. The response's `stop_reason` is `tool_use`.280 </Step>281282 <Step title="Run the calls in order and return results" icon="browser">283 * Iterate every `tool_use` block in `response.content` (don't assume there's exactly one) and run them sequentially, in the order they appear, because later calls usually depend on earlier ones.284 * Return one `tool_result` per block in a new `user` message, matched by `tool_use_id`, and echo `"toolset_name": "browser"` on each. Every call must be answered or the next request is rejected.285 * If a call fails, return `is_error: true` with a text description for that block, then apply the halt rule in [Batch actions](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#batch-actions) to every later block in the turn.286 </Step>287288 <Step title="Claude continues until the task is complete" icon="arrows-clockwise">289 * Claude reads the results (page text, accessibility trees, screenshots, tab state) and, if it needs more, returns further member calls, which takes you back to step 3.290 * Otherwise, it returns a text response to the user.291 </Step>292</Steps>293294Here's a skeleton of that loop's tool-call step in two parts. First, stub member handlers stand in for your browser automation. Five members (`navigate`, `read_page`, `left_click`, `type`, and `screenshot`) return the text, or for `screenshot` the image block, that becomes the result content, and the dispatcher raises an error for any member it doesn't implement.295296<CodeGroup exclude="shell">297 ```python Python298 # Placeholder image data; a real executor captures the viewport and returns the PNG bytes299 PLACEHOLDER_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="300301302 def navigate(url):303 return f"navigated to {url}"304305306 def read_page():307 return 'link "Docs" [ref_1]\nbutton "Search" [ref_2]'308309310 def click(target):311 # A target is an element reference from read_page or find, or a viewport coordinate312 if target["type"] == "ref":313 return f"clicked {target['ref']}"314 return f"clicked at ({target['x']}, {target['y']})"315316317 def type_text(text):318 return f"typed: {text}"319320321 def capture_screenshot() -> list[ImageBlockParam]:322 # screenshot answers with an image block rather than text: return the result content list323 return [324 {325 "type": "image",326 "source": {"type": "base64", "media_type": "image/png", "data": PLACEHOLDER_PNG},327 }328 ]329330331 def handle_browser_action(name, tool_input):332 if name == "navigate":333 return navigate(tool_input["url"])334 elif name == "read_page":335 return read_page()336 elif name == "left_click":337 return click(tool_input["target"])338 elif name == "type":339 return type_text(tool_input["text"])340 elif name == "screenshot":341 return capture_screenshot()342 # Handle other actions as needed343 raise ValueError(f"Unknown or unimplemented member: {name}")344 ```345346 ```typescript TypeScript347 // Placeholder image data; a real executor captures the viewport as PNG bytes348 const PLACEHOLDER_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";349350 function navigate(url: string): string {351 return `navigated to ${url}`;352 }353354 function readPage(): string {355 return 'link "Docs" [ref_1]\nbutton "Search" [ref_2]';356 }357358 function clickElement(ref: string): string {359 return `clicked ${ref}`;360 }361362 function clickAt(x: number, y: number): string {363 return `clicked at (${x}, ${y})`;364 }365366 function typeText(text: string): string {367 return `typed: ${text}`;368 }369370 function captureScreenshot(): Anthropic.ImageBlockParam[] {371 // screenshot answers with an image block rather than text372 return [373 {374 type: "image",375 source: {376 type: "base64",377 media_type: "image/png",378 data: PLACEHOLDER_PNG,379 },380 },381 ];382 }383384 function handleBrowserAction(385 action: string,386 input: unknown,387 ): string | Anthropic.ImageBlockParam[] {388 const params: object =389 typeof input === "object" && input !== null ? input : {};390 if (action === "navigate" && "url" in params) {391 return navigate(String(params.url));392 } else if (action === "read_page") {393 return readPage();394 } else if (action === "left_click" && "target" in params) {395 // target is an element reference from read_page or a viewport coordinate396 const target: object =397 typeof params.target === "object" && params.target !== null398 ? params.target399 : {};400 if ("type" in target && target.type === "ref" && "ref" in target) {401 return clickElement(String(target.ref));402 } else if ("x" in target && "y" in target) {403 return clickAt(Number(target.x), Number(target.y));404 }405 } else if (action === "type" && "text" in params) {406 return typeText(String(params.text));407 } else if (action === "screenshot") {408 return captureScreenshot();409 }410 // Handle other actions as needed411 throw new Error(`Unknown or unimplemented member: ${action}`);412 }413 ```414415 ```csharp C#416 // Placeholder image data; a real executor captures the viewport and returns the PNG bytes417 const string PlaceholderPng = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";418419 string Navigate(string url) => $"navigated to {url}";420421 string ReadPage() =>422 """423 link "Docs" [ref_1]424 button "Search" [ref_2]425 """;426427 string ClickRef(string elementRef) => $"clicked {elementRef}";428429 string ClickAt(int x, int y) => $"clicked at ({x}, {y})";430431 // target is {"type": "ref", "ref": "ref_1"} or {"type": "coordinate", "x": 640, "y": 380}432 string Click(JsonElement target) =>433 target.GetProperty("type").GetString() == "ref"434 ? ClickRef(target.GetProperty("ref").GetString()!)435 : ClickAt(target.GetProperty("x").GetInt32(), target.GetProperty("y").GetInt32());436437 string TypeText(string text) => $"typed: {text}";438439 // screenshot answers with an image block rather than text: return the result content list440 List<Block> CaptureScreenshot() =>441 [442 new ImageBlockParam(443 new Base64ImageSource { Data = PlaceholderPng, MediaType = MediaType.ImagePng }444 ),445 ];446447 ToolResultBlockParamContent HandleBrowserAction(448 string action,449 IReadOnlyDictionary<string, JsonElement> input450 ) =>451 action switch452 {453 "navigate" => Navigate(input["url"].GetString()!),454 "read_page" => ReadPage(),455 "left_click" => Click(input["target"]),456 "type" => TypeText(input["text"].GetString()!),457 "screenshot" => CaptureScreenshot(),458 // Handle other actions as needed459 _ => throw new NotSupportedException($"Unknown or unimplemented member: {action}"),460 };461 ```462463 ```go Go464 // placeholderPNG stands in for a real capture: an executor returns the465 // viewport as base64-encoded PNG data.466 const placeholderPNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="467468 // textContent wraps text as tool_result content.469 func textContent(text string) []anthropic.ToolResultBlockParamContentUnion {470 return []anthropic.ToolResultBlockParamContentUnion{471 {OfText: &anthropic.TextBlockParam{Text: text}},472 }473 }474475 func navigate(url string) string {476 return fmt.Sprintf("navigated to %s", url)477 }478479 func readPage() string {480 return "link \"Docs\" [ref_1]\nbutton \"Search\" [ref_2]"481 }482483 func clickRef(ref string) string {484 return fmt.Sprintf("clicked %s", ref)485 }486487 func clickAt(x, y int) string {488 return fmt.Sprintf("clicked at (%d, %d)", x, y)489 }490491 func typeText(text string) string {492 return fmt.Sprintf("typed: %s", text)493 }494495 // captureScreenshot returns an image block rather than text.496 func captureScreenshot() []anthropic.ToolResultBlockParamContentUnion {497 return []anthropic.ToolResultBlockParamContentUnion{{498 OfImage: &anthropic.ImageBlockParam{499 Source: anthropic.ImageBlockParamSourceUnion{500 OfBase64: &anthropic.Base64ImageSourceParam{501 MediaType: anthropic.Base64ImageSourceMediaTypeImagePNG,502 Data: placeholderPNG,503 },504 },505 },506 }}507 }508509 func handleBrowserAction(action string, params map[string]any) ([]anthropic.ToolResultBlockParamContentUnion, error) {510 switch action {511 case "navigate":512 if url, ok := params["url"].(string); ok {513 return textContent(navigate(url)), nil514 }515 case "read_page":516 return textContent(readPage()), nil517 case "left_click":518 // target is either an element reference from read_page or a viewport coordinate519 target, _ := params["target"].(map[string]any)520 if ref, ok := target["ref"].(string); ok && target["type"] == "ref" {521 return textContent(clickRef(ref)), nil522 }523 x, xok := target["x"].(float64)524 y, yok := target["y"].(float64)525 if xok && yok {526 return textContent(clickAt(int(x), int(y))), nil527 }528 case "type":529 if text, ok := params["text"].(string); ok {530 return textContent(typeText(text)), nil531 }532 case "screenshot":533 return captureScreenshot(), nil534 // Handle other actions as needed535 default:536 return nil, fmt.Errorf("unknown or unimplemented member: %s", action)537 }538 // Reached when a member's input is missing a field or a field has the wrong type539 return nil, fmt.Errorf("invalid input for %s", action)540 }541542 ```543544 ```java Java545 /** Placeholder pixels; a real executor captures the viewport and base64-encodes the PNG. */546 static final String PLACEHOLDER_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";547548 ToolResultBlockParam.Content captureScreenshot() {549 ImageBlockParam image = ImageBlockParam.builder()550 .source(Base64ImageSource.builder()551 .mediaType(Base64ImageSource.MediaType.IMAGE_PNG)552 .data(PLACEHOLDER_PNG)553 .build())554 .build();555 return ToolResultBlockParam.Content.ofBlocks(556 List.of(ToolResultBlockParam.Content.Block.ofImage(image)));557 }558559 String navigate(String url) {560 return "navigated to " + url;561 }562563 String readPage() {564 return """565 link "Docs" [ref_1]566 button "Search" [ref_2]""";567 }568569 String clickRef(String ref) {570 return "clicked " + ref;571 }572573 String clickAt(long x, long y) {574 return "clicked at (" + x + ", " + y + ")";575 }576577 String typeText(String text) {578 return "typed: " + text;579 }580581 /** Runs one browser toolset member; {@code action} is the tool_use block's name. */582 ToolResultBlockParam.Content handleBrowserAction(String action, Map<String, JsonValue> input) {583 if (action.equals("screenshot")) {584 return captureScreenshot(); // the one member here that answers with an image block585 }586 String output = switch (action) {587 case "navigate" -> navigate(input.get("url").asStringOrThrow());588 case "read_page" -> readPage();589 case "left_click" -> {590 // target is {"type": "ref", "ref": "ref_1"} or {"type": "coordinate", "x": 640, "y": 380}591 Map<String, JsonValue> target =592 (Map<String, JsonValue>) input.get("target").asObject().get();593 if (target.get("type").asStringOrThrow().equals("ref")) {594 yield clickRef(target.get("ref").asStringOrThrow());595 }596 long x = ((Number) target.get("x").asNumber().get()).longValue();597 long y = ((Number) target.get("y").asNumber().get()).longValue();598 yield clickAt(x, y);599 }600 case "type" -> typeText(input.get("text").asStringOrThrow());601 // Handle other actions as needed602 default -> throw new UnsupportedOperationException("Unknown or unimplemented member: " + action);603 };604 return ToolResultBlockParam.Content.ofString(output);605 }606 ```607608 ```php PHP609 // Stand-in for real PNG bytes; a real executor captures the viewport610 const PLACEHOLDER_PNG = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';611612 function navigateTo(string $url): string613 {614 return "navigated to {$url}";615 }616617 function readPage(): string618 {619 return <<<'TEXT'620 link "Docs" [ref_1]621 button "Search" [ref_2]622 TEXT;623 }624625 function clickTarget(array $target): string626 {627 // A target is an element reference from read_page or find, or a viewport pixel coordinate628 if ($target['type'] === 'ref') {629 return "clicked {$target['ref']}";630 }631632 return "clicked at ({$target['x']}, {$target['y']})";633 }634635 function typeText(string $text): string636 {637 return "typed: {$text}";638 }639640 function captureScreenshot(): array641 {642 // screenshot answers with an image block rather than text, so return the result content list643 $image = [644 'type' => 'image',645 'source' => ['type' => 'base64', 'media_type' => 'image/png', 'data' => PLACEHOLDER_PNG],646 ];647648 return [$image];649 }650651 function handleBrowserAction(string $name, array $input): string|array652 {653 return match ($name) {654 'navigate' => navigateTo($input['url']),655 'read_page' => readPage(),656 'left_click' => clickTarget($input['target']),657 'type' => typeText($input['text']),658 'screenshot' => captureScreenshot(),659 // Handle other actions as needed660 default => throw new RuntimeException("Unknown or unimplemented member: {$name}"),661 };662 }663 ```664665 ```ruby Ruby666 # Stand-in image data; a real executor captures the viewport as a PNG.667 PLACEHOLDER_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="668669 def navigate(url)670 "navigated to #{url}"671 end672673 def read_page674 <<~TREE675 link "Docs" [ref_1]676 button "Search" [ref_2]677 TREE678 end679680 def click(target)681 return "clicked #{target[:ref]}" if target[:type] == "ref"682683 "clicked at (#{target[:x]}, #{target[:y]})"684 end685686 def type_text(text)687 "typed: #{text}"688 end689690 def capture_screenshot691 [692 {693 type: "image",694 source: { type: "base64", media_type: "image/png", data: PLACEHOLDER_PNG }695 }696 ]697 end698699 def handle_browser_action(name, input)700 case name701 when "navigate"702 navigate(input[:url])703 when "read_page"704 read_page705 when "left_click"706 # target is an element reference (from read_page or find) or a coordinate707 click(input[:target])708 when "type"709 type_text(input[:text])710 when "screenshot"711 capture_screenshot712 # Handle other actions as needed713 else714 raise ArgumentError, "Unknown or unimplemented member: #{name}"715 end716 end717 ```718</CodeGroup>719720The second part runs a batch in order, dispatches each block to those handlers, echoes `toolset_name` on every result, and applies the halt rule from [Batch actions](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#batch-actions), turning a handler error into an error result. The sampling loop that calls it is the one shown in [Understand the agent loop](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#understanding-the-agentic-loop), with the browser toolset in `tools`.721722<CodeGroup exclude="shell">723 ```python Python724 NOT_EXECUTED = "Not executed: an earlier action in this turn failed."725726727 def process_tool_calls(response: Message) -> list[ToolResultBlockParam]:728 """729 Run the browser actions in Claude's response in order and answer each730 one. After the first failure the rest are skipped, because Claude planned731 them assuming the earlier actions succeeded.732 """733 tool_results: list[ToolResultBlockParam] = []734 failed = False735 for block in response.content:736 # Only the browser toolset is declared; route other tools here if you add them737 if block.type != "tool_use" or block.toolset_name != "browser":738 continue739 result: ToolResultBlockParam = {740 "type": "tool_result",741 "tool_use_id": block.id,742 "toolset_name": "browser",743 }744 if failed:745 result["content"] = NOT_EXECUTED746 result["is_error"] = True747 else:748 try:749 # A string or a list of content blocks; a real executor also adds a750 # browser_state block to navigation and tab-management results751 result["content"] = handle_browser_action(block.name, block.input)752 except Exception as err:753 result["content"] = f"Error: {err}"754 result["is_error"] = True755 failed = True756 tool_results.append(result)757 return tool_results758 ```759760 ```typescript TypeScript761 const HALT_TEXT = "Not executed: an earlier action in this turn failed.";762763 function browserResult(764 toolUseId: string,765 content: string | Anthropic.ImageBlockParam[],766 isError?: boolean,767 ): Anthropic.ToolResultBlockParam {768 return {769 type: "tool_result",770 tool_use_id: toolUseId,771 toolset_name: "browser",772 content,773 is_error: isError,774 };775 }776777 function processToolCalls(778 response: Anthropic.Message,779 ): Anthropic.ToolResultBlockParam[] {780 const toolResults: Anthropic.ToolResultBlockParam[] = [];781 let failed = false;782 for (const block of response.content) {783 if (block.type !== "tool_use") {784 continue;785 }786 if (block.toolset_name !== "browser") {787 // This example declares only the browser toolset; route other tools788 // here if you add them.789 continue;790 }791 if (failed) {792 // A batch stops at its first failure; answer later actions unexecuted793 toolResults.push(browserResult(block.id, HALT_TEXT, true));794 continue;795 }796 try {797 // A string or an image block list; a real executor also adds a798 // browser_state block to navigation and tab-management results799 const result = handleBrowserAction(block.name, block.input);800 toolResults.push(browserResult(block.id, result));801 } catch (error) {802 failed = true;803 const message = error instanceof Error ? error.message : String(error);804 toolResults.push(browserResult(block.id, `Error: ${message}`, true));805 }806 }807 return toolResults;808 }809 ```810811 ```csharp C#812 const string HaltText = "Not executed: an earlier action in this turn failed.";813814 List<ContentBlockParam> ProcessToolCalls(Message response)815 {816 List<ContentBlockParam> toolResults = [];817 var failed = false;818 foreach (var block in response.Content)819 {820 if (!block.TryPickToolUse(out var toolUse))821 {822 continue;823 }824825 if (toolUse.ToolsetName != "browser")826 {827 // This example declares only the browser toolset; route other tools828 // here if you add them.829 continue;830 }831832 if (failed)833 {834 // A batch stops at its first failure; answer later actions without running them835 toolResults.Add(836 new ToolResultBlockParam(toolUse.ID)837 {838 Content = HaltText,839 IsError = true,840 ToolsetName = "browser",841 }842 );843 continue;844 }845846 try847 {848 // A string or a list of content blocks; a real executor also adds a849 // browser_state block to navigation and tab-management results850 var result = HandleBrowserAction(toolUse.Name, toolUse.Input);851 toolResults.Add(852 new ToolResultBlockParam(toolUse.ID) { Content = result, ToolsetName = "browser" }853 );854 }855 catch (Exception e)856 {857 failed = true;858 toolResults.Add(859 new ToolResultBlockParam(toolUse.ID)860 {861 Content = $"Error: {e.Message}",862 IsError = true,863 ToolsetName = "browser",864 }865 );866 }867 }868 return toolResults;869 }870 ```871872 ```go Go873 const notExecuted = "Not executed: an earlier action in this turn failed."874875 // browserToolResult builds the result for one browser action. Unlike an876 // ordinary tool result, it must echo the toolset name. A real executor also877 // adds a browser_state block to navigation and tab-management results.878 func browserToolResult(toolUseID string, content []anthropic.ToolResultBlockParamContentUnion, isError bool) anthropic.ContentBlockParamUnion {879 result := anthropic.ToolResultBlockParam{880 ToolUseID: toolUseID,881 ToolsetName: anthropic.String("browser"),882 Content: content,883 }884 if isError {885 result.IsError = anthropic.Bool(true)886 }887 return anthropic.ContentBlockParamUnion{OfToolResult: &result}888 }889890 // processToolCalls runs the browser actions in Claude's response in order and891 // builds one tool_result per tool_use block. After the first failure it skips892 // the rest: Claude planned them assuming the earlier actions succeeded.893 func processToolCalls(response *anthropic.Message) []anthropic.ContentBlockParamUnion {894 var toolResults []anthropic.ContentBlockParamUnion895 failed := false896 for _, block := range response.Content {897 switch variant := block.AsAny().(type) {898 case anthropic.ToolUseBlock:899 // This example declares only the browser toolset; route other tools here if you add them.900 if variant.ToolsetName != "browser" {901 continue902 }903 if failed {904 toolResults = append(toolResults, browserToolResult(variant.ID, textContent(notExecuted), true))905 continue906 }907 var input map[string]any908 var content []anthropic.ToolResultBlockParamContentUnion909 err := json.Unmarshal(variant.Input, &input)910 if err == nil {911 content, err = handleBrowserAction(variant.Name, input)912 }913 if err != nil {914 failed = true915 content = textContent("Error: " + err.Error())916 }917 toolResults = append(toolResults, browserToolResult(variant.ID, content, err != nil))918 }919 }920 return toolResults921 }922923 ```924925 ```java Java926 /** The exact text the toolset contract prescribes for member calls skipped after a failure. */927 static final String HALT_TEXT = "Not executed: an earlier action in this turn failed.";928929 /** Every result answering a browser toolset member echoes toolset_name. */930 ToolResultBlockParam.Builder browserResult(ToolUseBlock toolUse) {931 return ToolResultBlockParam.builder()932 .toolUseId(toolUse.id())933 .toolsetName("browser");934 }935936 /**937 * Run the browser actions in Claude's response in order and build one938 * tool_result per tool_use block. After the first failure, skip the rest:939 * Claude planned them assuming the earlier actions succeeded.940 */941 List<ContentBlockParam> processToolCalls(Message response) {942 List<ContentBlockParam> toolResults = new ArrayList<>();943 boolean failed = false;944 for (ContentBlock block : response.content()) {945 // This example declares only the browser toolset; route other tools here if you add them.946 if (!block.isToolUse() || !block.asToolUse().toolsetName().equals(Optional.of("browser"))) {947 continue;948 }949 ToolUseBlock toolUse = block.asToolUse();950 ToolResultBlockParam result;951 if (failed) {952 result = browserResult(toolUse).content(HALT_TEXT).isError(true).build();953 } else {954 try {955 Map<String, JsonValue> input =956 (Map<String, JsonValue>) toolUse._input().asObject().get();957 ToolResultBlockParam.Content output = handleBrowserAction(toolUse.name(), input);958 // A real executor also adds a browser_state block to navigation and959 // tab-management results; see "Track tabs with browser_state" on this page.960 result = browserResult(toolUse).content(output).build();961 } catch (RuntimeException e) {962 failed = true;963 result = browserResult(toolUse).content("Error: " + e.getMessage()).isError(true).build();964 }965 }966 toolResults.add(ContentBlockParam.ofToolResult(result));967 }968 return toolResults;969 }970 ```971972 ```php PHP973 const HALT_TEXT = 'Not executed: an earlier action in this turn failed.';974975 function processToolCalls(Message $response): array976 {977 $toolResults = [];978 $failed = false;979 foreach ($response->content as $block) {980 // This example declares only the browser toolset; route other tools here if you add them.981 if (!($block instanceof \Anthropic\Messages\ToolUseBlock) || $block->toolsetName !== 'browser') {982 continue;983 }984 $result = ['type' => 'tool_result', 'tool_use_id' => $block->id, 'toolset_name' => 'browser'];985 if ($failed) {986 // A batch stops at its first failure; the remaining actions are answered without running987 $toolResults[] = [...$result, 'content' => HALT_TEXT, 'is_error' => true];988 continue;989 }990 try {991 // A real executor also returns a browser_state block on navigation and tab-management results992 $toolResults[] = [...$result, 'content' => handleBrowserAction($block->name, $block->input)];993 } catch (Throwable $e) {994 $failed = true;995 $toolResults[] = [...$result, 'content' => 'Error: ' . $e->getMessage(), 'is_error' => true];996 }997 }998999 return $toolResults;1000 }1001 ```10021003 ```ruby Ruby1004 NOT_EXECUTED = "Not executed: an earlier action in this turn failed."10051006 # Run the browser actions in Claude's response in order and build one1007 # tool_result per tool_use block. After the first failure, skip the rest:1008 # Claude planned them assuming the earlier actions succeeded.1009 def process_tool_calls(response)1010 tool_results = []1011 failed = false1012 response.content.each do |block|1013 # This example declares only the browser toolset; route other tools here1014 # if you add them.1015 next unless block.type == :tool_use && block.toolset_name == "browser"10161017 result = { type: "tool_result", tool_use_id: block.id, toolset_name: "browser" }1018 if failed1019 result.update(content: NOT_EXECUTED, is_error: true)1020 else1021 begin1022 # A String, or content blocks for a screenshot. A real executor also adds1023 # a browser_state block to navigation and tab-management results.1024 result[:content] = handle_browser_action(block.name, block.input)1025 rescue => e1026 result.update(content: "Error: #{e.message}", is_error: true)1027 failed = true1028 end1029 end1030 tool_results << result1031 end1032 tool_results1033 end1034 ```1035</CodeGroup>10361037Dispatch each block on the pair (`toolset_name`, `name`) rather than on `name` alone, because a custom tool in the same request may share a member's name; [Client toolsets](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference#client-toolsets) describes the parts of this contract both toolsets share. If Claude names a member your executor doesn't implement, or one you disabled, answer that block with an [error result](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#return-errors-from-your-executor) rather than dropping it.10381039When you stream the response, each member's `input` arrives as one complete `input_json_delta` rather than as fragments, so wait for the turn to finish before running the batch.10401041### Batch actions10421043A turn with several member calls is a batch action: run the calls in the order they appear, stop at the first failure, and answer every later call with `is_error: true` and the exact text `Not executed: an earlier action in this turn failed.` A batch uses the same response shape as [parallel tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use); the difference is that you run the blocks in order rather than concurrently. Here Claude clicks the search box it found earlier, types a query, and presses Enter in one turn:10441045```json1046{1047 "role": "assistant",1048 "content": [1049 {1050 "type": "tool_use",1051 "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV",1052 "name": "left_click",1053 "toolset_name": "browser",1054 "input": { "target": { "type": "ref", "ref": "ref_3" } }1055 },1056 {1057 "type": "tool_use",1058 "id": "toolu_01Ez4kLb1nQ2vXo8sJ9pWm3c",1059 "name": "type",1060 "toolset_name": "browser",1061 "input": { "text": "install" }1062 },1063 {1064 "type": "tool_use",1065 "id": "toolu_01FkP8rTz6uYh2mNq4LsXw7v",1066 "name": "key",1067 "toolset_name": "browser",1068 "input": { "text": "Enter" }1069 }1070 ]1071}1072```10731074Your application returns three `tool_result` blocks in one `user` message, each carrying `toolset_name` and a short text acknowledgment such as `Clicked element ref_3.` Pressing Enter loads a results page, so the `key` result also carries a `browser_state` block with the tab's updated URL ([Tab context on other results](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#tab-context-on-other-results)). If the click had failed instead, its result would carry your error text and the other two results would carry the halt text, as shown under [Return errors from your executor](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#return-errors-from-your-executor).10751076You don't need to return a screenshot after every call. Claude typically ends a batch with an observation call (`screenshot`, `read_page`, or `get_page_text`), and your application can also attach its own observation, such as a fresh screenshot or accessibility tree, as an extra content block on the last result in the batch to save a round trip. Because a tab-management result must be exactly one `browser_state` block, attach it to the last result that isn't a tab-management call.10771078If your executor can run only one call per round trip, set `disable_parallel_tool_use` to `true` in `tool_choice` and Claude returns at most one member call per turn, at the cost of more round trips ([Disable parallel tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use#disable-parallel-tool-use)). The rest of the contract under [Batch actions for the computer use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#batch-actions) carries over, including one `tool_result` for every `tool_use` in the next `user` message, except for two things: the halt text and what a successful result's `content` holds. Result content follows [Member tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#member-tools) on this page instead: a `new_tab`, `switch_tab`, `close_tab`, or `list_tabs` result is exactly one `browser_state` block with no text or image ([Tab management results](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#tab-management-results)), and any other member's result may add a `browser_state` block to its text or image ([Tab context on other results](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#tab-context-on-other-results)). Where cache breakpoints inside a batch take effect is described in the `cache_control` row of the computer use tool's [Tool parameters](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#tool-parameters).10791080### Targets and coordinates10811082Member tools that act on a location take a `target` object, which is either a viewport-pixel coordinate or a reference to an element that `read_page` or `find` returned. The [Member tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#member-tools) tables write `Target` for a parameter that accepts either shape.10831084| Shape | `target.type` | Fields | Accepted by |1085| ------------------ | -------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |1086| `CoordinateTarget` | `"coordinate"` | `x`, `y` (integers, viewport pixels) | `left_click`, `right_click`, `middle_click`, `double_click`, `triple_click`, `hover`, `left_click_drag` (`from` and `target`), `left_mouse_down`, `left_mouse_up`, `mouse_move`, `scroll` |1087| `RefTarget` | `"ref"` | `ref` (an element reference such as `"ref_2"`) | `left_click`, `right_click`, `middle_click`, `double_click`, `triple_click`, `hover`, `scroll_to`, `form_input`, `file_upload` |10881089**Coordinates are viewport pixels**, the pixel space of a full-viewport `screenshot` with the origin at the top left of the rendered page; there's no surrounding desktop or window frame. The toolset declares no display dimensions and Claude infers the viewport size from the screenshots you return, so keep them one consistent size. A `zoom` doesn't change the frame, so its `region` and any coordinates Claude emits after seeing the zoomed image are still full-viewport pixels.10901091**Screenshots must fit the image limits.** The API doesn't downscale toolset images: a screenshot or zoom image over your model's [image size limits](https://platform.claude.com/docs/en/build-with-claude/vision#evaluate-image-size), or over the stricter per-image limit that applies once a request holds [more than 20 images](https://platform.claude.com/docs/en/build-with-claude/vision#request-limits), is rejected. Resize before returning, and scale Claude's coordinates back up by the inverse of your factor before dispatching them ([Size screenshots to fit image limits](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#handle-coordinate-scaling-for-higher-resolutions)).10921093**Element references come from `read_page` and `find`.** Each element in their output carries a tag such as `[ref_2]`, as in the Quick start result:10941095```text wrap1096link "Documentation" [ref_1]1097link "Getting started" [ref_2]1098textbox "Search docs" [ref_3]1099button "Search" [ref_4]1100link "Pricing" [ref_5]1101```11021103Claude passes a reference back as a `{"type": "ref", "ref": "ref_2"}` target on a later click, `hover`, `scroll_to`, `form_input`, or `file_upload` call, or as the `ref` parameter on `read_page` to read a subtree. Your executor assigns the references, keeps the mapping from each one to the underlying node (an accessibility-node ID, a stored selector, or equivalent), and acts on that node when a reference comes back.11041105References are scoped to the tab that produced them and stay valid until that tab navigates or its DOM changes materially. The API can't detect a stale or unknown reference, so when Claude passes a reference your executor no longer recognizes, return an error result such as `Error: ref_3 is stale or not found on the current page. Re-read the page to get fresh references.` Claude then reads the page again. Don't renumber references you've already handed out for a tab until it navigates, because that silently invalidates references Claude still holds.11061107Claude uses both targeting styles and switches between them based on what the page exposes; your prompt and what your executor returns steer the choice:11081109* **Prefer references where the page has a usable accessibility tree.** A reference survives layout shifts and reflows that make pixel coordinates fragile, and lets Claude act on controls that are hard to hit with a pointer.1110* **Fall back to coordinates for content the tree doesn't describe.** Canvas-rendered interfaces, embedded video or remote-desktop surfaces, heavily virtualized lists, and elements inside cross-origin iframes often have no useful node, so Claude works from `screenshot` and `zoom` and clicks by coordinate; your executor resolves which frame a coordinate lands in.1111* **Scope reads, and read the tree before you screenshot.** On large pages, `read_page` with `filter: "interactive"` or the `ref` of a container returns a focused subtree, and a tree read of a typical page often costs fewer input tokens than a screenshot while giving Claude references it can act on immediately. Screenshots remain the right observation when visual layout, images, or rendering state matter.11121113## Security considerations11141115Browser use carries risks that standard API features don't, because Claude reads and acts on content from the open web, where any page can contain text written to manipulate it.11161117<Warning>1118 To reduce these risks, take precautions such as the following:11191120 1. Run the browser and your executor in a dedicated container or virtual machine with minimal privileges, a fresh profile that holds no credentials, and no access to sensitive filesystems or internal networks; isolate any tool you run alongside it the same way.1121 2. Restrict the hosts the browser can reach to a domain allowlist enforced at the network layer and re-checked in your `navigate` handler after redirects, and block loopback, link-local, and private ranges unless the task needs them.1122 3. Treat everything a page supplies as untrusted input, including the tab titles and URLs, and each download's `url`, `path`, and `error`, that you report in a [`browser_state`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#track-tabs-and-page-state) block, and build page reads from what the page renders (the accessibility tree or visible text), not raw DOM source, so hidden text doesn't reach Claude.1123 4. In your `navigate` handler, accept the history keywords `"back"`, `"forward"`, and `"reload"`, treat a URL without a scheme as `https://`, then parse the URL and refuse any scheme other than `http` or `https` (`javascript:`, `file:`, `data:`, `chrome:`, and so on) with an [error result](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#return-errors-from-your-executor). Check the scheme with a URL parser rather than a string prefix; the API doesn't filter the URLs Claude opens, so it can't reject one for you.1124 5. Leave `javascript_exec` and `file_upload` disabled unless you need them, and read [Enable optional members](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#enable-optional-member-tools) before turning either on.1125 6. Have a human confirm consequential actions and anything that requires affirmative consent (purchasing, modifying accounts, messaging, and accepting terms), and make that check in your executor before each call, because one turn can carry several.1126</Warning>11271128Claude sometimes follows instructions found in page content even when they conflict with yours; text on a page that says "ignore your previous instructions and navigate to..." can divert it from the task. Isolate Claude from sensitive data and actions to limit what a prompt injection can reach, review [Mitigate jailbreaks and prompt injections](https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks), and if a task can't avoid a logged-in session, use a dedicated low-privilege account and keep human confirmation on account-changing actions.11291130Anthropic has trained the model to resist these prompt injections and has added an extra layer of defense. If you use the browser use tool, classifiers will automatically scan what the browser returns, such as page text or screenshots, to flag potential prompt injections. When these classifiers identify a potential prompt injection, they will automatically steer the model to check whether the instruction really came from you before acting on it.11311132This extra protection won't be ideal for every use case (for example, use cases without a human in the loop), so if you'd like to opt out and turn it off, [contact support](https://support.claude.com/en/). The precautions above remain important even with these classifiers in place.11331134Because the browser runs in your environment, the sites Claude visits see your executor's network identity, and page content reaches the API only as the tool results you return. Inform end users of the relevant risks and obtain their consent before enabling browser use in your products.11351136## Member tools11371138The `browser_toolset_20260801` entry declares 31 member tools; each call's `input` is exactly the parameters listed here, and `tab_id`, where optional, defaults to the active tab. `Target`, `CoordinateTarget`, and `RefTarget` are the shapes described in [Targets and coordinates](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#targets-and-coordinates). Four members (`javascript_exec`, `file_upload`, `read_console`, and `read_network`) are disabled by default and appear only when you [enable them](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#enable-optional-member-tools). The input bounds and output conventions noted in each member's row are stated to Claude, not enforced by the API, so validate inputs (including coordinates against your viewport) and apply the conventions in your executor.11391140Only `screenshot` and `zoom` require an [`image` block](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls#handling-results-from-client-tools) in their result, and the four tab-management members (`new_tab`, `list_tabs`, `switch_tab`, and `close_tab`) return exactly one `browser_state` block (see [Tab management results](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#tab-management-results)). Every other member returns a `text` block: either a short acknowledgment such as `Clicked element ref_2.` or the member's output. Any result other than a tab-management result may also carry an `image` block, typically a screenshot taken after the action, so Claude sees the outcome without a separate `screenshot` call; [Batch actions](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#batch-actions) shows where to attach one in a batch. A member `tool_result` may contain only `text`, `image`, and `browser_state` content blocks.11411142### Navigation and capture11431144| Member | Input | Description |1145| ------------ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |1146| `navigate` | `url`, `tab_id?` | Load an `http` or `https` URL, or move through history with `"back"`, `"forward"`, or `"reload"`. Treat a URL without a scheme as `https://` and refuse any other scheme with an error result. Return a short acknowledgment, plus a `browser_state` block when the tab's URL or title changed. |1147| `screenshot` | `tab_id?` | Capture the viewport and return an `image` block. |1148| `zoom` | `region`, `tab_id?` | Return a cropped, upscaled `image` of `region`, given as `[x0, y0, x1, y1]` in viewport pixels, for closer inspection of small text or controls. |11491150### Pointer11511152| Member | Input | Description |1153| ----------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |1154| `left_click` | `target: Target`, `modifiers?`, `tab_id?` | Left-click a coordinate or a referenced element. `modifiers` is a chord held during the click, for example, `"shift"` or `"ctrl+shift"`. |1155| `right_click` | `target: Target`, `modifiers?`, `tab_id?` | Right-click a coordinate or element. |1156| `middle_click` | `target: Target`, `modifiers?`, `tab_id?` | Middle-click a coordinate or element. |1157| `double_click` | `target: Target`, `modifiers?`, `tab_id?` | Double left-click a coordinate or element. |1158| `triple_click` | `target: Target`, `modifiers?`, `tab_id?` | Triple left-click a coordinate or element, which typically selects a line or paragraph. |1159| `hover` | `target: Target`, `tab_id?` | Move the pointer over a coordinate or element without clicking. |1160| `left_click_drag` | `from: CoordinateTarget`, `target: CoordinateTarget`, `tab_id?` | Press at `from`, drag to `target`, and release. |1161| `left_mouse_down` | `target: CoordinateTarget`, `tab_id?` | Press and hold the left button at a coordinate; pair with `left_mouse_up` for a custom drag. |1162| `left_mouse_up` | `target: CoordinateTarget`, `tab_id?` | Release the left button at a coordinate. |1163| `mouse_move` | `target: CoordinateTarget`, `tab_id?` | Move the pointer to a coordinate. |1164| `scroll` | `target: CoordinateTarget`, `scroll_direction`, `scroll_amount?`, `tab_id?` | Scroll at a viewport position. `scroll_direction` is `"up"`, `"down"`, `"left"`, or `"right"`; `scroll_amount` is in scroll-wheel notches, 1 to 10, default 3. |1165| `scroll_to` | `target: RefTarget`, `tab_id?` | Scroll a referenced element into view. |11661167### Keyboard and timing11681169| Member | Input | Description |1170| ---------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |1171| `type` | `text`, `tab_id?` | Type a literal string at the current focus. |1172| `key` | `text`, `repeat?`, `tab_id?` | Press a key or chord. `text` is a single key (`"Enter"`), a chord joined with `+` (`"ctrl+a"`), or a space-separated sequence (`"Backspace Backspace"`); `repeat` is 1 to 100, default 1. |1173| `hold_key` | `text`, `duration`, `tab_id?` | Hold a key or chord for `duration` seconds, 0 to 30. |1174| `wait` | `duration`, `tab_id?` | Pause for `duration` seconds, 0 to 30. |11751176### Page reading11771178| Member | Input | Description |1179| --------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |1180| `read_page` | `filter?`, `depth?`, `ref?`, `tab_id?` | Return the page's accessibility tree as text with each element tagged with a reference such as `[ref_2]`. With `filter` omitted, return every visible element; with `"interactive"`, only visible interactive elements; with `"all"`, also elements outside the viewport. `depth` caps the tree depth (minimum 1, default 15) and `ref` scopes the read to that element's subtree. Cap the output at 50,000 characters and say so in the text; Claude then narrows with a smaller `depth` or a `ref`. |1181| `find` | `query`, `tab_id?` | Search for elements matching a natural-language description such as `"search field"` or `"add to cart button"`, and return up to 20 matches in the same tagged format as `read_page`. |1182| `get_page_text` | `tab_id?` | Return the page's visible text as plain text, prioritizing the main article content; suited to articles, documentation, and other text-heavy pages. |11831184### Forms and files11851186| Member | Input | Description |1187| ----------------------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |1188| `form_input` | `target: RefTarget`, `value`, `tab_id?` | Set a form element's value directly. `value` is a `string`, `number`, or `boolean`; use a `boolean` for checkboxes and an option's value or visible text for selects. |1189| `file_upload` (disabled by default) | `target: RefTarget`, `paths?`, `document_ids?`, `tab_id?` | Set the files on a file-input element from `paths` on the executor's filesystem, `document_ids` your application has staged, or both; at least one is required. See [Upload files](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#upload-files). |11901191### Diagnostics and scripting11921193| Member | Input | Description |1194| --------------------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |1195| `read_console` (disabled by default) | `tab_id?` | Return the tab's console entries (log, warning, and error lines) accumulated since the last read, one line per entry. See [Read console and network activity](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#read-console-and-network-activity). |1196| `read_network` (disabled by default) | `tab_id?` | Return the tab's network requests (method, URL, status, MIME type, timing) since the last read, one line per entry. |1197| `javascript_exec` (disabled by default) | `text`, `tab_id?` | Run `text` as JavaScript in the page context and return the value of the last expression as text. See [Enable optional members](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#enable-optional-member-tools). |11981199### Tab management12001201| Member | Input | Description |1202| ------------ | ------------------- | -------------------------------------- |1203| `new_tab` | (none) | Open a tab and make it the active tab. |1204| `list_tabs` | (none) | Report the tab inventory. |1205| `switch_tab` | `tab_id` (required) | Make `tab_id` the active tab. |1206| `close_tab` | `tab_id` (required) | Close `tab_id`. |12071208On success, each of these returns exactly one `browser_state` block and no text or image; see [Tab management results](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#tab-management-results).12091210## Configure the toolset12111212Besides `type`, the toolset entry accepts `configs`, `cache_control`, and `allowed_callers`; the rules these fields share with the computer use toolset are listed under [Client toolsets](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference#client-toolsets), and this section covers the browser-specific defaults. `configs` is an object keyed by member name, and each member's value accepts two fields:12131214| Field | Default | Meaning |1215| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |1216| `enabled` | `true`, except `false` for the four [optional members](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#enable-optional-member-tools) | Whether the member is offered to Claude. |1217| `defer_loading` | `false` | Whether the toolset's definition is deferred for tool search. Must resolve to the same value on every enabled member. With the four optional members left disabled, deferring the toolset means setting it on the other 27; see [Client toolsets](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference#client-toolsets). |12181219### Enable or disable member tools12201221List only the members you want to change in `configs`; every member you omit keeps its default. For example, an executor that implements console reads but not low-level pointer or key-hold control turns `read_console` on and withholds three members:12221223```json1224{1225 "type": "browser_toolset_20260801",1226 "configs": {1227 "read_console": { "enabled": true },1228 "left_mouse_down": { "enabled": false },1229 "left_mouse_up": { "enabled": false },1230 "hold_key": { "enabled": false }1231 }1232}1233```12341235A disabled member disappears from the definition Claude sees; that doesn't guarantee Claude never names it, so your executor still answers such a call with an [error result](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#return-errors-from-your-executor).12361237### Combine with other tools12381239Declare the browser use tool alongside your own tools and other Anthropic-provided tools in the same `tools` array. A custom tool may share a member's name (your own `navigate`, for example), because `toolset_name` distinguishes Claude's calls, but no other entry may be named `browser`, and a request may contain only one browser toolset entry.12401241You can also declare it alongside the [computer use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool), either the toolset or an earlier computer use tool version. The two work independently, each in its own coordinate frame (viewport pixels here, desktop screenshot pixels there), and Claude's calls to members that share a name, such as `screenshot` or `key`, are told apart by `toolset_name`.12421243## Enable optional members12441245Four member tools are disabled by default: `javascript_exec` and `file_upload` because they widen what a manipulated page could make Claude do, and `read_console` and `read_network` because not every browser automation stack can supply those logs and they widen what page-controlled content reaches Claude. Enable each one with `configs` (for example, `"configs": {"file_upload": {"enabled": true}}`) only when your executor implements it and the task needs it.12461247### Upload files12481249`file_upload` sets the files on an `<input type="file">` element directly, which is more reliable than driving a native file chooser. Its `target` is a reference only, because the call needs the element's identity, and it takes `paths`, `document_ids`, or both:12501251* `paths` are file paths on the executor's filesystem, for deployments where the executor can read your application's files directly (the same condition under which you populate a download's `path`).1252* `document_ids` are identifiers for files your application has staged for the browser, for deployments where it can't. Your application defines what the identifiers mean; scope their resolution the way you scope `paths`, to files staged for this task.12531254```json1255{1256 "type": "tool_use",1257 "id": "toolu_01N7gVzFEfZjLjgsYwnrPgrF",1258 "name": "file_upload",1259 "toolset_name": "browser",1260 "input": {1261 "target": { "type": "ref", "ref": "ref_12" },1262 "paths": ["/home/user/uploads/summary.pdf"],1263 "tab_id": "tab-2"1264 }1265}1266```12671268Claude writes these paths while it's reading untrusted pages, so an unrestricted implementation would let a malicious page direct the upload of any file the executor can read to a site the page controls. Enable the member only when your executor resolves each path (following symlinks and `..` segments) and accepts nothing outside a dedicated, allowlisted upload directory that holds only files meant for the task. Don't reuse the browser's download directory for this; if you do, every file a page causes the browser to download becomes uploadable.12691270### Run JavaScript in the page12711272`javascript_exec` runs the expression Claude writes in the page's context and returns the value of the last expression as text; Claude writes an expression, not a `return` statement. The code runs with the page's full privileges, including its cookies, storage, and same-origin requests. Enable the member only in sessions that hold no credentials, keep the domain allowlist from [Security considerations](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#security-considerations) in force, treat the returned value as untrusted input, and log the code Claude emits.12731274### Read console and network activity12751276`read_console` returns the tab's console entries and `read_network` returns its network requests, each as text with one line per entry accumulated since the previous read of that tab. A console line carries a log, warning, or error entry; a network line carries the method, URL, status, MIME type, and timing. Entries exist only from the moment your browser automation attached to the tab, so an empty result doesn't mean a tab that was already open had no traffic.12771278These members let Claude diagnose a misbehaving page (a failed request behind a spinner, a script error behind a dead button) without repeated screenshots. Console and network entries are page-controlled and often contain secrets such as tokens in request URLs, so redact credential-like values you don't want in Claude's context and truncate very long entries before returning them.12791280## Track tabs with `browser_state`12811282Claude addresses tabs by `tab_id`, your application is the source of truth for which tabs exist, and you report that state in a `browser_state` content block that Claude never sees directly: the API renders the text Claude reads from it.12831284```json1285{1286 "type": "browser_state",1287 "tabs": [1288 {1289 "tab_id": "tab-1",1290 "title": "Documentation",1291 "url": "https://example.com/docs",1292 "active": true1293 },1294 { "tab_id": "tab-2", "title": "Pricing", "url": "https://example.com/pricing" }1295 ]1296}1297```12981299* `tabs` is the full inventory of open tabs after the call, not a delta. It may be empty; whenever it isn't, exactly one entry carries `"active": true`.1300* `state_changes` (not shown here) reports side effects of the call: a `tab_opened` entry for each tab the call opened that's still open when it finishes, whose `tab_id` must also appear in `tabs`, and [download events](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#report-downloads). Omit the field when there's nothing to report; an empty array is rejected.1301* Send the block only on results that answer a browser member call, at most once per `tool_result`, and never on a result with `is_error: true`. You express "no tab state to report" by omitting the block.1302* The API renders `tabs`, and any download entries in `state_changes`, into text for Claude. The next two sections and [Report downloads](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#report-downloads) show that text.13031304**You assign `tab_id` values.** Any stable string works, such as your automation library's page identifier or your own counter, as long as you don't reuse a `tab_id` while a tab with that identifier is still listed as open in an earlier result. The API enforces these limits on the block:13051306* Each `tab_id`, `title`, and `url` may be at most 4,096 characters, `tab_id` must be non-empty, and none may contain control characters (including newlines) or Unicode line or paragraph separators.1307* A block may list at most 100 tabs and 200 state changes.1308* The same limits apply to the `tab_id` Claude passes to `switch_tab` and `close_tab`, because the API renders it into the result text, so answer a call whose `tab_id` violates them with an error result instead of a `browser_state` block.13091310<Warning>1311 Tab titles and URLs come from the page and render into text Claude reads, so they're a prompt-injection surface. The API renders tab URLs verbatim, so sanitize page-supplied URLs before populating `tabs`. It escapes double quotes and backslashes in titles when it renders them, so don't pre-escape titles (a pre-escaped title reaches Claude double-escaped); truncating or dropping suspicious titles is still worthwhile. A download entry's `url`, `path`, and `error` also render into text Claude reads, so treat them as untrusted too; the API quotes and escapes them as it does titles. The length and character limits the API enforces are a floor, not a defense.1312</Warning>13131314### Tab management results13151316For `new_tab`, `switch_tab`, `close_tab`, and `list_tabs`, a successful result's `content` is exactly one `browser_state` block with no text or image, and the API writes the text Claude sees. A `new_tab` result's block must also carry exactly one `tab_opened` state change whose `tab_id` matches the entry marked `active: true`.13171318| Member | Text Claude sees |1319| ------------ | --------------------------------------------------------------------------------------------------------------------------- |1320| `switch_tab` | `Switched to tab {tab_id}`, taken from the call's `input.tab_id` |1321| `close_tab` | `Closed tab {tab_id}`, taken from the call's `input.tab_id` |1322| `new_tab` | `Created new tab with tab_id: {tab_id}, URL: {url}. It is now the current tab.`, taken from the entry marked `active: true` |1323| `list_tabs` | `Available tabs:` followed by one line per tab, or `No tabs available` when `tabs` is empty |13241325A `list_tabs` result whose block lists two tabs with the first one active renders as follows, with each line indented two spaces and `(current)` appended to the active tab only:13261327```text wrap1328Available tabs:1329 • tab_id tab-1: "Documentation" (https://example.com/docs) (current)1330 • tab_id tab-2: "Pricing" (https://example.com/pricing)1331```13321333An error result for one of these members is the reverse: ordinary error text in `content`, `is_error: true`, and no `browser_state` block.13341335For example, when Claude calls `new_tab` (its `input` is empty), your executor opens the tab, makes it active, and returns the inventory with one `tab_opened` entry:13361337```json1338{1339 "role": "user",1340 "content": [1341 {1342 "type": "tool_result",1343 "tool_use_id": "toolu_01WvHSbQVV9j5nWGvTmk4vNL",1344 "toolset_name": "browser",1345 "content": [1346 {1347 "type": "browser_state",1348 "tabs": [1349 { "tab_id": "tab-1", "title": "Documentation", "url": "https://example.com/docs" },1350 { "tab_id": "tab-2", "title": "Pricing", "url": "https://example.com/pricing" },1351 { "tab_id": "tab-3", "title": "", "url": "about:blank", "active": true }1352 ],1353 "state_changes": [{ "type": "tab_opened", "tab_id": "tab-3" }]1354 }1355 ]1356 }1357 ]1358}1359```13601361Claude sees `Created new tab with tab_id: tab-3, URL: about:blank. It is now the current tab.` Report the URL the tab was opened at, as here, not one it later redirects to; later results report the tab's then-current URL.13621363### Tab context on other results13641365On every other member the block is optional: send it when the set of open tabs, the active tab, or a tab's title or URL changed, or when there are `state_changes` to report, and always include the full `tabs` inventory. When a result carries both text and a `browser_state` block, the API appends a `Tab Context` footer to that result's text, separated from your text by a blank line, so Claude receives the new state without a separate `list_tabs` call:13661367```text wrap1368Tab Context:1369- Executed on tab_id: tab-11370- Available tabs:1371 • tab_id tab-1: "Documentation" (https://example.com/docs)1372 • tab_id tab-2: "Pricing" (https://example.com/pricing)1373```13741375`Executed on` names the tab the call ran on, which is its `tab_id` input when present and otherwise the active tab, and the footer's tab lines carry no `(current)` marker. Don't append this text yourself; send the structured block and let the API render it. The footer is deduplicated, so identical tab state isn't rendered again on later results and populating the block liberally costs nothing.13761377Three cases render no footer even when the block is present:13781379* Any `zoom` result.1380* A result with no `text` block (an image-only `screenshot` result, for example). Nothing is rendered or remembered for that result; the tab context appears on the next result that carries both text and a `browser_state` block, so include a short text block alongside the image when you want Claude to see a tab change on that same result. The exception is a result whose block reports a [download event](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#report-downloads). The API adds the download lines as a text block, and the footer follows them as it would on any result with text.1381* A result whose `tabs` list is empty on a call that carried no `tab_id`, because there's no tab to name.13821383For example, when Claude clicked the "Pricing" link (`ref_5`) earlier in this session, the page opened it in a new tab Claude didn't ask for, and without a report Claude would have to call `list_tabs` to discover it. Return the click's acknowledgment plus a block whose `state_changes` names the opened tab, marking whichever tab your executor left active:13841385```json1386{1387 "role": "user",1388 "content": [1389 {1390 "type": "tool_result",1391 "tool_use_id": "toolu_01EgTXj1FjE2FCTt2zNFWLao",1392 "toolset_name": "browser",1393 "content": [1394 { "type": "text", "text": "Clicked element ref_5." },1395 {1396 "type": "browser_state",1397 "tabs": [1398 {1399 "tab_id": "tab-1",1400 "title": "Documentation",1401 "url": "https://example.com/docs",1402 "active": true1403 },1404 { "tab_id": "tab-2", "title": "Pricing", "url": "https://example.com/pricing" }1405 ],1406 "state_changes": [{ "type": "tab_opened", "tab_id": "tab-2" }]1407 }1408 ]1409 }1410 ]1411}1412```14131414Claude sees `Clicked element ref_5.` followed by the Tab Context footer shown earlier. A tab opened during a call that failed gets no `tab_opened` entry, because error results carry no `browser_state`; it appears in the `tabs` inventory of the next successful result instead. In a batch, attach the block to the result of the call during which the change happened, and give every successful tab-management result its own block even when an earlier result in the same turn reported the same state.14151416### Report downloads14171418When a click or navigation starts a file download, report it in `state_changes` on the result of the call during which it happened, correlated across results by a `download_id` you assign. Downloads run asynchronously and can span several results, so there are three event types:14191420| `type` | Fields | When to send |1421| -------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |1422| `download_started` | `download_id`, `url` | On the result of the call during which the download began. `url` is the final URL the file is served from, after redirects. |1423| `download_completed` | `download_id`, `url`, `path?`, `size_bytes?` | On the result of whichever later call is running when the download finishes. Include `path` only when another tool in the same environment (for example, the [bash tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool) or `file_upload`) can read the file there; otherwise `download_id` is the download's only identifier. |1424| `download_failed` | `download_id`, `url`, `error?` | When the download fails or is canceled, with the reason in `error` if the browser provides one. |14251426The API renders each entry as one line of text for Claude, in the order the entries appear. It adds the lines after the result's text, separated by a blank line, and before any Tab Context footer. Every kind of member result carries the lines, including `zoom` and tab-management results. A result with no `text` block gets them as a text block of its own. Each line gives the `download_id` and `url`, plus `path` and `size_bytes` (for `download_completed`) or `error` (for `download_failed`) when you send them. You don't need to describe the download in your own text. The API wraps `url`, `path`, and `error` in double quotes and escapes double quotes and backslashes inside them, so don't pre-escape these values.14271428For example, a click on "Download price list (CSV)" (`ref_8`) in the Pricing tab starts a download, so the click's result carries a `download_started` entry with `download_id` `"dl-1"` and the file's URL. The download finishes while a later `screenshot` call is running, so that result's `content` holds the image, a text block such as `Screenshot captured.`, and this `browser_state` block reporting the completion under the same `download_id`:14291430```json1431{1432 "type": "browser_state",1433 "tabs": [1434 { "tab_id": "tab-1", "title": "Documentation", "url": "https://example.com/docs" },1435 {1436 "tab_id": "tab-2",1437 "title": "Pricing",1438 "url": "https://example.com/pricing",1439 "active": true1440 }1441 ],1442 "state_changes": [1443 {1444 "type": "download_completed",1445 "download_id": "dl-1",1446 "url": "https://example.com/pricing/price-list.csv",1447 "path": "/home/user/downloads/price-list.csv",1448 "size_bytes": 482131449 }1450 ]1451}1452```14531454Claude sees `Screenshot captured.` followed by a blank line and a line like this:14551456```text wrap1457Download completed with download_id: dl-1, URL: "https://example.com/pricing/price-list.csv". Saved to "/home/user/downloads/price-list.csv". Size: 48213 bytes.1458```14591460Download reports follow these rules:14611462* At most one entry per `download_id` in a single block, so a download that starts and finishes during the same call reports only `download_completed`.1463* Never send `state_changes` on an `is_error: true` result; report a download event that occurred during a failed call on the next successful result.1464* `state_changes` isn't an inventory of downloads in progress; report each event once.1465* Each entry carries only the fields its `type` declares. `size_bytes` is a non-negative integer, `download_id` is non-empty, and `download_id`, `url`, `path`, and `error` are each at most 4,096 characters with no control characters or Unicode line or paragraph separators. The `url` comes from the remote server and often carries signed query-string credentials after redirects, so strip query parameters you don't want in Claude's context and sanitize it before reporting it or using it in a filesystem path.14661467## Handle errors14681469Report a failed call to Claude as an ordinary error result: `is_error: true`, text content that says what went wrong, `toolset_name` echoed, and no `browser_state` block.14701471### Return errors from your executor14721473Make error text specific, because Claude reads it and adapts: `Error: Navigation to https://example.com/status timed out after 30 seconds. The page may be unavailable.` gives Claude something to act on where a bare `Error: navigation failed` doesn't. Other common cases:14741475<AccordionGroup>1476 <Accordion title="Refused navigation scheme">1477 ```json1478 {1479 "type": "tool_result",1480 "tool_use_id": "toolu_01LeUTyqkhRxBFq1QTG3pkwN",1481 "toolset_name": "browser",1482 "is_error": true,1483 "content": "Error: Navigation refused. Only http and https URLs are allowed."1484 }1485 ```1486 </Accordion>14871488 <Accordion title="Stale or unknown element reference">1489 ```json1490 {1491 "type": "tool_result",1492 "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV",1493 "toolset_name": "browser",1494 "is_error": true,1495 "content": "Error: ref_3 is stale or not found on the current page. Re-read the page to get fresh references."1496 }1497 ```1498 </Accordion>14991500 <Accordion title="Disabled or unimplemented member">1501 ```json1502 {1503 "type": "tool_result",1504 "tool_use_id": "toolu_013h2Q55HcNwVyapSpy2s5ZG",1505 "toolset_name": "browser",1506 "is_error": true,1507 "content": "Error: javascript_exec is not enabled in this environment."1508 }1509 ```1510 </Accordion>15111512 <Accordion title="Skipped after an earlier failure in the turn">1513 When the `left_click` on `ref_3` from [Batch actions](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#batch-actions) fails with the stale-reference error shown earlier, the `type` and `key` calls after it each get this result:15141515 ```json1516 {1517 "type": "tool_result",1518 "tool_use_id": "toolu_01FkP8rTz6uYh2mNq4LsXw7v",1519 "toolset_name": "browser",1520 "is_error": true,1521 "content": "Not executed: an earlier action in this turn failed."1522 }1523 ```1524 </Accordion>1525</AccordionGroup>15261527### Request errors15281529The API validates the toolset entry and every member `tool_use` and `tool_result` block in the conversation. When one is malformed, the API returns an `invalid_request_error` before Claude runs. In the following table, the left column names what you sent.15301531| Request | Why it fails and what to do |1532| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |1533| An option or combination the toolset entry doesn't accept, for example, a `name`, `strict: true`, `input_examples`, `defer_loading` on the entry itself, a `configs` key that isn't a member name, a field other than `enabled` or `defer_loading` in a member's `configs` value ([Configure the toolset](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#configure-the-toolset)), enabled members whose `defer_loading` values differ ([Configure the toolset](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#configure-the-toolset)), a `configs` that leaves no member enabled, a code execution caller in `allowed_callers`, the legacy `fine-grained-tool-streaming-2025-05-14` beta header on the request, a `tool_choice` of type `tool` naming `browser` or a member, or a second browser toolset entry or another tool named `browser` | These aren't supported on client toolsets. See [Client toolsets](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference#client-toolsets) for each rule and its alternative. |1534| A `tool_result` answering a member call without `"toolset_name": "browser"` or with a different value, or `toolset_name` on a result whose call wasn't a member call | Echo `toolset_name` exactly on member results, and only on them. |1535| A member `tool_use` from an earlier turn with no matching `tool_result` | Answer every member call, including the ones you didn't run after a failure. |1536| A content block other than `text`, `image`, or `browser_state` in a member result | Member results accept only those three block types. |1537| A `browser_state` block that breaks a rule in [Track tabs with `browser_state`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#track-tabs-and-page-state), for example, one on an `is_error: true` result or on a result that doesn't answer a browser member call, more than one in a result, a non-empty `tabs` without exactly one `active: true` entry, a duplicate `tab_id`, an empty `state_changes` array, a `tab_opened` whose `tab_id` isn't in `tabs`, two state changes for one `download_id` or a state-change field its `type` doesn't declare ([Report downloads](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#report-downloads)), or a field over its limits | Fix the block. "Nothing to report" is expressed by omitting the block or the `state_changes` field, never by an empty value. |1538| A successful `new_tab`, `switch_tab`, `close_tab`, or `list_tabs` result whose `content` isn't exactly one `browser_state` block, or a `new_tab` result without exactly one `tab_opened` matching the active tab | The API renders these results from the block and needs it in that exact shape; see [Tab management results](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#tab-management-results). |1539| An `image` in a result over your model's [image size limits](https://platform.claude.com/docs/en/build-with-claude/vision#evaluate-image-size), or over the stricter per-image limit that applies once the request holds [more than 20 images](https://platform.claude.com/docs/en/build-with-claude/vision#request-limits), counting screenshots and `zoom` images in earlier results | The API doesn't downscale toolset images. Resize screenshots before returning them ([Size screenshots to fit image limits](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#handle-coordinate-scaling-for-higher-resolutions)). |1540| A `model` that doesn't support `browser_toolset_20260801` | See [Compatibility](https://platform.claude.com/docs/en/agents-and-tools/tool-use/browser-use-tool#compatibility) for the supported models. |15411542## Limitations15431544* **Platform availability:** Browser use is available on the Claude API and [Google Cloud](https://platform.claude.com/docs/en/build-with-claude/claude-on-vertex-ai).1545* **Whole-input streaming only:** When you stream, each member's `input` arrives as one complete `input_json_delta` ([Client toolsets](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference#client-toolsets)).1546* **Element references are best-effort:** Highly dynamic pages (virtualized lists, canvas-rendered interfaces, pages that re-render on scroll) might not expose stable references, and Claude falls back to screenshots and coordinate clicks there.1547* **`read_console` and `read_network` depend on your browser automation:** They report only what it can capture, and only from the moment it attached to a tab.1548* **General agent limitations apply:** Latency, vision accuracy, and prompt-injection risks carry over from computer use (see the computer use tool's [Limitations](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#understand-computer-use-limitations)), and its guidance under [Optimize model performance with prompting](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#optimize-model-performance-with-prompting), [Manage screenshot history](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#manage-screenshot-history), and [Follow implementation best practices](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool#follow-implementation-best-practices) (action delays, action validation, and logging) applies to browser executors too.15491550## Pricing and data retention15511552Browser use follows the standard [tool use pricing](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview#pricing). When using the browser use tool:15531554**Toolset definition overhead:** Declaring `browser_toolset_20260801` with its default members adds about 6,600 input tokens to a request (about 6,610 on Claude Fable 5, Claude Mythos 5, Claude Opus 5, and Claude Opus 4.8, and about 6,670 on Claude Sonnet 5), which covers the member tool definitions and the tool use system prompt. Enabling all four optional members adds about 880 tokens, and disabling members with `configs` reduces the count. The exact count for a request is reported in the response `usage`, and you can estimate it in advance with the [token counting endpoint](https://platform.claude.com/docs/en/build-with-claude/token-counting).15551556**Additional token consumption:**15571558* Screenshot and zoom images returned in tool results, billed as image input (see [Vision pricing](https://platform.claude.com/docs/en/build-with-claude/vision#evaluate-image-size))1559* Text tool results returned to Claude, such as accessibility trees, page text, and console or network entries15601561<Note>1562 If you also use the computer use tool, bash tool, text editor tool, or your own tools alongside browser use, those tools have their own token costs as documented on their respective pages.1563</Note>15641565The browser session, downloads, and uploaded files stay in your environment; the screenshots, page text, and tab state you return are part of your API request content and follow the standard retention policy, or your ZDR arrangement if you have one. The browser use tool is ZDR eligible; see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention) for retention periods and eligibility across features.15661567## Next steps15681569<CardGroup cols={3}>1570 <Card title="Computer use tool" icon="computer" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool">1571 Give Claude control of a full desktop when the task leaves the browser; its implementation guidance applies to browser executors too.1572 </Card>15731574 <Card title="Handle tool calls" icon="wrench" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls">1575 Format `tool_result` blocks, return images and errors, and continue the conversation.1576 </Card>15771578 <Card title="Tool reference" icon="book" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference">1579 Browse client toolsets and every other Anthropic-provided tool, with their versions and parameters.1580 </Card>1581</CardGroup>1582