TypeScript SDK Reference

Package browserscale-ts. All methods return a Promise; the return types below show the resolved value (the Promise<…> wrapping is implicit).
class

CloudBrowser

71 methods

CloudBrowser is the SDK-side handle for an active browserscale browser session.

One CloudBrowser corresponds to exactly one browser context, which always has at least one page; its commands act on the primary page.

Construct via rentBrowser() / createWebSocketBrowser() — never directly.

addReaction

method on CloudBrowser
addReaction(match: Locator, opts: ReactionOpts | undefined): string

Registers a one-shot "reaction": the browser watches for the match locator in the background and, as soon as it matches, clicks it the same way CloudBrowser.click does (scroll, human path, occlusion check) — then removes itself. The reaction yields to any in-flight input action and only fires while the pointer is idle, so a reaction naturally slots into the gaps of a retrying foreground action (e.g. it dismisses a newsletter modal blocking a CloudBrowser.click, after which the click's own retry succeeds). Reactions are scoped to the page and torn down automatically when the page/session ends.

match must be a css() or js() Locator — node()/at() are rejected. Use .inAllFrames() to watch every frame and .visible(false) to opt out of the default visibility gate. Pass a ReactionOpts to click a different target (on), change the button/click count, or the poll cadence.

Parameters
matchLocatorthe css()/js() locator to watch for
optsReactionOpts | undefinedoptional reaction customization; see ReactionOpts
// Auto-dismiss a consent button whenever it appears, in any frame.
const id = await browser.addReaction(css("button#accept").inAllFrames());
// Watch for a newsletter modal, but click its close "X" instead.
const id = await browser.addReaction(css("#newsletter-modal"), {
on: css(".modal-close"),
});
Returns
stringthe reactionId (pass to CloudBrowser.removeReaction)
Throws
BrowserScaleError`match` (or `opts.on`) is not a css()/js() locator, or a server/transport error

captureNetwork

method on CloudBrowser
captureNetwork(opts: NetworkCaptureOptions, onExchange: NetworkExchangeHandler): NetworkCapture

Starts capturing the session's network traffic and returns a live view of it.

Every request matching opts.patterns is reported once it completes, and "every request" is literal: capture sits in the browser process rather than in a page, so cross-process iframes, workers and service workers are included, the headers are the ones actually put on the wire (Cookie and Sec-* included), and each hop of a redirect chain arrives as its own exchange. Requests are never paused, so the page loads at full speed.

This resolves as soon as the capture is running; onExchange then fires in the background while you drive the browser. The capture is armed only after the subscription exists, so nothing that happens after this resolves is missed. Call NetworkCapture.stop when done — it disarms the capture server-side, which an aborted transport alone does not.

Parameters
optsNetworkCaptureOptionswhich requests to capture and whether to keep bodies
onExchangeNetworkExchangeHandlercalled per exchange; see NetworkExchangeHandler for the ordering and blocking rules
const capture = await browser.captureNetwork(
{ patterns: ["*\/api/*"], bodies: "text" },
(ex) => console.log(ex.statusCode, ex.method, ex.url),
);
try {
await browser.navigate("https://example.com");
} finally {
await capture.stop();
}
Returns
NetworkCaptureNetworkCapture handle for stopping the capture and inspecting how it ended

clearCookies

method on CloudBrowser
clearCookies(): void

Deletes every cookie in the browser context.

Rejects only on a transport failure - a dead session, a page that is gone, a broken connection. This call has no semantic failure of its own, so there are no error codes to branch on.

await browser.clearCookies();
Returns
void

clearStorage

method on CloudBrowser
clearStorage(origin: string | undefined): void

Deletes localStorage in the browser context.

Parameters
originstring | undefinedif set, only this origin's storage is deleted (e.g. "https://example.com"); omit to delete all origins
// Wipe one origin.
await browser.clearStorage("https://example.com");

// Wipe everything.
await browser.clearStorage();
Returns
void

click

method on CloudBrowser
click(target: Locator, opts: ClickOpts | undefined): ElementResult

Triggers a single left mouse click on the given target.

The browser scrolls the element into view if needed, moves the cursor along a human-like path, then dispatches a full mouseDown+mouseUp at a randomized point inside the element's bounding rect.

For right-click, double-click, press/release-only, or to override the target frame, pass a ClickOpts object as the second argument.

Parameters
targetLocatorlocator describing what to click; at is also valid
optsClickOpts | undefinedoptional click customization; see ClickOpts
try {
await browser.click(css("button.submit"));
} catch (e) {
if (e instanceof ClickError) console.log(e.code, e.occluder?.tagName);
}
// Right double-click on a context menu trigger.
await browser.click(css("li.menu"), { button: "right", clickCount: 2 });
Returns
ElementResultElementResult with success, resolved frameId, backendNodeId, post-scroll isVisible, element bounds, and the root-viewport (rootX, rootY) where the click landed
Throws
BrowserScaleErrorinvalid locator, or a server/transport error (element not found, frame not found, timeout, page closed)
ClickErrorthe target was found but the click could not land because another element covered it; `.code`, `.occluder` and `.result` describe the blocker and the resolved coordinates

dragBy

method on CloudBrowser
dragBy(target: Locator, offsetX: number, offsetY: number): DragResult

Picks up the target and drops it at an offset relative to the pickup point.

The browser presses the left mouse button at a pickup point inside the element, drags along a human-like path to (pickupX+offsetX, pickupY+offsetY), then releases. at is not a valid target — drag needs a real element.

Parameters
targetLocatorlocator describing the element to pick up
offsetXnumberhorizontal distance to drag, in CSS pixels
offsetYnumbervertical distance to drag, in CSS pixels
await browser.dragBy(css(".slider .handle"), 120, 0);
Returns
DragResultDragResult with the resolved frameId, backendNodeId and the final cursor position (rootX, rootY) where the drop happened
Throws
BrowserScaleErrorinvalid locator or a server/transport error
DragErrorthe source could not be acquired/pressed; `.code` and `.clickError` describe the underlying click-core failure

dragTo

method on CloudBrowser
dragTo(target: Locator, absoluteX: number, absoluteY: number): DragResult

Picks up the target and drops it at absolute root-viewport coordinates.

Same gesture as dragBy, but the drop destination is in page coordinates rather than relative to the pickup point.

Parameters
targetLocatorlocator describing the element to pick up
absoluteXnumberhorizontal drop coordinate in the root viewport
absoluteYnumbervertical drop coordinate in the root viewport
await browser.dragTo(css(".card"), 800, 400);
Returns
DragResultDragResult with the resolved frameId, backendNodeId and the final cursor position (rootX, rootY) where the drop happened
Throws
BrowserScaleErrorinvalid locator or a server/transport error
DragErrorthe source could not be acquired/pressed; `.code` and `.clickError` describe the underlying click-core failure

evaluate

method on CloudBrowser
evaluate(expression: string): EvaluateResult<T>

Runs a JavaScript expression in the page's main frame.

The expression's return value is JSON-serialized server-side and parsed eagerly into .value. When the expression returns a DOM element the .value is null and the element metadata (backendNodeId, isVisible, bounds) is populated instead — use node in subsequent calls to act on it.

The generic T is a TypeScript hint only — there is no runtime validation that the JS expression actually returned that type.

A falsy answer and a broken expression are different outcomes. Returning null, false or undefined is a successful evaluation and resolves normally; an expression that throws or will not compile rejects with a CommandError, so a typo can never read as "the page says null".

Parameters
expressionstringJavaScript expression evaluated in the main frame
const res = await browser.evaluate<string>("document.title");
console.log(res.value);
// Telling a false answer from a broken expression.
try {
const res = await browser.evaluate<boolean>("window.__ready === true");
if (!res.value) { /* legitimately not ready yet *\/ }
} catch (e) {
if (e instanceof CommandError) throw new Error(`expression broken: ${e.message}`);
throw e;
}
Returns
EvaluateResult<T>EvaluateResult with either value (non-Element) or element metadata (Element)
Throws
CommandErrorcode `"threw"` (the expression raised; the message carries the exception text), `"not_run"` (it could not be compiled, or execution never started), `"aborted"` (the browser stopped execution) or `"no_context"` (the frame had no live script context)

evaluateInFrame

method on CloudBrowserinherits evaluate
evaluateInFrame(frameId: string, expression: string): EvaluateResult<T>

Runs a JavaScript expression in the given frame.

Same semantics as evaluate but targets a specific frame instead of the main frame. Useful for evaluating inside OOPIFs (out- of-process iframes) found via getPages. ALL_FRAMES is not supported here.

Parameters
frameIdstringid of the frame to evaluate in
expressionstringJavaScript expression evaluated in the main frame
const pages = await browser.getPages();
const iframeId = pages[0].frameTree.children[0].frameId;
await browser.evaluateInFrame(iframeId, "location.href");
Returns
EvaluateResult<T>EvaluateResult with either value (non-Element) or element metadata (Element)
Throws
CommandErrorcode `"threw"` (the expression raised; the message carries the exception text), `"not_run"` (it could not be compiled, or execution never started), `"aborted"` (the browser stopped execution) or `"no_context"` (the frame had no live script context)

fill

method on CloudBrowser
fill(target: Locator, text: string, opts: FillOpts | undefined): ElementResult

Clicks the target and types text into it, appending to any existing content.

The browser scrolls the element into view, moves the cursor along a human-like path, clicks to focus, then types the text character- by-character with QWERTZ keyboard simulation and human-like timing.

To overwrite the field instead of appending, pass { clearFirst: true }.

at is not a valid target — fill requires an actual element.

Parameters
targetLocatorlocator describing the input element
textstringtext to type into the element
optsFillOpts | undefinedoptional fill customization; see FillOpts
await browser.fill(css("input[name=email]"), "user@example.com");
// Wipe the field first, then type fresh content.
await browser.fill(css("input[name=email]"), "user@example.com", { clearFirst: true });
Returns
ElementResultElementResult with success, resolved frameId, backendNodeId and the root-viewport (rootX, rootY) where the element was clicked
Throws
BrowserScaleErrorinvalid locator, or a server/transport error (element not found, frame not found, timeout, page closed)
FillErrorthe field could not be focused/typed; `.code` and `.clickError` (the underlying click-core failure) describe why

followScript

method on CloudBrowser
followScript(runId: string, onEvent: ScriptEventHandler): ScriptFollow

Watches script output in this session without starting anything.

For the case CloudBrowser.startScript cannot cover: a run somebody else launched, or one this page started before it reloaded. Several readers can watch the same session, each with its own buffer.

Only output produced from now on arrives — lines printed before the subscription existed are not kept. A run that has already finished is therefore invisible here; CloudBrowser.listScriptRuns is how you tell that apart from a run that is merely quiet.

Parameters
runIdstringrun to follow, or "" to follow every run in the session
onEventScriptEventHandlercalled per event; see ScriptEventHandler for the ordering and blocking rules
const follow = await browser.followScript(runId, (ev) => {
if (ev.log) console.log(ev.log.message);
});
try {
await follow.wait();
} finally {
await follow.stop();
}
Returns
ScriptFollowScriptFollow handle for stopping the subscription

getApiKey

method on CloudBrowser
getApiKey(): string

Returns the API key used to rent this session.

Returns
string

getAuthSession

method on CloudBrowser
getAuthSession(): AuthSession | undefined

Exports the signed-in primary account and DBSC sessions of this browser context.

State is read in the browser process, so no page needs to be open. Returns undefined when the context has neither a signed-in account nor DBSC sessions.

const auth = await browser.getAuthSession();
if (auth) await fs.writeFile("auth.json", JSON.stringify(auth));
Returns
AuthSession | undefinedAuthSession, or undefined when there is nothing to export

getCookies

method on CloudBrowser
getCookies(): CookieParam[]

Returns all cookies currently stored in this session's browser context.

const cookies = await browser.getCookies();
for (const c of cookies) console.log(c.name, "=", c.value);
Returns
CookieParam[]CookieParam[], one per cookie in the context

getDOM

method on CloudBrowser
getDOM(frameId: string, opts: GetDOMOpts | undefined): DOMResult

Returns the DOM in CDP DOM.Node shape for the requested frame.

The shape matches Chrome DevTools' Protocol DOM.Node — useful for piping into agent loops or visualizers that already speak CDP. For a much smaller agent-oriented payload, prefer getObservation instead. The cheap polling endpoint is getDOMHash.

Parameters
frameIdstringid of the frame to dump; empty string targets the main frame
optsGetDOMOpts | undefinedoptional depth: -1 full tree (default), 0 root only, N root + N descendant levels
const { dom } = await browser.getDOM();
Returns
DOMResultDOMResult with the JSON string in .dom (the .hash field is populated by getDOMHash, not by this call)

getDomChildren

method on CloudBrowser
getDomChildren(backendNodeId: number, frameId: string, depth: number | undefined): { children: string; seq: number }

Fetches a node's children and starts reporting changes inside them. DomMirror.expand calls this and folds the result into the tree.

On an <iframe> the one child is the document it hosts, and this call is what starts mirroring that frame.

An id that is simply unknown is not a failure: the call resolves with an empty result.

Parameters
backendNodeIdnumber
frameIdstring
depthnumber | undefined
Returns
{ children: string; seq: number }
SeeCommandError for reading the code off the rejection

getDOMHash

method on CloudBrowser
getDOMHash(frameId: string): string

Returns sha256:8 of the full-tree DOM JSON for cheap polling-based change detection.

Computing a hash is much cheaper than transferring the full tree — pair this with getDOM only when the hash differs from your last snapshot.

Parameters
frameIdstringid of the frame to hash; empty string targets the main frame
const hash = await browser.getDOMHash();
if (hash !== lastHash) {
// DOM changed → re-fetch
}
Returns
string16-char hex string (the first 8 bytes of sha256 of the DOM JSON)

getDomRevision

method on CloudBrowser
getDomRevision(frameId: string): number

A frame's mutation counter, incremented on every change the document sees. O(1) in the browser and the change detector to poll if you are not consuming mirror events.

Prefer this over CloudBrowser.getDOMHash, which serializes the whole tree just to hash it. The two answer different questions: a hash compares content, a revision only says whether this document moved since you last asked.

Rejects only on a transport failure - a dead session, a page that is gone, a broken connection. This call has no semantic failure of its own, so there are no error codes to branch on.

Parameters
frameIdstring
Returns
number

getFingerprint

method on CloudBrowser
getFingerprint(): string

Returns the browser fingerprint id in use for this session.

Returns
string

getObservation

method on CloudBrowser
getObservation(opts: GetObservationOpts | undefined): string

Returns a compact, frame-aware view of the visible page — the first thing to reach for on an unfamiliar page, and the cheapest way to re-read the current state afterwards.

Each frame opens with header lines carrying the URL, the title and the scroll position, then one line per visible element:

`` input#email47 type="email" name="loginId" value="a@b.com" required click "E-Mail" ``

It spans every frame, pierces open and closed shadow roots, enumerates <select> options, and reports live form state: value= is what is typed in right now (passwords as a length), checked= for boxes. The trailing quoted string is always the label or text, never the value, so an empty and a prefilled field stay distinguishable. Because the headers already carry URL, title and scroll offset, this replaces the usual handful of evaluate probes after each step.

On what to do with the result: backendNodeId (the 47 above) is a handle for this session and can be passed straight to click/fill via node. It does not survive a new document, so for anything you write into a script, target with css or js instead — those calls return the backendNodeId they resolved to, which lets you confirm the durable anchor hits the element you saw.

Parameters
optsGetObservationOpts | undefinedoptional format and budget overrides; see GetObservationOpts
const obs = await browser.getObservation();
console.log(obs);
Returns
stringthe observation in the requested format, ready to hand to a model
SeeCommandError for reading the code off the rejection

getPages

method on CloudBrowser
getPages(): PageInfo[]

Returns all open pages (tabs and popups) for this session's browser context.

Each PageInfo carries the page's URL, title, viewport and a full nested frame tree (out-of-process iframes are children of the page's main frame).

const pages = await browser.getPages();
for (const p of pages) console.log(p.url, p.title);
Returns
PageInfo[]PageInfo[] for every page currently open in the context

getSelection

method on CloudBrowser
getSelection(): string

Returns the current text selection.

Walks every frame and returns the first non-empty selection found — useful for "copy what the user highlighted" flows. Returns an empty string when nothing is selected anywhere.

const sel = await browser.getSelection();
console.log("user selected:", sel);
Returns
stringthe selected text, or "" when nothing is selected

getSessionId

method on CloudBrowser
getSessionId(): string

Returns the unique server-assigned id for this browser session.

Returns
string

getStorage

method on CloudBrowser
getStorage(origin: string | undefined): StorageOriginEntry[]

Returns the localStorage contents of this session's browser context, grouped by origin.

The storage database is read directly in the browser process, so no page needs to be open. Only first-party localStorage is included — sessionStorage is per-tab and not covered.

Parameters
originstring | undefinedif set, only this origin is returned (e.g. "https://example.com"); omit to get all origins
const storage = await browser.getStorage();
for (const e of storage) {
for (const { key, value } of e.items) console.log(e.origin, key, "=", value);
}
Returns
StorageOriginEntry[]StorageOriginEntry[], one per origin with localStorage data

getStreamConfig

method on CloudBrowser
getStreamConfig(): IceServer[]

Returns the ICE servers (TURN URL + short-lived credentials) to put in your RTCPeerConnection BEFORE creating the offer, so it can gather relay candidates.

Live streaming is a two-step, client-offerer handshake: call getStreamConfig, build your peer with the returned servers, create an offer, then pass its SDP to startStream and apply the answer.

const ice = await browser.getStreamConfig();
const pc = new RTCPeerConnection({ iceServers: ice });
Returns
IceServer[]the ICE servers for the client RTCPeerConnection

getUsage

method on CloudBrowser
getUsage(): SessionUsage

Reports what this session's browser has consumed so far.

Everything but minMemory and averageMemory only ever grows, so polling and diffing two readings gives the cost of what ran in between. The final figures need no call of their own: CloudBrowser.stopBrowser resolves with them.

const before = await browser.getUsage();
await browser.navigate("https://example.com");
const after = await browser.getUsage();
console.log(`navigation cost ${after.cpuTime - before.cpuTime}s of CPU`);
Returns
SessionUsageSessionUsage as of now

highlightNode

method on CloudBrowser
highlightNode(backendNodeId: number, frameId: string): void

Paints a debug overlay over the node identified by backendNodeId.

Useful for visual debugging of agent flows — the overlay stays until the next call. Pass backendNodeId <= 0 to clear any current highlights.

Parameters
backendNodeIdnumberid of the node to highlight, or <= 0 to clear
frameIdstringid of the frame the node lives in; empty string targets the main frame
await browser.highlightNode(res.backendNodeId, res.frameId);
Returns
void

insertText

method on CloudBrowser
insertText(text: string): void

Pastes text at the current caret using IME-style input.

No individual key events are dispatched; the entire string is committed at once via Input.insertText. Whatever element currently has focus receives the text. Use click or fill first if you need a specific element to be focused.

Parameters
textstringthe text to insert at the caret
await browser.insertText("hello world");
Returns
void
SeeCommandError for reading the code off the rejection

inspectAtPosition

method on CloudBrowser
inspectAtPosition(x: number, y: number): InspectResult

Hit-tests at the viewport-relative (x, y) and returns the topmost element under that point.

Mirrors what the live-UI overlay does on hover. Elements with pointer-events:none are skipped — the result is the actual click target, not the visually-topmost node. A backendNodeId === 0 in the result means nothing was found.

Parameters
xnumberviewport-relative x in CSS pixels
ynumberviewport-relative y in CSS pixels
const r = await browser.inspectAtPosition(200, 300);
console.log(r.tagName, r.textContent);
Returns
InspectResultInspectResult with the resolved backendNodeId, frameId, tag name, trimmed textContent, visibility and bounds

listReactions

method on CloudBrowser
listReactions(): ReactionInfo[]

Returns the still-pending reactions registered for the current page. Reactions that have already fired (one-shot) are not included.

const pending = await browser.listReactions();
for (const r of pending) console.log(r.reactionId, r.matchSelector);
Returns
ReactionInfo[]the pending reactions for the page

listScriptRuns

method on CloudBrowser
listScriptRuns(): ScriptRunInfo[]

Reports the scripts still running in this session.

Only runs in flight — a finished run is reported once on the event stream and then forgotten, so this is not a history. Its use is finding work this caller did not start: a script a previous page left behind, which CloudBrowser.stopScripts needs an id to name.

for (const run of await browser.listScriptRuns()) {
console.log(run.runId, run.runningMs);
}
Returns
ScriptRunInfo[]one entry per run still executing

loadHTML

method on CloudBrowser
loadHTML(url: string, html: string, opts: LoadHTMLOpts | undefined): void

Serves a synthetic response for the next navigation to url.

Registers a one-shot interceptor that intercepts the next request to url and replies with the supplied html and headers instead of going to the network. Useful for snapshotted pages, test fixtures, and offline replays. Pair with navigate to trigger the load.

Parameters
urlstringURL pattern that, when navigated to, returns the html
htmlstringresponse body to serve
optsLoadHTMLOpts | undefinedoptional headers and statusCode (default 200)
await browser.loadHTML("https://example.com", "<h1>hi</h1>");
await browser.navigate("https://example.com");
Returns
void
SeeCommandError for reading the code off the rejection

mirrorDom

method on CloudBrowser
mirrorDom(opts: DomMirrorOptions, onChange: DomChangeHandler, onResync: DomResyncHandler | undefined): DomMirror

Starts a live copy of a frame's DOM and keeps it up to date.

The browser sends the top of the tree once, then reports only what changed in the part you expanded. Everything else costs a child count per batch, no matter how much churns inside it — which is what makes this usable on a page that rewrites a list sixty times a second, where re-fetching the document on a timer is not.

Expand and collapse as the user opens and closes nodes; that is what moves the boundary of what gets reported. The returned DomMirror holds the tree and exposes expand, collapse and reveal.

The handler runs after each applied batch. mirror.root is a new object whenever anything under it changed and the untouched parts keep their identity, so rendering straight from it with memoized components is cheap.

One mirror covers the whole page as ONE tree. An <iframe> is an ordinary element whose single child is the document it hosts; expanding it fetches that document and starts mirroring the frame, however deeply nested and whether or not it is cross-origin. Unlike the inlining CloudBrowser.getDOM does, these regions stay live — and frames nobody opened cost nothing.

``ts const mirror = await browser.mirrorDom({ pierce: true }, () => { render(mirror.root); }); await mirror.expand(bodyNode); // ... await mirror.stop(); ``

Parameters
optsDomMirrorOptionsinitial depth and whether to pierce shadow roots
onChangeDomChangeHandlercalled after every change, including the first snapshot
onResyncDomResyncHandler | undefinedcalled when the copy had to be rebuilt, after the new tree is in place. Rebuilding is automatic; this is for telling the user why their expanded nodes collapsed.
Returns
SeeCommandError for reading the code off the rejection

modifyRequest

method on CloudBrowser
modifyRequest(urlPattern: string, opts: {
      body?: string;
      modifications?: HeaderModification[];
      timeoutMs?: number;
    } | undefined): InterceptedRequest | null

Waits for the next request whose URL matches urlPattern, applies the supplied header modifications (and optional body replacement), then forwards the modified request.

One-shot: consumes the first matching request. Each modification is a plain HeaderModification object literal.

Parameters
urlPatternstringURL wildcard to wait for
opts{ body?: string; modifications?: HeaderModification[]; timeoutMs?: number; } | undefinedoptional body (replacement request body), modifications (header changes), and timeoutMs (per-call timeout)
const req = await browser.modifyRequest("*\/api/me", {
modifications: [
  { action: "add", name: "X-Trace", value: "abc123" },
  { action: "remove", name: "Cookie" },
],
timeoutMs: 5000,
});
console.log("forwarded headers:", req?.headers);
Returns
InterceptedRequest | nullInterceptedRequest carrying the method/URL/headers/body that were actually sent on the wire after modifications were applied; null when no request payload was reported
SeeCommandError for reading the code off the rejection

moveTo

method on CloudBrowser
moveTo(target: Locator): ElementResult

Moves the mouse cursor over the given target.

The browser scrolls the target into view first if necessary, then animates the cursor along a human-like path to the element's center (or to the viewport coordinate when target is at).

Parameters
targetLocatorlocator describing where to move; at is also valid
await browser.moveTo(css("nav .menu"));
Returns
ElementResultElementResult with the resolved frameId, backendNodeId, post-scroll isVisible, element bounds and the root-viewport (rootX, rootY) where the cursor ended up
Throws
BrowserScaleErrorinvalid locator or a server/transport error
MoveErrorthe target could not be located (`.code` is `"not_found"`); `.result` carries the resolved payload

pressKey

method on CloudBrowser
pressKey(key: string, opts: { code?: string; modifiers?: number; location?: number } | undefined): void

Fires a single key-down event.

Only the keydown half is dispatched — pair with releaseKey for a full press cycle. The event targets whichever element currently has focus.

Parameters
keystringDOM KeyboardEvent.key value (e.g. "Enter", "a", "ArrowLeft")
opts{ code?: string; modifiers?: number; location?: number } | undefinedoptional key customization: code (DOM KeyboardEvent.code, e.g. "KeyA"), modifiers (bit-flag: Alt=1, Ctrl=2, Meta=4, Shift=8), location (0=standard, 1=left, 2=right, 3=numpad)
// Ctrl+A
await browser.pressKey("a", { code: "KeyA", modifiers: 2 });
await browser.releaseKey("a", { code: "KeyA", modifiers: 2 });
Returns
void
SeeCommandError for reading the code off the rejection

readCanvas

method on CloudBrowser
readCanvas(target: Locator, opts: ReadCanvasOpts | undefined): ReadCanvasResult

Reads the pixels of a <canvas> element directly in the renderer, bypassing the origin-clean (tainted) security check and without executing any page JavaScript — so cross-origin/tainted canvases (common in captchas) read fine where a normal toDataURL / getImageData would throw a SecurityError.

at is not a valid target — a real <canvas> element is required.

Parameters
targetLocatorlocator for the <canvas>; css, js or node
optsReadCanvasOpts | undefinedoptional format, quality, sub-rectangle and frame override; see ReadCanvasOpts
const res = await browser.readCanvas(css("#game canvas"));
await fs.writeFile("canvas.png", Buffer.from(res.dataBase64, "base64"));
// Read the left half as JPEG at quality 80.
const res = await browser.readCanvas(css("canvas"), {
format: "jpeg", quality: 80, sw: 150, sh: 300,
});
Returns
ReadCanvasResultReadCanvasResult with the base64 image in dataBase64, the canvas width/height, resolved frameId/backendNodeId and the originClean flag
SeeCommandError for reading the code off the rejection

readNetworkBody

method on CloudBrowser
readNetworkBody(bodyId: string): NetworkBody

Reads a whole captured body: an exchange's requestBodyId or responseBodyId.

Bodies are stored by the browser, not sent with the exchange, so reading one is a separate call. They stay readable after the capture stops, until the session ends.

The body is fetched in ranges and assembled in memory. For very large bodies, use CloudBrowser.readNetworkBodyRange to process them piece by piece.

Parameters
bodyIdstringrequestBodyId or responseBodyId from a NetworkExchange
const capture = await browser.captureNetwork(
{ patterns: ["*\/api/*"], bodies: "text" },
(ex) => {
  if (!ex.responseBodyId) return;
  void browser.readNetworkBody(ex.responseBodyId).then(({ data }) =>
    console.log(ex.url, new TextDecoder().decode(data)),
  );
},
);
Returns
NetworkBodythe body and whether the kept body is shorter than the original

readNetworkBodyRange

method on CloudBrowserinherits readNetworkBody
readNetworkBodyRange(bodyId: string, offset: number | undefined, length: number | undefined): NetworkBodyRange

Reads up to length bytes of a captured body starting at offset.

A single call returns at most 2 MiB; omit length to read that much. Loop on offset + data.length until it reaches totalSize to stream a large body.

Parameters
bodyIdstringrequestBodyId or responseBodyId from a NetworkExchange
offsetnumber | undefinedfirst byte to read (default 0)
lengthnumber | undefinedbytes to read (default and maximum 2 MiB)
Returns
NetworkBodyRangeNetworkBodyRange with the bytes and the body's total size

releaseDomSubtree

method on CloudBrowser
releaseDomSubtree(backendNodeId: number, frameId: string): void

Stops reporting changes inside a node, and inside any frame below it. DomMirror.collapse calls this.

Parameters
backendNodeIdnumber
frameIdstring
Returns
void
SeeCommandError for reading the code off the rejection

releaseKey

method on CloudBrowserinherits pressKey
releaseKey(key: string, opts: { code?: string; modifiers?: number; location?: number } | undefined): void

Fires a single key-up event.

Mirror of pressKey. Same parameter semantics; use this to close a press cycle that was started with pressKey.

Parameters
keystringDOM KeyboardEvent.key value (e.g. "Enter", "a", "ArrowLeft")
opts{ code?: string; modifiers?: number; location?: number } | undefinedoptional key customization: code (DOM KeyboardEvent.code, e.g. "KeyA"), modifiers (bit-flag: Alt=1, Ctrl=2, Meta=4, Shift=8), location (0=standard, 1=left, 2=right, 3=numpad)
await browser.pressKey("Shift", { code: "ShiftLeft", location: 1 });
await browser.releaseKey("Shift", { code: "ShiftLeft", location: 1 });
Returns
void
SeeCommandError for reading the code off the rejection

removeReaction

method on CloudBrowser
removeReaction(reactionId: string): boolean

Removes a pending reaction by id. Returns false if the reaction had already fired (one-shot) or was never registered.

Parameters
reactionIdstringid returned by CloudBrowser.addReaction
const removed = await browser.removeReaction(id);
Returns
booleantrue if a pending reaction with this id existed and was removed

revealDomNode

method on CloudBrowser
revealDomNode(backendNodeId: number, frameId: string): { path: string; seq: number }

Returns the chain from the main document down to a node, each ancestor with its own children, crossing into frames where it has to and starting the ones it passes through. DomMirror.reveal calls this and splices it in.

An id that is simply unknown is not a failure: the call resolves with an empty result.

Parameters
backendNodeIdnumber
frameIdstring
Returns
{ path: string; seq: number }
SeeCommandError for reading the code off the rejection

runScript

method on CloudBrowser
runScript(source: string): ScriptResult

Runs source in the session's browser and waits for it to finish.

The script runs beside the browser, in a V8 isolate of its own rather than in the page, and reaches the document through the engine: a cross-origin <iframe> is read as plain contentDocument with no frame ids anywhere, values come back as live objects it can assign to rather than snapshots, an element can be handed straight to browser.click, and the page sees nothing injected. Steps cost microseconds rather than network round trips, so work that is chatty by nature — polling for a selector, walking a list, following pagination — is affordable there. A guide for it is still to come.

This waits for as long as the script runs, and cannot be bounded: the run id needed to cancel only arrives with the reply. Use CloudBrowser.startScript when the script may outlive the caller's patience, or CloudBrowser.stopScripts to abandon what this session is running.

Parameters
sourcestringJavaScript to execute; its return value comes back as JSON
const result = await browser.runScript(`
await browser.navigate("https://example.com");
const items = [];
for (const el of await browser.getDOM().querySelectorAll("h1")) {
  items.push(el.textContent);
}
return items;
`);
console.log(result.success, result.result);
Returns
ScriptResultthe return value and the script's whole console output. A script that threw is reported as success: false, not as a rejection

screenshot

method on CloudBrowser
screenshot(opts: ScreenshotOpts | undefined): ScreenshotResult

Captures a single image of the page's current frame and returns it as base64-encoded image bytes.

The capture uses a one-shot surface copy (the same mechanism as CDP Page.captureScreenshot), so it is independent of any active live stream and works with both GPU (hardware) and software compositing.

Parameters
optsScreenshotOpts | undefinedoptional format ("png" default, "jpeg", "webp") and quality (0-100, for jpeg/webp only); omit to use the server defaults
const shot = await browser.screenshot({ format: "png" });
await fs.writeFile("page.png", Buffer.from(shot.dataBase64, "base64"));
Returns
ScreenshotResultScreenshotResult with the base64 image in dataBase64 and the physical pixel width/height
SeeCommandError for reading the code off the rejection

scrollTo

method on CloudBrowser
scrollTo(target: Locator): ElementResult

Scrolls the given element into view.

Whatever scroll container is closest to the element does the scrolling — nested scroll containers and out-of-process iframe chains are walked automatically. at is not a valid target here; scrolling needs a real element.

Parameters
targetLocatorlocator describing the element to bring into view; at is rejected
await browser.scrollTo(css("#footer"));
Returns
ElementResultElementResult with the resolved frameId, backendNodeId, post-scroll isVisible and the element's bounds after the scroll
Throws
BrowserScaleErrorinvalid locator or a server/transport error
ScrollErrorthe target could not be located/scrolled (`.code` is `"not_found"`); `.result` carries the resolved payload

selectByIndex

method on CloudBrowser
selectByIndex(target: Locator, index: number, opts: SelectOpts | undefined): SelectOptionResult

Picks the <option> at the zero-based index inside the targeted <select> element.

Sets the option as selected on the targeted <select>, then fires the standard input + change events (unless suppressed via opts.fireEvents = false).

at is not a valid target — select requires an actual <select> element.

Parameters
targetLocatorlocator describing the <select> element
indexnumberzero-based option index
optsSelectOpts | undefinedoptional select customization; see SelectOpts
await browser.selectByIndex(css("select#country"), 2);
// Pick the option silently, no input/change events.
await browser.selectByIndex(css("select#hidden"), 0, { fireEvents: false });
Returns
SelectOptionResultSelectOptionResult with the resolved selectedIndex, selectedValue and selectedText after the change
Throws
BrowserScaleErrorinvalid locator, or a server/transport error (frame not found, timeout, page closed)
SelectOptionErrorthe option could not be selected; `.code` is `"not_found"` (no `<select>`) or `"option_not_found"`

selectByText

method on CloudBrowserinherits selectByIndex
selectByText(target: Locator, text: string, opts: SelectOpts | undefined): SelectOptionResult

Picks the <option> whose visible (trimmed) text matches the given string exactly.

Sets the option as selected on the targeted <select>, then fires the standard input + change events (unless suppressed via opts.fireEvents = false).

at is not a valid target — select requires an actual <select> element.

Parameters
targetLocatorlocator describing the <select> element
textstringthe visible option text to match
optsSelectOpts | undefinedoptional select customization; see SelectOpts
await browser.selectByText(css("select#country"), "Germany");
Returns
SelectOptionResultSelectOptionResult with the resolved selectedIndex, selectedValue and selectedText after the change
Throws
BrowserScaleErrorinvalid locator, or a server/transport error (frame not found, timeout, page closed)
SelectOptionErrorthe option could not be selected; `.code` is `"not_found"` (no `<select>`) or `"option_not_found"`

selectByValue

method on CloudBrowserinherits selectByIndex
selectByValue(target: Locator, value: string, opts: SelectOpts | undefined): SelectOptionResult

Picks the <option> whose value attribute matches the given string exactly.

Sets the option as selected on the targeted <select>, then fires the standard input + change events (unless suppressed via opts.fireEvents = false).

at is not a valid target — select requires an actual <select> element.

Parameters
targetLocatorlocator describing the <select> element
valuestringthe value attribute to match
optsSelectOpts | undefinedoptional select customization; see SelectOpts
await browser.selectByValue(css("select#country"), "DE");
Returns
SelectOptionResultSelectOptionResult with the resolved selectedIndex, selectedValue and selectedText after the change
Throws
BrowserScaleErrorinvalid locator, or a server/transport error (frame not found, timeout, page closed)
SelectOptionErrorthe option could not be selected; `.code` is `"not_found"` (no `<select>`) or `"option_not_found"`

setAuthSession

method on CloudBrowser
setAuthSession(session: AuthSession): void

Imports an auth session so the context comes up signed in (and syncing if syncConsent) with its DBSC sessions restored.

Call it before navigating. Pair with setCookies() / setStorage() to restore a full persona.

Parameters
sessionAuthSessionsession as returned by getAuthSession()
await browser.setAuthSession(saved);
await browser.navigate("https://mail.google.com");
Returns
void

setBlockList

method on CloudBrowser
setBlockList(patterns: string[]): void

Replaces the session's URL blocklist.

Any request whose URL matches one of the supplied patterns is blocked before it leaves the browser. Patterns are simple URL wildcards (* matches any character span). Pass an empty array to clear the blocklist and let everything through.

Parameters
patternsstring[]URL wildcards to block; empty array clears the list
await browser.setBlockList([
"*.doubleclick.net/*",
"*googletagmanager.com*",
]);
Returns
void

setCookies

method on CloudBrowser
setCookies(cookies: CookieParam[]): void

Writes the supplied cookies into the browser context.

Existing cookies with the same (name, domain, path) tuple are overwritten. Pass an empty array for a no-op.

Parameters
cookiesCookieParam[]cookies to write; empty array is a no-op
await browser.setCookies([
{ name: "auth", value: "tok", domain: "example.com", path: "/" },
]);
Returns
void

setProxy

method on CloudBrowser
setProxy(proxyHost: string, proxyPort: number, proxyUsername: string, proxyPassword: string): void

Changes the runtime proxy for this session.

Takes effect for new requests immediately; in-flight requests keep their original routing. Pass an empty proxyHost to clear the proxy and route directly.

Parameters
proxyHoststringupstream proxy host; empty disables the proxy
proxyPortnumberupstream proxy port; ignored when proxyHost is empty
proxyUsernamestringproxy auth user; empty for unauthenticated proxies
proxyPasswordstringproxy auth password; empty for unauthenticated proxies
await browser.setProxy("proxy.example.com", 8080, "user", "pass");
Returns
void

setStaticPaths

method on CloudBrowser
setStaticPaths(blobName: string, patterns: string[]): void

Configures the session to serve cached static responses for requests matching the given patterns from blobName.

Useful for replaying frozen page assets (HTML/JS/CSS/images) without hitting the origin every time. The cache backend itself is configured server-side. Pass an empty patterns array to disable caching for this session.

Parameters
blobNamestringserver-side identifier of the snapshot to serve from
patternsstring[]URL wildcards to redirect to the cache; empty disables
await browser.setStaticPaths("snap-2026-05", ["*.example.com/*"]);
Returns
void

setStorage

method on CloudBrowser
setStorage(storage: StorageOriginEntry[]): void

Writes localStorage entries into the browser context, grouped by origin.

Accepts the same structure getStorage() returns, so a dump can be fed back verbatim. Existing keys are overwritten. Works without any open page; pages that are already open will not observe the writes until they reload.

Parameters
storageStorageOriginEntry[]entries to write, grouped by origin
await browser.setStorage([
{
  origin: "https://example.com",
  items: [
    { key: "token", value: "abc123" },
    { key: "theme", value: "dark" },
  ],
},
]);
Returns
void

solveCaptcha

method on CloudBrowser
solveCaptcha(opts: { timeoutMs?: number; retryAmount?: number } | undefined): string

Detects and solves the first supported bot-challenge it finds anywhere on the page.

Detection covers the common challenge types you run into in the wild. The challenge is completed in-page server-side (the resulting token / bypass cookies are wired into the page automatically), so callers can ignore the returned string.

Parameters
opts{ timeoutMs?: number; retryAmount?: number } | undefinedoptional timeoutMs (how long to wait for a captcha to appear; omit for server default 60s) and retryAmount (failures tolerated before giving up)
await browser.solveCaptcha({ retryAmount: 2 });
Returns
stringempty string on success — the solution is applied server-side

startDomMirror

method on CloudBrowser
startDomMirror(opts: DomMirrorOptions): DomSnapshot

Starts (or restarts) the page's mirror and returns the main document, without subscribing to changes. CloudBrowser.mirrorDom is what you normally want; this is the raw command.

Parameters
optsDomMirrorOptions
SeeCommandError for reading the code off the rejection

startNetworkCapture

method on CloudBrowser
startNetworkCapture(opts: NetworkCaptureOptions): void

Arms a capture without subscribing to it.

Use it when the reader lives somewhere else — another tab, or a later createWebSocketBrowser against the same session. Most callers want CloudBrowser.captureNetwork instead, which arms and subscribes together. Calling this again replaces the running capture.

Parameters
optsNetworkCaptureOptionswhich requests to capture and whether to keep bodies
Returns
void

startScript

method on CloudBrowser
startScript(source: string, onEvent: ScriptEventHandler): ScriptRun

Launches source in the session's browser and resolves as soon as the run is under way.

The counterpart to CloudBrowser.runScript, for scripts that are not worth waiting on: a watcher that runs for the life of the session, work that should survive this page. Output arrives at onEvent while the caller gets on with something else, and ScriptRun.wait collects the outcome if it is wanted.

Subscribing has to happen before the launch, because a detached run's output is not kept anywhere — the browser rejects a start with nobody listening rather than discard the script's log and result. This call does both in that order, so nothing the script prints is missed.

Parameters
sourcestringJavaScript to execute
onEventScriptEventHandlercalled per log line and once for the outcome; see ScriptEventHandler for the ordering and blocking rules
const run = await browser.startScript(source, (ev) => {
if (ev.log) console.log(ev.log.level, ev.log.message);
});
const outcome = await run.wait();
Returns
ScriptRunScriptRun handle for awaiting or cancelling the run

startStream

method on CloudBrowser
startStream(offerSdp: string): StreamAnswer

Answers your WebRTC SDP offer and starts streaming the page as a video track. The browser is the answerer; you are the offerer (see getStreamConfig for the credentials to build the offer).

Parameters
offerSdpstringyour RTCPeerConnection's SDP offer
const { answerSdp, viewport } = await browser.startStream(offer.sdp);
await pc.setRemoteDescription({ type: "answer", sdp: answerSdp });
Returns
StreamAnswerthe SDP answer to apply as the remote description, plus the viewport to map input coordinates into
SeeCommandError for reading the code off the rejection

stopDomMirror

method on CloudBrowser
stopDomMirror(): void

Stops the page's mirror, every frame of it. Idempotent.

Returns
void

stopNetworkCapture

method on CloudBrowser
stopNetworkCapture(): boolean

Disarms the session's capture.

Returns
booleanwhether a capture was running

stopScripts

method on CloudBrowser
stopScripts(runId: string): number

Cancels runs in this session and reports how many it ended.

An empty runId cancels every run in the session, which is the only form available to a caller that never learned an id — notably one abandoning a CloudBrowser.runScript.

Parameters
runIdstringrun to cancel, or "" for all of them
await browser.stopScripts(""); // abandon everything running
Returns
numberhow many runs were cancelled; 0 when the id named nothing in flight

stopStream

method on CloudBrowser
stopStream(): void

Tears down the live video stream for the session's page. Safe to call even if no stream is running.

Rejects only on a transport failure. Stopping a stream that is not running is a no-op rather than a failure, so there are no error codes to branch on.

await browser.stopStream();
Returns
void

streamNetworkExchanges

method on CloudBrowser
streamNetworkExchanges(onExchange: NetworkExchangeHandler): NetworkCapture

Subscribes to the session's capture without arming one, for reading a capture that CloudBrowser.startNetworkCapture armed elsewhere. Several readers can watch the same capture, each with its own buffer.

Stopping the returned view detaches this reader and leaves the capture running, since other readers may still be attached.

Parameters
onExchangeNetworkExchangeHandlercalled per exchange; see NetworkExchangeHandler for the ordering and blocking rules
Returns
NetworkCaptureNetworkCapture attached to whatever capture is running; onExchange simply never fires when none is

type

method on CloudBrowser
type(text: string, opts: { clearFirst?: boolean } | undefined): void

Types text into the currently focused element as a per-key stream of real keyboard events (keyDown/char/keyUp with the context's QWERTZ/QWERTY layout and human cadence) — unlike insertText, a single IME-style commit with no key events.

type is intentionally UNtargeted and loose: it does not locate or focus any element and does NOT pin focus, so the page is free to route keys and move focus between fields mid-stream — ideal for one-time-code / OTP inputs that auto-advance to the next box on each digit. To type one specific field that must stay focused for the whole value, use fill instead (strict, target-bound, per-key focus-verified).

Nothing is focused for you: click (or fill) the field first, or otherwise ensure focus, before calling type.

Parameters
textstringthe text to type as real key events
opts{ clearFirst?: boolean } | undefinedoptional: clearFirst clears the focused field (Ctrl+A, Delete) before typing
// OTP field that auto-advances across boxes.
await browser.click(css("input.otp-0"));
await browser.type("123456");
Returns
void

wait

method on CloudBrowserinherits waitAny
wait(condition: Locator, opts: WaitOpts | undefined): WaitResult

Blocks until the given locator matches.

Shortcut for waitAny(condition, opts). See waitAny for timeout handling, per-locator visible/steady defaults and the list of locators that are not valid wait conditions.

Parameters
conditionLocatorthe single locator to wait for
optsWaitOpts | undefinedoptional wait customization (timeoutMs)
await browser.wait(css(".success"));
Returns
WaitResultWaitResult for the first matching condition
Throws
BrowserScaleErrora condition was invalid, or a server/transport error occurred
WaitErrorno condition matched before the deadline; `.conditions` holds the per-condition breakdown of why each never matched

waitAny

method on CloudBrowser
waitAny(conditions: Locator[], opts: WaitOpts | undefined): WaitResult

Blocks until any of the supplied locators matches.

When several conditions are supplied, the first one to match wins; the others are abandoned. The returned WaitResult's .index points to the entry in conditions that matched.

Defaults applied automatically: - timeout: 30000 ms — override via opts.timeoutMs - per-locator visible/steady: true / 500 for css() and js() locators. For js() expressions returning a non-Element value both flags are no-ops. Override with .visible(false) / .steady(ms) on individual locators.

node and at are not valid wait conditions — they only make sense as action targets — and throw at send time.

Parameters
conditionsLocator[]one or more Locators to wait for; must be non-empty
optsWaitOpts | undefinedoptional wait customization (timeoutMs)
const r = await browser.waitAny(
[css(".success"), js("window.__ready === true")],
{ timeoutMs: 5000 },
);
console.log("matched index:", r.index);
Returns
WaitResultWaitResult for the first matching condition
Throws
BrowserScaleErrora condition was invalid, or a server/transport error occurred
WaitErrorno condition matched before the deadline; `.conditions` holds the per-condition breakdown of why each never matched

waitForAnyRequest

method on CloudBrowser
waitForAnyRequest(patterns: RequestPattern[], opts: { timeoutMs?: number } | undefined): { index: number; request: InterceptedRequest | null }

Blocks until the next request whose URL matches one of the supplied patterns is observed.

Returns the matched pattern's index and the captured request. When patternsi.abort is true the request is dropped with an empty 200 response instead of being sent to the network.

Parameters
patternsRequestPattern[]one or more URL patterns (with optional abort flags)
opts{ timeoutMs?: number } | undefinedoptional timeoutMs; omit to use the server default
const { index, request } = await browser.waitForAnyRequest(
[{ url: "*\/api/login" }],
{ timeoutMs: 5000 },
);
console.log(index, request?.method, request?.url);
Returns
{ index: number; request: InterceptedRequest | null }object with index (matched pattern index) and request (the captured method/URL/headers/body; null if intercepted with no body)
SeeCommandError for reading the code off the rejection

waitForAnyResponse

method on CloudBrowserinherits waitForAnyRequest
waitForAnyResponse(patterns: RequestPattern[], opts: { timeoutMs?: number } | undefined): { index: number; response: InterceptedResponse | null }

Blocks until the next response whose URL matches one of the supplied patterns is observed.

Same shape as waitForAnyRequest but on the response phase. When patternsi.abort is true the page receives an empty 200 instead of the real response.

Parameters
patternsRequestPattern[]one or more URL patterns (with optional abort flags)
opts{ timeoutMs?: number } | undefinedoptional timeoutMs; omit to use the server default
const { index, response } = await browser.waitForAnyResponse(
[{ url: "*\/api/login" }],
{ timeoutMs: 5000 },
);
console.log(index, response?.statusCode);
Returns
{ index: number; response: InterceptedResponse | null }object with index (matched pattern index) and response (the captured status/headers/body; null if no body was returned)
SeeCommandError for reading the code off the rejection
class

Locator

4 methods

Locator is the universal "what element / what condition" type. It is used both as a wait condition (passed to wait()/waitAny()) and as a target for element actions (passed to click(), fill(), …).

Not every field is meaningful in every context: - selector / jsExpression → both wait and actions - backendNodeId → actions only (wait rejects it) - visible / steadyMs → wait only (silently ignored by actions) - x / y → actions only (wait rejects it) - frameId → both, may be overridden by call-level opts.inFrame

Use the css() / js() / node() / at() constructors instead of building this class by hand.

Modifiers (visible, steady, inFrame, inAllFrames) are immutable — they return a new Locator and leave the original untouched, so it's safe to share a base locator across calls.

Fields
selectorstring = ""
jsExpressionstring = ""
backendNodeIdnumber = 0
frameIdstring = ""
visibleFlag?boolean
steadyMs?number
x?number
y?number

inAllFrames

method on Locator
inAllFrames(): Locator

Scopes this Locator to every frame.

Equivalent to .inFrame(AllFrames). Use this when an element might appear inside any of several frames and you do not want to enumerate them.

await browser.wait(css("button.consent").inAllFrames());
Returns
Locatora new Locator scoped to all frames

inFrame

method on Locator
inFrame(frameId: string): Locator

Scopes this Locator to a specific frameId.

Use the frameId from a previous result or CloudBrowser.getPages to target elements inside a known iframe.

Parameters
frameIdstringid of the frame to scope to
const pages = await browser.getPages();
const iframeId = pages[0].frameTree.children[0].frameId;
await browser.click(css("button").inFrame(iframeId));
Returns
Locatora new Locator scoped to that frame

steady

method on Locator
steady(ms: number): Locator

Requires the element to keep a stable position and size for at least ms milliseconds before the wait matches.

Settling defaults to 500ms, so pass 0 to match the instant the element is found. Has no effect for js() expressions that return a non-Element value, nor when used as an action target.

Parameters
msnumbersteady-state duration in milliseconds; 0 disables
await browser.wait(css(".banner").steady(0));
Returns
Locatora new Locator with the override applied

visible

method on Locator
visible(v: boolean): Locator

Enforces or disables the visibility check for this Locator's wait condition.

Visibility is required by default, so pass false to wait for DOM presence alone. Has no effect when used as an action target — actions never check visibility before dispatching.

Parameters
vbooleantrue to require visibility, false to skip the check
await browser.wait(css("#hidden").visible(false));
Returns
Locatora new Locator with the override applied
class

BrowserConfig

3 methods

Configuration for renting a browser session. Create with the required parameters, then chain optional setters.

Fields
apiKeystring
rentDurationnumber
proxyHoststring
proxyPortnumber
proxyUsernamestring
proxyPasswordstring

withCountryCode

method on BrowserConfig
withCountryCode(countryCode: string): BrowserConfig

Sets the geo-IP country code for the rented session.

Drives both the assigned exit-IP region and the locale defaults (Accept-Language, timezone fallback) when those are not overridden separately.

Parameters
countryCodestringISO-3166 country code (e.g. "DE", "US")
new BrowserConfig(apiKey, 600, "", 0, "", "").withCountryCode("DE");
Returns
BrowserConfigthis BrowserConfig for chaining

withFingerprint

method on BrowserConfig
withFingerprint(fingerprint: string): BrowserConfig

Pins a specific browser fingerprint id for the session.

When omitted the server picks a fingerprint based on the country code. Pass a known id (e.g. one returned by a previous rental) to keep fingerprints stable across sessions.

Parameters
fingerprintstringserver-side fingerprint id
new BrowserConfig(apiKey, 600, "", 0, "", "").withFingerprint("fp_abc123");
Returns
BrowserConfigthis BrowserConfig for chaining

withTimezone

method on BrowserConfig
withTimezone(timezone: string): BrowserConfig

Sets the IANA timezone for the rented session.

Parameters
timezonestringIANA timezone (e.g. "Europe/Berlin")
new BrowserConfig(apiKey, 600, "", 0, "", "").withTimezone("Europe/Berlin");
Returns
BrowserConfigthis BrowserConfig for chaining
class

DomMirror

8 methods

A live copy of a page's DOM, across every frame in it.

Returned by CloudBrowser.mirrorDom. The browser sends the top of the tree once and from then on only what changed in the part you expanded, so a page that churns inside a collapsed subtree costs one number per batch instead of a re-serialized document.

It is one tree. An <iframe> is an element whose one child is the document it hosts; expanding it fetches that document and starts mirroring the frame, collapsing it stops again, and a frame navigating arrives as its owner's child being replaced. Underneath there is still one mirror per document, because a mutation observer is bound to a single Document and an out-of-process iframe is a different Document in a different process — but that is engine bookkeeping, not something a caller models.

Node ids restart per frame, so a node's address is the pair (DomNode.frameId, backendNodeId) and never the id alone.

The tree is treated as immutable: applying a change replaces the nodes from the root down to the one that moved and leaves every other object identical. A UI can therefore re-render from root and let React.memo (or any identity check) skip the parts that did not move.

``ts const mirror = await browser.mirrorDom({ pierce: true }, () => render(mirror.root)); await mirror.expand(bodyNode); // start reporting changes inside <body> await mirror.collapse(bodyNode); // stop again await mirror.stop(); ``

collapse

method on DomMirror
collapse(node: DomNode): void

Stops reporting changes inside a node, called when the user closes it. The node itself stays in the tree and keeps reporting its child count. A child frame below it stops being mirrored too.

Skipping this is not an error, it is a slow leak: the browser's revealed set only grows, and eventually it is no longer filtering anything.

Parameters
nodeDomNode
Returns
void
SeeCommandError for reading the code off the rejection

expand

method on DomMirror
expand(node: DomNode, depth: number | undefined): void

Fetches a node's children and starts reporting changes inside them. This is what a tree view calls when the user opens a node.

On an <iframe> the one child is the document it hosts, and this call is what starts mirroring that frame. Nothing about the result says a process boundary was crossed; it is a child list like any other.

Parameters
nodeDomNode
depthnumber | undefinedlevels below the node, default 1
Returns
void
SeeCommandError for reading the code off the rejection

getNode

method on DomMirror
getNode(frameId: string, backendNodeId: number): DomNode | undefined

Looks up a node by its address.

Parameters
frameIdstring
backendNodeIdnumber
Returns
DomNode | undefined

isExpanded

method on DomMirror
isExpanded(node: DomNode): boolean

Whether this node's children are known. Changes inside a node that is not expanded arrive only as an updated childNodeCount.

Parameters
nodeDomNode
Returns
boolean

resync

method on DomMirror
resync(reason: DomResyncReason): void

Throws away the local copy of the whole page and fetches a fresh one. Happens automatically whenever the browser says the copy is void, so you rarely need to call it.

Parameters
reasonDomResyncReason
Returns
void
SeeCommandError for reading the code off the rejection

reveal

method on DomMirror
reveal(backendNodeId: number, frameId: string | undefined): DomNode[]

Brings a node into the tree together with its ancestors and their siblings, and starts reporting changes along that path.

Use it to focus a node you do not hold — an inspectAtPosition hit, say. You cannot walk up to it yourself: it is not in your tree, so there is nothing to walk from.

The node may be in a frame nobody opened, and that works: the chain comes back crossing the frame boundaries it has to, and those frames start being mirrored, exactly as if you had expanded your way there by hand.

Parameters
backendNodeIdnumber
frameIdstring | undefined
Returns
DomNode[]the ancestor chain, the main document first, or an empty array if the node is not on the page
SeeCommandError for reading the code off the rejection

stop

method on DomMirror
stop(): void

Stops mirroring and detaches the reader. Idempotent, safe in a finally.

Returns
void

wait

method on DomMirror
wait(): void

Resolves once the mirror ends — stop(), a dead session, a transport failure.

Returns
void
class

NetworkCapture

2 methods

NetworkCapture is a running capture, returned by CloudBrowser.captureNetwork. Exchanges are delivered to the handler passed there; this handle only exists to stop the capture and to report how it went.

stop

method on NetworkCapture
stop(): void

Ends the capture. Idempotent, and safe to call from a finally.

Once it resolves the handler is no longer running, so data it collected is complete. Views from CloudBrowser.streamNetworkExchanges only detach — they never disarm a capture other readers may share.

To stop from inside the handler, call CloudBrowser.stopNetworkCapture instead: stop awaits the reader, which cannot finish while the handler it called is still running.

Rejects only on a transport failure, and the local reader is shut down regardless. Disarming a capture that is not running is a no-op rather than a failure, so there are no error codes to branch on.

Returns
void

wait

method on NetworkCapture
wait(): void

Resolves once the capture ends — NetworkCapture.stop, a dead session or a transport failure.

Use it to capture for as long as the session lives. It is not needed when you drive the browser yourself and call stop when done.

Returns
void
class

ScriptFollow

2 methods

ScriptFollow is a read-only view of script output, returned by CloudBrowser.followScript.

stop

method on ScriptFollow
stop(): void

Ends the subscription. Idempotent, and safe to call from a finally. It never cancels a run: other readers, and the script itself, are unaffected.

Returns
void

wait

method on ScriptFollow
wait(): void

Resolves once the subscription ends — ScriptFollow.stop, a dead session or a transport failure.

Returns
void
class

ScriptRun

3 methods

ScriptRun is a script running in the background, returned by CloudBrowser.startScript. Its output is delivered to the handler passed there; this handle exists to await the outcome and to cancel the run.

detach

method on ScriptRun
detach(): void

Stops reading this run's output without cancelling the run. The script keeps going with nobody watching, which is what makes a detached run outlive the page that started it.

Returns
void

stop

method on ScriptRun
stop(): void

Cancels the run and detaches this reader. Idempotent, and safe to call from a finally.

A script that is executing is interrupted; one parked on an await unwinds at its next operation in the page. Either way the handler sees a finished with stopped set, unless the local reader is torn down first.

Rejects only on a transport failure, and the local reader is detached regardless. Cancelling a run that has already finished is a no-op.

Returns
void

wait

method on ScriptRun
wait(): ScriptFinished

Resolves once the run ends, with how it ended.

A script that threw is an outcome, not an error: it resolves with success: false. It rejects when the run's fate is unknown — the stream broke or the session died before the script finished.

Returns
ScriptFinishedhow the script ended
class

WebSocketTransport

1 method

stream

method on WebSocketTransport
stream(method: DescMethodStreaming<I, O>, signal: AbortSignal | undefined, _timeoutMs: number | undefined, _header: HeadersInit | undefined, input: AsyncIterable<MessageInitShape<I>>, _contextValues: ContextValues | undefined): StreamResponse<I, O>

Runs a server-streaming call. Client-streaming is not supported: the frame format carries exactly one request message, and no RPC needs more.

Resolving means the server confirmed the subscription, so a caller may act on it — arm a network capture, say — without racing the first message.

Parameters
methodDescMethodStreaming<I, O>
signalAbortSignal | undefined
_timeoutMsnumber | undefined
_headerHeadersInit | undefined
inputAsyncIterable<MessageInitShape<I>>
_contextValuesContextValues | undefined
Returns
StreamResponse<I, O>

Functions

11 functions

Package-level functions: renting, listing, connecting to and stopping sessions, and the locator constructors.

at

function
at(x: number, y: number): Locator

at targets viewport coordinates instead of an element.

Useful for clicking inside a canvas, hovering decorative regions, or dispatching events at synthetic positions. Action-only — using it in wait() throws at send time. Note that only click and moveTo accept at; scrollTo, drag, fill and select all require a real element.

Parameters
xnumberviewport-relative x in CSS pixels
ynumberviewport-relative y in CSS pixels
// Click at canvas-relative coordinates.
await browser.click(at(120, 240));
Returns
LocatorLocator usable only as an action target
connectSession(grpcUrl: string, apiKey: string, sessionId: string): CloudBrowser

Attaches to an already-rented session over a fresh gRPC connection.

Useful when a session id (and its gRPC URL) was persisted across processes and you want to drive it again without renting a new one. Mirrors browserscale-go's ConnectSession. Closing the returned handle via CloudBrowser.stopBrowser releases the rental (calls the stop endpoint) and closes the transport.

Parameters
grpcUrlstringsession host gRPC URL from the original rent (grpc:// or grpcs://)
apiKeystringAPI key the session was rented with
sessionIdstringid of the existing session
const browser = connectSession(grpcUrl, apiKey, sessionId);
try {
await browser.navigate("https://example.com");
} finally {
await browser.stopBrowser();
}
Returns
CloudBrowserCloudBrowser attached to the existing session
createWebSocketBrowser(wsUrl: string, sessionId: string, apiKey: string, fingerprint: string): CloudBrowser

Attaches a CloudBrowser to an existing session over a raw WebSocket transport.

Use this from a browser context: the WebSocket transport framing is defined by WebSocketTransport and is served directly by the browserscale session host. The session must already exist server-side; unlike rentBrowser this does not call the rent API. Closing the returned handle (via CloudBrowser.stopBrowser) only closes the transport — the rental stays alive.

Parameters
wsUrlstringWebSocket URL (ws:// or wss://) of the session host
sessionIdstringid of the existing session
apiKeystringAPI key authorizing access to the session
fingerprintstringbrowser fingerprint id; empty if unknown
const browser = createWebSocketBrowser(
"wss://session-abc.browserscale.example.com/ws",
sessionId,
apiKey,
);
await browser.navigate("https://example.com");
Returns
CloudBrowserCloudBrowser attached to the existing session

css

function
css(selector: string): Locator

css waits for / targets an element matching the given CSS selector.

When used in CloudBrowser.wait/CloudBrowser.waitAny, the condition requires the element to be visible and to hold still for 500ms before it matches — the API's defaults for a condition that does not set them. Override per call with .visible(false) / .steady(ms) (use .steady(0) to disable the steady check).

When used as an action target (click, fill, …) the visible/steady fields are ignored — there are no corresponding fields on the action requests.

Parameters
selectorstringCSS selector matching the element
// As a wait condition.
await browser.wait(css("button.submit"));
// As an action target.
await browser.click(css("button.submit"));
Returns
LocatorLocator usable as a wait condition or as an action target

js

function
js(expression: string): Locator

js waits for / targets the result of a JavaScript expression.

Same wait defaults as css (visible, 500ms steady); these only apply when the expression returns a DOM Element. For non-Element truthy values (boolean, string, number, plain object) both fields are no-ops and the condition matches as soon as the value is truthy. Use .visible(false) / .steady(0) to opt out.

Parameters
expressionstringJavaScript expression evaluated in the target frame
await browser.wait(js("window.__ready === true"));
Returns
LocatorLocator usable as a wait condition or as an action target
listBrowsers(apiKey: string): BrowserInfo[]

Reports the sessions an API key currently holds.

Use it to recover session ids the process lost — after a restart, or from a different machine entirely. Without it a rental is only reachable through the handle that created it, so a crash between rent and stop leaves a paid session running with nothing able to name it.

Each entry carries the grpcUrl it is driven from, so a listed session can be handed straight to connectSession. Only live sessions are listed; a stopped one is gone, not reported as ended.

Parameters
apiKeystringAPI key whose sessions to list
const browsers = await listBrowsers(apiKey);
for (const b of browsers) console.log(b.sessionId, b.countryCode);

// and to drive one of them
const browser = connectSession(browsers[0].grpcUrl, apiKey, browsers[0].sessionId);
Returns
BrowserInfo[]the running sessions, oldest first; empty when the key holds none

node

function
node(backendNodeId: number): Locator

node targets an element by its DevTools backendNodeId.

Use this when you already have a backendNodeId from a previous result (e.g. a wait or evaluate result) and want to act on the exact same element without re-resolving by selector. Action-only — using it in wait() throws at send time.

Parameters
backendNodeIdnumberDevTools backendNodeId of the target element
const r = await browser.click(css("button.open"));
await browser.click(node(r.backendNodeId));
Returns
LocatorLocator usable only as an action target

rentBrowser

function
rentBrowser(config: BrowserConfig): CloudBrowser

Rents a new browser session and returns a connected handle.

Calls the browserscale rent endpoint with the supplied BrowserConfig, opens a gRPC connection to the assigned session host, and returns a ready-to-use CloudBrowser. Closing the returned handle (via CloudBrowser.stopBrowser) also releases the rental.

Parameters
configBrowserConfigrental parameters
const cfg = new BrowserConfig("sk_…", 600, "", 0, "", "");
const browser = await rentBrowser(cfg);
try {
await browser.navigate("https://example.com");
} finally {
await browser.stopBrowser();
}
Returns
CloudBrowserCloudBrowser ready to drive the rented session
setApiEndpoint(endpoint: string): void

Overrides the HTTP rent/stop endpoint.

Defaults to https://api.browserscale.cloud. Call this before any rentBrowser / stopBrowser call if you need to point at a private browserscale deployment.

Parameters
endpointstringbase URL of the rent/stop service, with no trailing slash
setApiEndpoint("https://browserscale.internal.example.com");
Returns
void

stopBrowser

function
stopBrowser(apiKey: string, sessionId: string): SessionUsage | undefined

Releases a session without needing a CloudBrowser handle.

Useful when a session id was persisted across processes and the rental outlived the original handle. Only calls the rent stop endpoint; there is no gRPC connection to close in this form.

Parameters
apiKeystringAPI key the session was rented with
sessionIdstringid of the session to release
const usage = await stopBrowser(apiKey, sessionId);
Returns
SessionUsage | undefinedSessionUsage what the session consumed over its whole life; undefined when the server could not report it
throwCommandError(command: string, error: { code: string; message: string } | undefined): void

Raises the optional error of a uniform result, and does nothing when the command succeeded — so a call site stays one line instead of an if.

Parameters
commandstring
error{ code: string; message: string } | undefined
Returns
void

Errors

8 errors

Thrown by the command they belong to, each with a stable code and typed detail. Catch them with instanceof.

BrowserScaleError is the base class for every error thrown by the SDK. It wraps either: - a client-side validation failure (bad locator, missing patterns, …) - a server-side gRPC error (Connect's ConnectError, available as cause)

Semantic action failures (an occluded click, a wait timeout, an option that did not exist, …) are thrown as the typed subclasses below, each carrying the same structured detail the Go SDK exposes via errors.As — plus the partial result of the attempted action on .result, so a single catch gives you both the diagnostics and the resolved coordinates.

Catch the base for anything, or narrow to a subclass for the detail:

try { await browser.click(css("#btn")); } catch (e) { if (e instanceof ClickError) console.log(e.code, e.occluder?.tagName); else if (e instanceof BrowserScaleError) { ... } }

ClickError is thrown by CloudBrowser.click when the click did not land — the target was found but another element covered the intended point. occluder describes the blocker; result carries the resolved element and coordinates (success is false).

It is also nested under FillError / DragError as the underlying click-core failure; in that nested form result is undefined (the partial result lives on the outer error).

Fields
codestringMachine-stable failure code, e.g. "occluded_no_reachable_point" (target fully covered, no exposed part reachable), "occluded_after_evade" (a reposition was tried but the target was still covered) or "not_found".
occluder?OccluderInfoThe intercepting element (present for occlusion codes).
evadeAttemptedbooleanWhether a pointer reposition was tried before giving up.
result?ElementResultResolved element + coordinates at the failed action. Present when this is the thrown top-level error; undefined when nested inside another error.

DragError is thrown by CloudBrowser.dragBy / CloudBrowser.dragTo when the source element could not be acquired/pressed. Drag picks up the source with the same smart click as CloudBrowser.click, so a pre-drag failure is a click failure: code mirrors it and the full click diagnostics live under clickError.

Fields
codestring
clickError?ClickError
resultDragResultResolved source + coordinates at the failed drag (success is false).

FillError is thrown by CloudBrowser.fill when the field could not be focused/typed. The click-phase codes ("not_found", "occluded_no_reachable_point", "occluded_after_evade") mirror the underlying focus click, with diagnostics under clickError. The focus codes are "focus_stolen" (another element took focus — focusedElement names it; fill is strictly target-bound and will not type into the thief) and "focus_lost" (focus left the target and nothing is focused). For untargeted stream typing that lets focus move (e.g. OTP), use CloudBrowser.type.

Fields
codestringMachine-stable failure code (see the class doc for the full set).
clickError?ClickErrorThe underlying click-core failure that prevented focusing/typing. Present for the click-phase codes; undefined for "focus_stolen"/"focus_lost".
focusedBackendNodeId?numberNode that held focus when fill gave up (0 if nothing was focused), for the "focus_stolen"/"focus_lost" codes.
focusedElement?ElementRefThe element that grabbed focus instead of the target ("focus_stolen"), so you can act on it (e.g. a consent button).
targetEditable?booleanThe fill target's own state at the point of failure (the focus codes): whether it is still an editable text sink and its current text length.
targetValueLength?number
resultElementResultResolved element + coordinates at the failed action (success is false).

MoveError is thrown by CloudBrowser.moveTo when the target could not be located. A move has no occlusion notion, so this is the only semantic failure.

Fields
codestringCurrently always "not_found".
resultElementResult

ScrollError is thrown by CloudBrowser.scrollTo when the target could not be located/scrolled.

Fields
codestringCurrently always "not_found".
resultElementResult

SelectOptionError is thrown by the CloudBrowser.selectByIndex / selectByValue / selectByText calls when the option could not be selected. selectOption is programmatic (no pointer gate), so it only reports semantic failures.

Fields
codestring"not_found" (the <select> was not located) or "option_not_found" (no option matched the requested index/value/text).
resultSelectOptionResult

WaitError is thrown by CloudBrowser.wait / CloudBrowser.waitAny when no condition matched before the deadline. conditions holds the per-condition breakdown (same order/length as the conditions passed in) explaining why each one never matched.

Fields
codestringMachine-stable failure code, currently always "timeout".
conditionsWaitConditionStatus[]Per-condition status, same order/length as the conditions passed to wait.
resultWaitResultThe partial wait result (index is -1 on timeout).

Options

13 types

Optional settings a command accepts.

ClickOpts

interface

Optional customization for CloudBrowser.click. All fields are optional; missing or zero values mean "use the server default".

Fields
inFrame?stringOverride the locator's frame. Omit to use the locator's own Locator.inFrame (or the main frame). Pass a specific frameId or AllFrames to search elsewhere.
button?ButtonMouse button. Default "left".
clickCount?number1 = single click (default), 2 = double-click.
action?ClickAction"click" (default) performs a full mouseDown+mouseUp. "press" only dispatches mouseDown, "release" only mouseUp at the current cursor position.

CookieParam

interface

CookieParam is one entry returned by getCookies() or passed to setCookies().

Fields
namestring
valuestring
url?string
domainstring
pathstring
secure?boolean
httpOnly?boolean
sameSite?string
expires?number
priority?string
sourceScheme?string
sourcePort?number
partitionKey?CookiePartitionKey

FillOpts

interface

Optional customization for CloudBrowser.fill. All fields are optional; missing values mean "use the server default".

Fields
inFrame?stringOverride the locator's frame. Omit to use the locator's own Locator.inFrame (or the main frame). Pass a specific frameId or AllFrames to search elsewhere.
clearFirst?booleantrue wipes the field's existing content with Ctrl+A, Delete before typing. Default (false) appends to whatever is already in the field.
timeoutMs?numberBudget in ms to make the field focusable+clickable (locate, scroll, settle, un-occlude), mirroring the click timeout. Omit for the server default (5000). 0 makes fill one-shot (no retry).
steadyMs?numberSettle window in ms before the focus click, mirroring the click steady-time. Omit for the server default (750). 0 skips settling.

GetDOMOpts

interface

Depth used by getDOM(). 0 = whole tree (default), >0 = limited depth.

Fields
depth?number

Optional customization for CloudBrowser.getObservation.

Fields
format?"text" | "json""text" (default) for the compact line format meant to be handed to a model as-is, or "json" for the structured form. Only the requested representation is built, so asking for one does not cost the other.
maxElementsPerFrame?numberCap on emitted elements per frame. Default 800 — a safety net against runaway documents; maxTotalTokens is the limit that normally binds.
maxTextLength?numberCap on human-readable strings (labels, text, values) in characters. Default 300. Identifier-like attributes (type, name, role) have their own fixed, shorter cap and are unaffected.
maxTotalTokens?numberBudget across ALL frames, in estimated tokens rather than characters — the same character count is worth roughly four times as many tokens in CJK text as in ASCII. Default 8000. Frames are visited in tree order and each gets whatever is left.
includeBounds?booleanInclude element bounds as bounds="x,y,w,h". Off by default; bounds cost about as much as the rest of a row and are rarely needed, since elements are addressed by backendNodeId.
viewportOnly?booleanOnly emit elements intersecting the frame's current viewport.
backendNodeId?numberSubtree scope — set exactly one of backendNodeId, selector or jsExpression to observe only that element's subtree (follow-up looks at a form then cost the form, not the ads around it). Omit all three for the whole page. Child iframes reached inside the scope are still visited.
selector?stringScope root by CSS selector.
jsExpression?stringScope root by JS expression that evaluates to a DOM Element (including __wrc.shadow(...) for closed shadow roots).
frameId?stringWhere to look up the scope root: a specific frameId, omit for the main frame, or AllFrames to search every frame until found. Ignored when observing the whole page.

LoadHTMLOpts

interface

Optional customization for CloudBrowser.loadHTML.

Fields
headers?{ name: string; value: string }[]Extra headers to attach to the synthetic response.
statusCode?numberDefault 200.

Configures CloudBrowser.captureNetwork.

There is deliberately no byte-cap option: kept bodies are stored on a machine shared with other sessions, so the server owns the quota. When a session's bodies exceed it, the oldest are dropped first.

Fields
patterns?string[]URL wildcards to capture; omit to capture every request the session makes. Prefix a pattern with "!" to exclude it, which is the short way to say "everything except this".
bodies?NetworkBodiesResponse-body capture. "text" keeps bodies whose MIME type is textual, "all" keeps every body, binary included. Defaults to "none", headers and status only. Request bodies are kept whenever a request has one.
bodyPatterns?string[]Narrows body capture to a subset of the captured requests; omit to apply bodies to all of them. Use it to log every request but only keep the payloads you care about.

ReactionOpts

interface

Optional customization for CloudBrowser.addReaction. All fields are optional; missing or zero values mean "use the server default".

Fields
on?LocatorOverride the click target. Omit to click the matched element itself. Provide a css()/js() Locator to click a different element, resolved in the matched element's frame (e.g. a modal's close "X"). node()/at() locators are rejected.
button?ButtonMouse button for the click. Default "left".
clickCount?number1 = single click (default), 2 = double-click.
intervalMs?numberPoll cadence in milliseconds for the shared page loop. Default 300.

Optional customization for CloudBrowser.readCanvas.

Fields
inFrame?stringOverride the locator's frame. Omit to use the locator's own Locator.inFrame (or the main frame). Pass a specific frameId or AllFrames to search elsewhere.
format?"png" | "jpeg" | "webp" | "rgba"Output encoding: "png" (default), "jpeg", "webp", or "rgba" for the raw unpremultiplied RGBA pixel buffer.
quality?numberEncode quality 0-100 for "jpeg"/"webp" (ignored otherwise). Default 90.
sx?numberOptional sub-rectangle in canvas pixels (mirrors getImageData(sx, sy, sw, sh)). The full canvas is read when sw/sh are omitted or <= 0.
sy?number
sw?number
sh?number

Optional customization for CloudBrowser.screenshot.

Fields
format?"png" | "jpeg" | "webp"Image format: "png" (default), "jpeg", or "webp".
quality?numberEncode quality 0-100 for "jpeg"/"webp" (ignored for "png"). Default 90.

SelectOpts

interface

Optional customization for CloudBrowser.selectByIndex, CloudBrowser.selectByValue and CloudBrowser.selectByText.

Fields
inFrame?stringOverride the locator's frame. Omit to use the locator's own Locator.inFrame (or the main frame). Pass a specific frameId or AllFrames to search elsewhere.
fireEvents?booleanfalse suppresses change/input events. Default (true) fires the standard events after the selection.

WaitOpts

interface

Optional customization for CloudBrowser.wait / CloudBrowser.waitForAny.

Fields
timeoutMs?numberOmitted leaves it to the API, which defaults to 30 000 ms.

Results

21 types

What commands hand back.

BrowserInfo

interface

BrowserInfo describes one running session, as listBrowsers reports it.

Fields
sessionIdstring
grpcUrlstringThe endpoint this session is driven from — the same one rent returned. It is what makes a listed id usable: pass it to connectSession.
startTimenumberUnix seconds the session was rented at.
rentDurationnumberThe rental length in seconds; 0 means unlimited.
remainingSeconds?numberSeconds left on the rental, and undefined for an unlimited one — there is nothing to count down.
countryCodestring
timezonestring
proxyHoststring
publicIpstringThe address the session egresses from.
gpuIndex?numberThe physical card the session renders on, and undefined on a software-rendered host.

DOMResult

interface

DOMResult is the full-tree DOM snapshot returned by CloudBrowser.getDOM, plus its sha256:8 hash for cheap change detection.

Fields
hashstring
domstring

DragResult

interface

DragResult is the outcome of a CloudBrowser.drag gesture: the resolved source element and the start/end coordinates of the performed drag.

Fields
successboolean
frameIdstring
backendNodeIdnumber
startXnumber
startYnumber
endXnumber
endYnumber

ElementResult is the outcome of an element interaction such as CloudBrowser.click, CloudBrowser.fill or CloudBrowser.scrollTo: the resolved element plus the root-relative coordinates the action was performed at.

Fields
successboolean
frameIdstring
backendNodeIdnumber
isVisibleboolean
boundsRect
rootXnumber
rootYnumber

EvaluateResult carries the outcome of a JS evaluate call.

If the expression returned a DOM element, backendNodeId/isVisible/bounds are populated and value is null. Otherwise value holds the parsed JSON value (string/number/boolean/array/object/null). On parse failure value falls back to the raw server string so the caller is never empty-handed.

The optional generic T types the .value field for convenience — this is purely a TS hint, not a runtime guarantee.

Fields
successbooleanFalse only when the expression never produced a value, which rejects the call — so a resolved result always has this true. An expression that answers falsy is a successful evaluation, so this does not mean "the answer was false".
valueT
backendNodeIdnumber
isVisibleboolean
boundsRect

FrameInfo

interface

FrameInfo describes a single frame within a page's frame tree.

Fields
frameIdstring
urlstring
isOOPIFboolean
hasJSContextboolean
isLoadingboolean
isVisibleboolean
absoluteRectRect
relativeRectRect
childrenFrameInfo[]

InspectResult describes the topmost element hit at viewport-relative (x, y). backendNodeId === 0 means nothing was found at that position.

Fields
backendNodeIdnumber
frameIdstring
tagNamestring
textContentstring
isVisibleboolean
boundsRect

InterceptedResponse describes a network response captured by CloudBrowser.waitForAnyResponse.

Fields
urlstring
statusCodenumber
headersHeader[]
bodystring

OccluderInfo

interface

OccluderInfo describes the element that intercepted a click — the element sitting on top of the target at the intended click point. Coordinates are in root-viewport CSS pixels. Populated on ClickError for occlusion failures so the caller can locate and clear the blocker (e.g. find its close button).

Fields
backendNodeIdnumber
frameIdstring
tagNamestring
idstring
classNamestring
textstring
boundsRect
pointerEventsstringComputed pointer-events keyword (e.g. "auto", "none", "all"). Lets you tell an invisible pass-through layer from one that genuinely swallows the click.
visibilitystringComputed visibility keyword ("visible", "hidden", "collapse").
opacitynumberComputed opacity (0..1). 0 means visually invisible but it may still intercept clicks depending on pointerEvents.
zIndexstringComputed effective z-index as a string ("0" when auto / not stacked).
hittableWhileInvisiblebooleanTrue when the blocker intercepts clicks even while invisible (computed pointer-events in {all, painted, fill, stroke}): a real click is swallowed even at visibility:hidden / opacity:0. When false and the element is invisible, a real click would fall through.
positionstringComputed position keyword. "fixed"/"sticky" means the blocker is pinned (by itself or an ancestor) and stays put no matter where the pointer goes — clear it by scrolling the target out from under it; ordinary overlays often collapse once the pointer leaves.

PageInfo

interface

PageInfo describes an open page (tab or popup) inside a browser context.

Fields
pageIdstring
browserContextIdstring
urlstring
titlestring
viewportRect
frameTreeFrameInfo

ReactionInfo

interface

ReactionInfo describes a still-pending reaction, as returned by CloudBrowser.listReactions. One-shot reactions that have already fired are gone and never appear here.

Fields
reactionIdstringStable id assigned by CloudBrowser.addReaction (pass to removeReaction).
matchSelectorstringSet if the reaction matches by CSS selector.
matchJsExpressionstringSet if the reaction matches by JS expression.
actionSelectorstringSet if the click target differs from the matched element.
actionJsExpressionstringSet if the click target differs from the matched element.
frameIdstringFrame scope: "" for the main frame, a specific frameId, or AllFrames.
visiblebooleanWhether the match additionally requires the element to be visible.

ReadCanvasResult is the pixel readback of a <canvas>, returned by CloudBrowser.readCanvas. dataBase64 holds the encoded image bytes (PNG by default) or the raw RGBA buffer when format is "rgba". originClean reports whether the canvas was untainted (informational — the read succeeds either way).

Fields
successboolean
frameIdstring
backendNodeIdnumber
dataBase64string
widthnumber
heightnumber
originCleanboolean

RentResponse

interface

RentResponse is the result of renting a browser via the REST API.

Fields
sessionIdstring
grpcUrlstring
countryCodestring
timezonestring
acceptLanguagestring
fingerprintstring

ScreenshotResult is a single captured image of the page, returned by CloudBrowser.screenshot. dataBase64 holds the encoded image bytes (PNG by default); width and height are in physical pixels.

Fields
dataBase64string
widthnumber
heightnumber

How a run ended.

Fields
successbooleanFalse when the script failed to compile or threw; result then holds the message.
resultstringThe return value as JSON, or the error message.
stoppedbooleanTrue when the run was cancelled, or the session went away under it, rather than the script returning on its own.

ScriptResult

interface

The outcome of a blocking CloudBrowser.runScript.

Fields
successbooleanFalse when the script failed to compile or threw; result then holds the message.
resultstringThe return value as JSON, or "undefined" when the script returned nothing. On failure it is the error message.
runIdstringNames the run. It arrives with the reply, so it is only useful after the fact — to match up log lines a separate follower already saw.
logScriptLogEntry[]Everything the script printed, in order.
truncatedbooleanTrue when the script printed more than the reply holds, in which case log is the tail of the output rather than all of it.

One run still in flight, as CloudBrowser.listScriptRuns reports it.

Fields
runIdstring
runningMsnumberHow long the run has been going, in milliseconds.

SelectOptionResult reports which option a selectByXxx call ended up selecting.

Fields
selectedIndexnumber
selectedValuestring
selectedTextstring

StreamAnswer

interface

StreamAnswer is what CloudBrowser.startStream replies with.

Fields
answerSdpstringSDP answer to apply as your peer's remote description.
viewport{ width: number; height: number } | nullThe page's viewport in CSS pixels — the coordinate space its input expects. The video may be displayed at any size, so map your pointer positions into this space before sending them. It comes back with the answer rather than from a separate CloudBrowser.getPages so it cannot race the stream or describe a different page, and the browser pushes {"type":"viewport","width":W,"height":H} on the reliable "input" data channel whenever it changes. Null against an older engine.

WaitResult

interface

WaitResult is the outcome of a CloudBrowser.wait / CloudBrowser.waitForAny call: which condition matched (index, in argument order) and where the matched element lives.

Fields
successbooleanFalse iff nothing matched before the deadline. Redundant with index being -1, and carried so every command answers the same question the same way.
indexnumber
frameIdstring
backendNodeIdnumber
isVisibleboolean
boundsRect

Data types

33 types

Everything else the API passes around: handles, events, records and enums.

AuthSession

interface

AuthSession is a portable snapshot of a context's signed-in Google account and/or DBSC sessions. Every field is optional, so a context that only has DBSC sessions (no primary account) or only a sign-in (no DBSC) round-trips.

Pair it with getCookies()/setCookies() and getStorage()/setStorage() to move a whole persona between fresh contexts.

Fields
gaiaId?stringGaia obfuscated account id.
email?stringAccount email.
refreshToken?stringOAuth refresh token (persistent).
wrappedBindingKey?stringBase64 of the wrapped device-binding key for the refresh token. Absent means the token is unbound.
signinScopedDeviceId?stringSignin-scoped device id; must travel with the token.
syncConsent?booleanTrue if the account should be restored at Sync consent.
dbscSessions?DbscSession[]Device Bound Session Credentials for this context (all bound sites).

Button

type alias
type Button = "left" | "right" | "middle"

Mouse button used by CloudBrowser.click.

ClickAction

type alias
type ClickAction = "click" | "press" | "release"

Mouse phase performed by CloudBrowser.click.

Partition metadata for partitioned cookies (CHIPS).

Fields
topLevelSitestring
hasCrossSiteAncestorboolean

DbscSession

interface

DbscSession is one Device Bound Session Credentials entry.

Fields
sitestringSerialized schemeful site key, e.g. "https://google.com".
sessionstringBase64 of the serialized DBSC Session proto. It includes the wrapped binding key, which is portable under WRC's software key provider.
type DomChangeHandler = (mirror: DomMirror) => void

Called after the tree changed. root is a fresh object whenever anything below it changed, so it can be compared by identity and rendered with memoized components.

DomNode

interface

A node in the mirrored page, in CDP's DOM.Node shape — the same shape CloudBrowser.getDOM returns, with two additions that the mirror needs and a caller usually wants anyway: DomNode.frameId and DomNode.contentFrameId.

An <iframe> is an ordinary element here. The document it hosts is its one entry in DomNode.children, present once the element has been expanded, and nothing about walking the tree has to know a process boundary runs through it.

Fields
nodeIdnumber
backendNodeIdnumber
nodeTypenumber
nodeNamestring
localName?string
nodeValue?string
attributes?string[]Flat name, value, name, value, ..., as CDP sends it.
childNodeCount?numberTotal children in the page, whether or not they are in children. An <iframe> reports 1: the document it hosts.
children?DomNode[]Present once the node has been expanded.
shadowRoots?DomNode[]Author shadow roots, when the mirror was started with pierce.
frameIdstringThe frame this node lives in. Always set. Together with backendNodeId this is the node's address: node ids are handed out per renderer and restart per frame, so two frames can and do use the same one, and the id on its own is ambiguous across a page.
contentFrameId?stringFor an element that hosts a frame (<iframe>, <frame>, <object>): the frame it hosts, which is a different frame from frameId and is the one its child document's ids belong to.
type DomResyncHandler = (reason: DomResyncReason) => void

Called when the mirror had to be rebuilt, after the new tree is in place.

type DomResyncReason = | "documentReplaced" | "overflow" | "rendererGone" | "slowReader" | string

Why a mirror had to be rebuilt.

DomSnapshot

interface

The opening snapshot: the main frame's document. Child frames are not in it — their documents are fetched by expanding the <iframe> elements that host them, which is also what starts mirroring them.

Fields
rootstring
frameIdstring
seqnumberThe page sequence this snapshot is the baseline for.

ElementRef

interface

ElementRef is a lightweight descriptor of an element — enough to identify it (and decide what to do) without another DOM round-trip. It names the element that stole focus in a FillError focus-loss failure.

Fields
backendNodeIdnumber
tagNamestringUpper-case tag name, e.g. "INPUT", "BUTTON", "DIV".
id?stringid attribute, if present.
name?stringname attribute, if present.
className?stringclass attribute, if present.
inputType?string<input> type, if the element is an <input>.
text?stringWhitespace-collapsed textContent/value snippet (max 120 chars).
editablebooleanWhether this element is itself an editable text sink (input / textarea / contenteditable).

HeaderModification is one entry passed to CloudBrowser.modifyRequest. Write it as a plain object literal.

Fields
actionHeaderModificationAction"add" inserts a new header, "edit" replaces an existing header's value, "remove" drops the header.
namestringHeader name the action applies to.
value?stringHeader value for add/edit; ignored for remove.
before?stringPositions an "add" immediately before the named existing header; otherwise the header is appended at the end. Ignored for edit/remove.
after?stringPositions an "add" immediately after the named existing header. Mirror of before; ignored for edit/remove.
type HeaderModificationAction = "add" | "edit" | "remove"

Action verb for a HeaderModification.

IceServer

interface

IceServer is one entry for a WebRTC RTCPeerConnection's iceServers config: a TURN (or STUN) URL plus the short-lived credentials to authenticate with it. Pass these to your peer before creating the offer.

Fields
urlsstring[]ICE server URLs (e.g. turn:relay.example.com:3478?transport=udp).
usernamestringShort-lived TURN REST username (empty for plain STUN).
credentialstringShort-lived TURN REST credential (empty for plain STUN).

InterceptedRequest describes an outgoing request captured by CloudBrowser.waitForAnyRequest.

Fields
methodstring
urlstring
headersHeader[]
bodystring
resourceTypestring

NetworkBodies

type alias
type NetworkBodies = "none" | "text" | "all"

Which response bodies a network capture keeps. Kept bodies are not part of the exchange; read them with CloudBrowser.readNetworkBody.

NetworkBody

interface

A whole captured body, from CloudBrowser.readNetworkBody.

Fields
dataUint8Array
truncatedbooleanThe kept body is shorter than the original; see the exchange's flag.

One range of a captured body, from CloudBrowser.readNetworkBodyRange.

Fields
dataUint8ArrayThe bytes read; empty past the end of the body.
totalSizenumberBytes kept for the body as a whole.
truncatedbooleanMatches the exchange's truncated flag for this body.

NetworkExchange is one request together with the response it received, as reported by CloudBrowser.captureNetwork.

A redirect chain arrives as one exchange per hop: the hops share chainId and count up redirectIndex, so a 302 and the request it points at are two exchanges, each with its own headers and status.

Fields
requestIdstringUnique per hop.
chainIdstringShared by every hop of one redirect chain.
redirectIndexnumber0 for the original request, incremented once per redirect followed.
frameIdstringThe frame that issued the request; empty for worker traffic.
isOopifbooleanWhether that frame runs in its own process. Capture happens in the browser process, so cross-process iframes are included.
resourceTypeNetworkResourceType
methodstring
urlstring
initiatorUrlstringThe origin that started the request; empty when the browser itself did.
requestHeadersHeader[]
requestHeadersAreWirebooleanWhether requestHeaders are the bytes actually sent — Cookie, User-Agent and Sec-* included — rather than what the page asked for before the network stack filled in the rest.
requestBodyIdstringNames the request body; read it with CloudBrowser.readNetworkBody. Empty when the request had no body. Bodies never travel with the exchange.
requestBodySizenumberBytes kept for the request body.
requestBodyTruncatedbooleanPart of the request body is missing: it hit the per-body cap, or it was a file or streamed upload, which are not kept.
hasResponsebooleanFalse when the request failed before any response arrived; see error.
statusCodenumber
statusTextstring
mimeTypestring
protocolstringNegotiated ALPN protocol, e.g. "h2" or "http/1.1".
remoteAddressstring
servedFromNetworkServedFrom
responseHeadersHeader[]
responseHeadersAreWireboolean
responseBodyIdstringNames the response body; read it with CloudBrowser.readNetworkBody. Empty when body capture did not apply to this exchange — see NetworkCaptureOptions.bodies.
responseBodySizenumberBytes kept for the response body, after content decoding.
responseBodyTruncatedbooleanThe kept body is shorter than the one the page received: it hit the per-body cap or the load ended early.
encodedDataLengthnumberBytes on the wire, not body size; 0 for a response served from cache.
errorstringNet error name (e.g. "net::ERR_ABORTED"), empty on success.
type NetworkExchangeHandler = (exchange: NetworkExchange) => void

NetworkExchangeHandler is called once per completed exchange.

Calls are sequential and in the order the browser finished the requests, so the hops of a redirect chain arrive in order.

Blocking here stalls the capture: awaiting something slow means the server keeps buffering, and once its per-reader bound is reached it drops the oldest entries, which NetworkCapture.dropped reports. Hand slow work (uploads, IndexedDB) to a queue of your own instead of awaiting it here.

type NetworkResourceType = | "document" | "subframe" | "script" | "stylesheet" | "image" | "font" | "media" | "fetch" | "worker" | "manifest" | "object" | "csp-report" | "other" | (string & {})

NetworkResourceType is the kind of load an exchange belongs to.

Typed as a union with a string fallback so an exchange from a newer browser still carries its value through instead of failing to type. Note that fetch(), XMLHttpRequest and EventSource all report "fetch": they are indistinguishable at the capture point.

type NetworkServedFrom = | "network" | "cache" | "serviceWorker" | "wrcStaticCache" | "wrcSynthetic" | (string & {})

NetworkServedFrom says where an exchange's response came from. "wrcStaticCache" is browserscale's own static cache — see CloudBrowser.setStaticPaths.

Rect

interface

Rect describes a position and size in CSS pixels.

Fields
xnumber
ynumber
widthnumber
heightnumber

RequestPattern matches a URL pattern in waitForAnyRequest/Response. Set abort to true to drop the request with an empty 200 response instead of letting it through to the network.

Fields
urlstring
abort?booleanDefault false.

ScriptEvent

interface

One item on a session's script event stream. Exactly one of log and finished is set.

Fields
runIdstringThe run that produced this event.
log?ScriptLogEntryA console line the script printed.
finished?ScriptFinishedThe end of the run. No further event for that run follows.
type ScriptEventHandler = (event: ScriptEvent) => void

ScriptEventHandler is called once per script event.

Calls are sequential and in the order the browser produced them, so a run's last log line always arrives before its finished.

Blocking here stalls delivery: the server buffers a bounded number of events per reader and then drops its oldest, which ScriptRun.dropped reports. Hand slow work to a queue of your own instead of awaiting it here.

One console.* call from a script.

Fields
levelstring"info", "warning" or "error", from console.log / .warn / .error.
messagestringThe logged arguments, already stringified the way console does it.
timestampDateWhen the script printed the line, stamped in the browser.

SessionUsage

interface

SessionUsage is what a session's browser has consumed since the session started: the CPU time and memory of every process that rendered its pages — main frames, cross-site iframes and the workers they host — including processes that have since exited. Work done on the session's behalf in processes it shares with other sessions is not included.

CloudBrowser.getUsage reports it while the session runs, and CloudBrowser.stopBrowser resolves with the final figures, so there is no need to read it right before stopping.

Fields
wallTimenumberReal time since the session's browser was created, in seconds.
cpuTimenumberUser plus kernel CPU time in seconds. Only time a thread actually ran on a core counts; waiting and idling do not.
minMemorynumberThe least memory the session held once its browser was ready, in bytes. Sampled about once a second.
averageMemorynumberThe memory held, averaged over wallTime, in bytes; averageMemory * wallTime is the memory-time used. Sampled about once a second, so short spikes count toward the peak but barely toward the average.
peakMemorynumberThe most memory held at any one moment, in bytes.
renderersUsednumberRenderer processes that hosted at least one of the session's frames: one per site its pages and cross-site iframes needed.
framesCreatednumberChild frames created in the session's pages, whether or not they got a process of their own.

StorageItem

interface

StorageItem is a single localStorage key/value pair.

Fields
keystring
valuestring

StorageOriginEntry groups the localStorage entries of one origin (e.g. "https://example.com"). getStorage() returns these and setStorage() accepts the same shape, so a dump can be fed back verbatim.

Fields
originstring
itemsStorageItem[]

WaitConditionStatus is the per-condition diagnostic carried by WaitError when a CloudBrowser.wait times out: one entry per condition (in the order they were passed) explaining why it never matched.

Fields
indexnumberIndex into the condition list this entry describes.
statestringLast observed state: "not_found", "found_hidden", "found_occluded" (only when the condition required visibility), or "pending_steady".
backendNodeIdnumberbackendNodeId last seen for this condition (0 if never found).
frameIdstringframeId where it was last seen (empty if never found).
isVisiblebooleanWhether it was CSS-visible at the last observation.
bounds?RectLast known rect in root-viewport coordinates (undefined if never found).
occluder?OccluderInfoThe intercepting element, present iff state === "found_occluded".

WaitUntil

type alias
type WaitUntil = "load" | "domcontentloaded" | "networkidle"

Lifecycle event CloudBrowser.navigate waits for before returning.