---
title: "Execute Playwright/TypeScript code against the browser"
method: POST
path: "/browsers/{id_or_name}/playwright/execute"
tags: ["Browser Playwright"]
---

# Execute Playwright/TypeScript code against the browser

`POST /browsers/{id_or_name}/playwright/execute`

## Request

- Base URL: `https://api.onkernel.com`
- URL: `https://api.onkernel.com/browsers/{id_or_name}/playwright/execute`
- Auth: HTTP bearer

Execute arbitrary Playwright code in a fresh execution context against the browser.
The code runs in the same VM as the browser, minimizing latency and maximizing throughput.
It has access to 'page', 'context', 'browser', and 'webmcp' variables.
Use 'webmcp.listTools()' to discover browser-wide WebMCP tools and
'webmcp.invokeTool(toolRef, input?, { timeoutSec? })' to invoke an exact registration.
It can `return` a value, and this value is returned in the response.

Every call runs in an executor: a dedicated Node.js process with its own browser
connection. Calls on different executors run concurrently; calls on the same executor run
one at a time. A timeout, crash, or blocked event loop in one executor does not affect
other executors. After a timeout the executor keeps its process and drops its browser
connection, so code abandoned by the timeout cannot keep driving the browser. After a
crash or a blocked event loop, the next call on that executor starts a fresh process.

Calls without 'executor' run in the executor named 'default', which always exists and is
the same as passing 'executor: "default"'. In the default executor, 'page' is bound to an
active tab reported by Chrome. In single-window sessions, this is the foreground tab. When
multiple browser windows are open, Chrome reports one active tab per window and the
selected window is unspecified. 'context' is the BrowserContext that owns the selected
page. Use 'browser.contexts()' to select a context or page explicitly.

Pass any other name to run the call in a named executor. The first call with a new name
creates it. Each named executor owns a tab: its first call opens a new background tab in
the default browser context, and 'page' is bound to that tab on every later call while it
stays open. Opening it does not change the active tab of an existing window. If the tab is
closed, the next call opens a new one and reports 'tab.created: true'. Executor code can
still reach other tabs through 'context' and 'browser'; ownership only decides what 'page'
is bound to. Use named executors to drive several tabs of one browser in parallel.

A browser can have at most 8 named executors; the default executor does not count. A call
that would create another returns 409 with the current executors; delete one with
DELETE /browsers/{id_or_name}/playwright/executors/{name}. Named executors are not removed
automatically while the browser runs; when it shuts down, they are removed and their tabs
closed.

## Path parameters

- `id_or_name` string, required

## Headers

- `Idempotency-Key` string

## Request body

- ExecutePlaywrightRequest: Request to execute Playwright code
  - `code` string, required: TypeScript/JavaScript code to execute. The code has access to 'page', 'context', and 'browser' variables. It runs within a function, so you can use a return statement at the end to return a value. This value is returned as the `result` property in the response. Example: "await page.goto('https://example.com'); return await page.title();"
  - `executor` string: Name of a Playwright executor. Calls with the same name run in the same executor, one at a time; the first call with a new name creates it. Calls on different executors run concurrently. 'default' names the executor that runs calls without a name.
  - `timeout_sec` integer: Maximum execution time in seconds. Default is 60.

## Response `200`

Code executed successfully

- ExecutePlaywrightResult: Result of Playwright code execution
  - `error` string: Error message if execution failed
  - `result` unknown
  - `stderr` string: Standard error from the execution
  - `stdout` string: Standard output from the execution
  - `success` boolean, required: Whether the code executed successfully
  - `tab` PlaywrightTab: The tab 'page' was bound to for this call. Absent if the call failed before binding a tab.
    - `created` boolean, required: Whether this call opened the tab. For a named executor this happens on its first call and after its previous tab was closed. For the default executor it happens only when the browser had no open page.
    - `target_id` string, required: CDP page target ID of the tab

## Other responses

- `400`: Bad Request – invalid input
- `409`: The call would create a named executor beyond the per-browser limit
- `422`: The Idempotency-Key was already used for a request with different parameters
- `500`: Internal Server Error
- `default`: Error returned by the browser or its proxy

## Changes

- **2026-10-28** `5e31244c396e`: 2 info
  - added the new optional `header` request parameter `Idempotency-Key`
  - added the non-success response with the status `422`
- **2026-10-07** `9d6785128a5e`: 3 info
  - added the new optional request property `executor`
  - added the non-success response with the status `409`
  - added the optional property `tab` to the response with the `200` status

[Operation history](https://skmtc.dev/onkernel/apis/kernel-api/changes/browsers/:id_or_name/playwright/execute/post.md)

---

[API](https://skmtc.dev/onkernel/apis/kernel-api.md) - [All operations](https://skmtc.dev/onkernel/apis/kernel-api/llms.txt) - [OpenAPI document](https://skmtc.dev/onkernel/apis/kernel-api/revisions/0f6cf4f58fe6?raw)
