TypeScript SDK Reference
browserscale-ts. All methods return a Promise; the return types below show the resolved value (the Promise<…> wrapping is implicit).CloudBrowser
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. The session is implicitly bound to its primary page server-side — the proto's page_id field is currently ignored server-side, so the SDK never sets it.
Construct via rentBrowser() / createWebSocketBrowser() — never directly.
clearCookies
method on CloudBrowserclearCookies(): voidDeletes every cookie in the browser context.
await browser.clearCookies();void| UNKNOWN_ERROR | the cookies could not be cleared |
clearStorage
method on CloudBrowserclearStorage(origin: string | undefined): voidDeletes localStorage in the browser context.
| origin | string | undefined | if 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();void| UNKNOWN_ERROR | the storage could not be cleared |
click
method on CloudBrowserclick(target: Locator, opts: ClickOpts | undefined): ElementResultTriggers 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.
| target | Locator | locator describing what to click; at is also valid |
| opts | ClickOpts | undefined | optional click customization; see ClickOpts |
await browser.click(css("button.submit"));// Right double-click on a context menu trigger.
await browser.click(css("li.menu"), { button: "right", clickCount: 2 });ElementResultElementResult with success, resolved frameId, backendNodeId, post-scroll isVisible, element bounds, and the root-viewport (rootX, rootY) where the click landed| CLICK_FAILED | the click could not be dispatched |
| ELEMENT_NOT_FOUND | no element matched the locator |
| FRAME_NOT_FOUND | the requested frame does not exist |
| INVALID_LOCATOR | target is empty or has multiple targets set |
| PAGE_NOT_ALIVE | the page has been closed |
| TIMEOUT | the operation exceeded the server-side timeout |
dragBy
method on CloudBrowserdragBy(target: Locator, offsetX: number, offsetY: number): DragResultPicks 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.
| target | Locator | locator describing the element to pick up |
| offsetX | number | horizontal distance to drag, in CSS pixels |
| offsetY | number | vertical distance to drag, in CSS pixels |
await browser.dragBy(css(".slider .handle"), 120, 0);DragResultDragResult with the resolved frameId, backendNodeId and the final cursor position (rootX, rootY) where the drop happened| UNKNOWN_ERROR | the drag could not be performed |
dragTo
method on CloudBrowserdragTo(target: Locator, absoluteX: number, absoluteY: number): DragResultPicks 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.
| target | Locator | locator describing the element to pick up |
| absoluteX | number | horizontal drop coordinate in the root viewport |
| absoluteY | number | vertical drop coordinate in the root viewport |
await browser.dragTo(css(".card"), 800, 400);DragResultDragResult with the resolved frameId, backendNodeId and the final cursor position (rootX, rootY) where the drop happened| UNKNOWN_ERROR | the drag could not be performed |
evaluate
method on CloudBrowserevaluate(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.
| expression | string | JavaScript expression evaluated in the main frame |
const res = await browser.evaluate<string>("document.title");
console.log(res.value);EvaluateResult<T>EvaluateResult with either value (non-Element) or element metadata (Element)| UNKNOWN_ERROR | the expression threw or could not be compiled |
evaluateInFrame
method on CloudBrowserinherits evaluateevaluateInFrame(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.
| frameId | string | id of the frame to evaluate in |
| expression | string | JavaScript 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");EvaluateResult<T>EvaluateResult with either value (non-Element) or element metadata (Element)| UNKNOWN_ERROR | the expression threw or could not be compiled |
fill
method on CloudBrowserfill(target: Locator, text: string, opts: FillOpts | undefined): ElementResultClicks 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.
| target | Locator | locator describing the input element |
| text | string | text to type into the element |
| opts | FillOpts | undefined | optional 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 });ElementResultElementResult with success, resolved frameId, backendNodeId and the root-viewport (rootX, rootY) where the element was clicked| ELEMENT_NOT_FOUND | no element matched the locator |
| FILL_FAILED | the input could not be filled |
| FRAME_NOT_FOUND | the requested frame does not exist |
| INVALID_LOCATOR | target is empty or has multiple targets set |
| PAGE_NOT_ALIVE | the page has been closed |
| TIMEOUT | the operation exceeded the server-side timeout |
getApiKey
method on CloudBrowsergetApiKey(): stringReturns the API key used to rent this session.
stringgetCookies
method on CloudBrowsergetCookies(): 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);CookieParam[]CookieParam[], one per cookie in the context| UNKNOWN_ERROR | the cookies could not be read |
getDOM
method on CloudBrowsergetDOM(frameId: string, opts: GetDOMOpts | undefined): DOMResultReturns 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.
| frameId | string | id of the frame to dump; empty string targets the main frame |
| opts | GetDOMOpts | undefined | optional depth: -1 full tree (default), 0 root only, N root + N descendant levels |
const { dom } = await browser.getDOM();DOMResultDOMResult with the JSON string in .dom (the .hash field is populated by getDOMHash, not by this call)| UNKNOWN_ERROR | the DOM could not be retrieved |
getDOMHash
method on CloudBrowsergetDOMHash(frameId: string): stringReturns 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.
| frameId | string | id of the frame to hash; empty string targets the main frame |
const hash = await browser.getDOMHash();
if (hash !== lastHash) {
// DOM changed → re-fetch
}string16-char hex string (the first 8 bytes of sha256 of the DOM JSON)| UNKNOWN_ERROR | the hash could not be computed |
getFingerprint
method on CloudBrowsergetFingerprint(): stringReturns the browser fingerprint id in use for this session.
stringgetObservation
method on CloudBrowsergetObservation(opts: GetObservationOpts | undefined): ObservationResultReturns a compact, agent-friendly description of every interactable element currently visible on the page, together with a truncated view of the surrounding text.
Intended as input for LLM/agent loops where a full DOM dump would be too large; the server filters down to elements that are actually visible and interactable.
| opts | GetObservationOpts | undefined | optional caps: maxElementsPerFrame, maxTextLength; omit either to use the server default |
const obs = await browser.getObservation({ maxElementsPerFrame: 200 });
console.log(obs.text);ObservationResultObservationResult with both a human-readable text rendering and a json payload of the structured observation| UNKNOWN_ERROR | the observation could not be produced |
getPages
method on CloudBrowsergetPages(): 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);PageInfo[]PageInfo[] for every page currently open in the context| UNKNOWN_ERROR | the pages could not be enumerated |
getSelection
method on CloudBrowsergetSelection(): stringReturns 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);stringthe selected text, or "" when nothing is selected| UNKNOWN_ERROR | the selection could not be read |
getSessionId
method on CloudBrowsergetSessionId(): stringReturns the unique server-assigned id for this browser session.
stringgetStorage
method on CloudBrowsergetStorage(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.
| origin | string | undefined | if 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);
}StorageOriginEntry[]StorageOriginEntry[], one per origin with localStorage data| UNKNOWN_ERROR | the storage could not be read |
highlightNode
method on CloudBrowserhighlightNode(backendNodeId: number, frameId: string): voidPaints 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.
| backendNodeId | number | id of the node to highlight, or <= 0 to clear |
| frameId | string | id of the frame the node lives in; empty string targets the main frame |
await browser.highlightNode(res.backendNodeId, res.frameId);void| UNKNOWN_ERROR | the highlight could not be applied |
insertText
method on CloudBrowserinsertText(text: string): voidPastes 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.
| text | string | the text to insert at the caret |
await browser.insertText("hello world");void| UNKNOWN_ERROR | the text could not be inserted |
inspectAtPosition
method on CloudBrowserinspectAtPosition(x: number, y: number): InspectResultHit-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.
| x | number | viewport-relative x in CSS pixels |
| y | number | viewport-relative y in CSS pixels |
const r = await browser.inspectAtPosition(200, 300);
console.log(r.tagName, r.textContent);InspectResultInspectResult with the resolved backendNodeId, frameId, tag name, trimmed textContent, visibility and bounds| UNKNOWN_ERROR | the hit-test failed |
loadHTML
method on CloudBrowserloadHTML(url: string, html: string, opts: LoadHTMLOpts | undefined): voidServes 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.
| url | string | URL pattern that, when navigated to, returns the html |
| html | string | response body to serve |
| opts | LoadHTMLOpts | undefined | optional headers and statusCode (default 200) |
await browser.loadHTML("https://example.com", "<h1>hi</h1>");
await browser.navigate("https://example.com");void| UNKNOWN_ERROR | the interceptor could not be installed |
modifyRequest
method on CloudBrowsermodifyRequest(urlPattern: string, opts: {
body?: string;
modifications?: HeaderModification[];
timeoutMs?: number;
} | undefined): InterceptedRequest | nullWaits 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.
| urlPattern | string | URL wildcard to wait for |
| opts | { body?: string; modifications?: HeaderModification[]; timeoutMs?: number; } | undefined | optional 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);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| UNKNOWN_ERROR | no matching request appeared within the timeout |
moveTo
method on CloudBrowsermoveTo(target: Locator): ElementResultMoves 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).
| target | Locator | locator describing where to move; at is also valid |
await browser.moveTo(css("nav .menu"));ElementResultElementResult with the resolved frameId, backendNodeId, post-scroll isVisible, element bounds and the root-viewport (rootX, rootY) where the cursor ended up| UNKNOWN_ERROR | the move could not be completed |
pressKey
method on CloudBrowserpressKey(key: string, opts: { code?: string; modifiers?: number; location?: number } | undefined): voidFires 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.
| key | string | DOM KeyboardEvent.key value (e.g. "Enter", "a", "ArrowLeft") |
| opts | { code?: string; modifiers?: number; location?: number } | undefined | optional 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 });void| UNKNOWN_ERROR | the event could not be dispatched |
readCanvas
method on CloudBrowserreadCanvas(target: Locator, opts: ReadCanvasOpts | undefined): ReadCanvasResultReads 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.
| target | Locator | locator for the <canvas>; css, js or node |
| opts | ReadCanvasOpts | undefined | optional 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,
});ReadCanvasResultReadCanvasResult with the base64 image in dataBase64, the canvas width/height, resolved frameId/backendNodeId and the originClean flag| ELEMENT_NOT_FOUND | no element matched the locator |
| FRAME_NOT_FOUND | the requested frame does not exist |
| INVALID_LOCATOR | target is empty, uses at(x,y), or has multiple targets |
| PAGE_NOT_ALIVE | the page has been closed |
| TIMEOUT | the operation exceeded the server-side timeout |
releaseKey
method on CloudBrowserinherits pressKeyreleaseKey(key: string, opts: { code?: string; modifiers?: number; location?: number } | undefined): voidFires a single key-up event.
Mirror of pressKey. Same parameter semantics; use this to close a press cycle that was started with pressKey.
| key | string | DOM KeyboardEvent.key value (e.g. "Enter", "a", "ArrowLeft") |
| opts | { code?: string; modifiers?: number; location?: number } | undefined | optional 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 });void| UNKNOWN_ERROR | the event could not be dispatched |
screenshot
method on CloudBrowserscreenshot(opts: ScreenshotOpts | undefined): ScreenshotResultCaptures 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.
| opts | ScreenshotOpts | undefined | optional 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"));ScreenshotResultScreenshotResult with the base64 image in dataBase64 and the physical pixel width/height| UNKNOWN_ERROR | the screenshot could not be captured |
scrollTo
method on CloudBrowserscrollTo(target: Locator): ElementResultScrolls 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.
| target | Locator | locator describing the element to bring into view; at is rejected |
await browser.scrollTo(css("#footer"));ElementResultElementResult with the resolved frameId, backendNodeId, post-scroll isVisible and the element's bounds after the scroll| UNKNOWN_ERROR | the element could not be scrolled into view |
selectByIndex
method on CloudBrowserselectByIndex(target: Locator, index: number, opts: SelectOpts | undefined): SelectOptionResultPicks 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.
| target | Locator | locator describing the <select> element |
| index | number | zero-based option index |
| opts | SelectOpts | undefined | optional 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 });SelectOptionResultSelectOptionResult with the resolved selectedIndex, selectedValue and selectedText after the change| ELEMENT_NOT_FOUND | no element matched the locator |
| FRAME_NOT_FOUND | the requested frame does not exist |
| INVALID_LOCATOR | target is empty or has multiple targets set |
| PAGE_NOT_ALIVE | the page has been closed |
| SELECT_FAILED | the option could not be selected (out of range, or element is not a `<select>`) |
| TIMEOUT | the operation exceeded the server-side timeout |
selectByText
method on CloudBrowserinherits selectByIndexselectByText(target: Locator, text: string, opts: SelectOpts | undefined): SelectOptionResultPicks 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.
| target | Locator | locator describing the <select> element |
| text | string | the visible option text to match |
| opts | SelectOpts | undefined | optional select customization; see SelectOpts |
await browser.selectByText(css("select#country"), "Germany");SelectOptionResultSelectOptionResult with the resolved selectedIndex, selectedValue and selectedText after the change| ELEMENT_NOT_FOUND | no element matched the locator |
| FRAME_NOT_FOUND | the requested frame does not exist |
| INVALID_LOCATOR | target is empty or has multiple targets set |
| PAGE_NOT_ALIVE | the page has been closed |
| SELECT_FAILED | the option could not be selected (out of range, or element is not a `<select>`) |
| TIMEOUT | the operation exceeded the server-side timeout |
selectByValue
method on CloudBrowserinherits selectByIndexselectByValue(target: Locator, value: string, opts: SelectOpts | undefined): SelectOptionResultPicks 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.
| target | Locator | locator describing the <select> element |
| value | string | the value attribute to match |
| opts | SelectOpts | undefined | optional select customization; see SelectOpts |
await browser.selectByValue(css("select#country"), "DE");SelectOptionResultSelectOptionResult with the resolved selectedIndex, selectedValue and selectedText after the change| ELEMENT_NOT_FOUND | no element matched the locator |
| FRAME_NOT_FOUND | the requested frame does not exist |
| INVALID_LOCATOR | target is empty or has multiple targets set |
| PAGE_NOT_ALIVE | the page has been closed |
| SELECT_FAILED | the option could not be selected (out of range, or element is not a `<select>`) |
| TIMEOUT | the operation exceeded the server-side timeout |
setBlockList
method on CloudBrowsersetBlockList(patterns: string[]): voidReplaces 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.
| patterns | string[] | URL wildcards to block; empty array clears the list |
await browser.setBlockList([
"*.doubleclick.net/*",
"*googletagmanager.com*",
]);void| UNKNOWN_ERROR | the blocklist could not be applied |
setCookies
method on CloudBrowsersetCookies(cookies: CookieParam[]): voidWrites 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.
| cookies | CookieParam[] | cookies to write; empty array is a no-op |
await browser.setCookies([
{ name: "auth", value: "tok", domain: "example.com", path: "/" },
]);void| UNKNOWN_ERROR | the cookies could not be written |
setProxy
method on CloudBrowsersetProxy(proxyHost: string, proxyPort: number, proxyUsername: string, proxyPassword: string): voidChanges 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.
| proxyHost | string | upstream proxy host; empty disables the proxy |
| proxyPort | number | upstream proxy port; ignored when proxyHost is empty |
| proxyUsername | string | proxy auth user; empty for unauthenticated proxies |
| proxyPassword | string | proxy auth password; empty for unauthenticated proxies |
await browser.setProxy("proxy.example.com", 8080, "user", "pass");void| UNKNOWN_ERROR | the proxy could not be applied |
setStaticPaths
method on CloudBrowsersetStaticPaths(blobName: string, patterns: string[]): voidConfigures 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.
| blobName | string | server-side identifier of the snapshot to serve from |
| patterns | string[] | URL wildcards to redirect to the cache; empty disables |
await browser.setStaticPaths("snap-2026-05", ["*.example.com/*"]);void| UNKNOWN_ERROR | the static paths could not be configured |
setStorage
method on CloudBrowsersetStorage(storage: StorageOriginEntry[]): voidWrites 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.
| storage | StorageOriginEntry[] | entries to write, grouped by origin |
await browser.setStorage([
{
origin: "https://example.com",
items: [
{ key: "token", value: "abc123" },
{ key: "theme", value: "dark" },
],
},
]);void| UNKNOWN_ERROR | the storage could not be written |
solveCaptcha
method on CloudBrowsersolveCaptcha(opts: { timeoutMs?: number; retryAmount?: number } | undefined): stringDetects 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.
| opts | { timeoutMs?: number; retryAmount?: number } | undefined | optional 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 });stringempty string on success — the solution is applied server-side| UNKNOWN_ERROR | no captcha appeared within timeoutMs, or the detected captcha could not be solved within retryAmount attempts |
wait
method on CloudBrowserinherits waitAnywait(condition: Locator, opts: WaitOpts | undefined): WaitResultBlocks 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.
| condition | Locator | the single locator to wait for |
| opts | WaitOpts | undefined | optional wait customization (timeoutMs) |
await browser.wait(css(".success"));WaitResultWaitResult for the first matching condition| UNKNOWN_ERROR | the wait timed out or a condition was invalid |
waitAny
method on CloudBrowserwaitAny(conditions: Locator[], opts: WaitOpts | undefined): WaitResultBlocks 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.
| conditions | Locator[] | one or more Locators to wait for; must be non-empty |
| opts | WaitOpts | undefined | optional wait customization (timeoutMs) |
const r = await browser.waitAny(
[css(".success"), js("window.__ready === true")],
{ timeoutMs: 5000 },
);
console.log("matched index:", r.index);WaitResultWaitResult for the first matching condition| UNKNOWN_ERROR | the wait timed out or a condition was invalid |
waitForAnyRequest
method on CloudBrowserwaitForAnyRequest(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.
| patterns | RequestPattern[] | one or more URL patterns (with optional abort flags) |
| opts | { timeoutMs?: number } | undefined | optional 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);{ 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)| UNKNOWN_ERROR | the wait timed out or no patterns were supplied |
waitForAnyResponse
method on CloudBrowserinherits waitForAnyRequestwaitForAnyResponse(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.
| patterns | RequestPattern[] | one or more URL patterns (with optional abort flags) |
| opts | { timeoutMs?: number } | undefined | optional timeoutMs; omit to use the server default |
const { index, response } = await browser.waitForAnyResponse(
[{ url: "*\/api/login" }],
{ timeoutMs: 5000 },
);
console.log(index, response?.statusCode);{ index: number; response: InterceptedResponse | null }object with index (matched pattern index) and response (the captured status/headers/body; null if no body was returned)| UNKNOWN_ERROR | the wait timed out or no patterns were supplied |
Locator
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.
inAllFrames
method on LocatorinAllFrames(): LocatorScopes 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());Locatora new Locator scoped to all framesinFrame
method on LocatorinFrame(frameId: string): LocatorScopes this Locator to a specific frameId.
Use the frameId from a previous result or CloudBrowser.getPages to target elements inside a known iframe.
| frameId | string | id 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));Locatora new Locator scoped to that framesteady
method on Locatorsteady(ms: number): LocatorRequires the element to keep a stable position and size for at least ms milliseconds before the wait matches.
Pass 0 to disable the default DefaultSteadyMs (500). Has no effect for js() expressions that return a non-Element value, nor when used as an action target.
| ms | number | steady-state duration in milliseconds; 0 disables |
await browser.wait(css(".banner").steady(0));Locatora new Locator with the override appliedvisible
method on Locatorvisible(v: boolean): LocatorEnforces or disables the visibility check for this Locator's wait condition.
Pass false to opt out of the default DefaultVisible (true). Has no effect when used as an action target — actions never check visibility before dispatching.
| v | boolean | true to require visibility, false to skip the check |
await browser.wait(css("#hidden").visible(false));Locatora new Locator with the override appliedBrowserConfig
Configuration for renting a browser session. Create with the required parameters, then chain optional setters.
withCountryCode
method on BrowserConfigwithCountryCode(countryCode: string): BrowserConfigSets 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.
| countryCode | string | ISO-3166 country code (e.g. "DE", "US") |
new BrowserConfig(apiKey, 600, "", 0, "", "").withCountryCode("DE");BrowserConfigthis BrowserConfig for chainingwithFingerprint
method on BrowserConfigwithFingerprint(fingerprint: string): BrowserConfigPins 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.
| fingerprint | string | server-side fingerprint id |
new BrowserConfig(apiKey, 600, "", 0, "", "").withFingerprint("fp_abc123");BrowserConfigthis BrowserConfig for chainingwithTimezone
method on BrowserConfigwithTimezone(timezone: string): BrowserConfigSets the IANA timezone for the rented session.
| timezone | string | IANA timezone (e.g. "Europe/Berlin") |
new BrowserConfig(apiKey, 600, "", 0, "", "").withTimezone("Europe/Berlin");BrowserConfigthis BrowserConfig for chainingFunctions
at
functionat(x: number, y: number): Locatorat 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.
| x | number | viewport-relative x in CSS pixels |
| y | number | viewport-relative y in CSS pixels |
// Click at canvas-relative coordinates.
await browser.click(at(120, 240));LocatorLocator usable only as an action targetcreateWebSocketBrowser
functioncreateWebSocketBrowser(wsUrl: string, sessionId: string, apiKey: string, fingerprint: string): CloudBrowserAttaches 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.
| wsUrl | string | WebSocket URL (ws:// or wss://) of the session host |
| sessionId | string | id of the existing session |
| apiKey | string | API key authorizing access to the session |
| fingerprint | string | browser fingerprint id; empty if unknown |
const browser = createWebSocketBrowser(
"wss://session-abc.browserscale.example.com/ws",
sessionId,
apiKey,
);
await browser.navigate("https://example.com");CloudBrowserCloudBrowser attached to the existing sessioncss
functioncss(selector: string): Locatorcss waits for / targets an element matching the given CSS selector.
When used in CloudBrowser.wait/CloudBrowser.waitAny, the returned Locator carries the SDK defaults DefaultVisible (true) and DefaultSteadyMs (500). 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.
| selector | string | CSS selector matching the element |
// As a wait condition.
await browser.wait(css("button.submit"));
// As an action target.
await browser.click(css("button.submit"));LocatorLocator usable as a wait condition or as an action targetjs
functionjs(expression: string): Locatorjs waits for / targets the result of a JavaScript expression.
Same wait defaults as css (DefaultVisible=true, DefaultSteadyMs=500); 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.
| expression | string | JavaScript expression evaluated in the target frame |
await browser.wait(js("window.__ready === true"));LocatorLocator usable as a wait condition or as an action targetnode
functionnode(backendNodeId: number): Locatornode 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.
| backendNodeId | number | DevTools backendNodeId of the target element |
const r = await browser.click(css("button.open"));
await browser.click(node(r.backendNodeId));LocatorLocator usable only as an action targetrentBrowser
functionrentBrowser(config: BrowserConfig): CloudBrowserRents 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.
| config | BrowserConfig | rental parameters |
const cfg = new BrowserConfig("sk_…", 600, "", 0, "", "");
const browser = await rentBrowser(cfg);
try {
await browser.navigate("https://example.com");
} finally {
await browser.stopBrowser();
}CloudBrowserCloudBrowser ready to drive the rented session| UNKNOWN_ERROR | the rent API rejected the request or the gRPC connection could not be established |
setApiEndpoint
functionsetApiEndpoint(endpoint: string): voidOverrides 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.
| endpoint | string | base URL of the rent/stop service, with no trailing slash |
setApiEndpoint("https://browserscale.internal.example.com");voidstopBrowser
functionstopBrowser(apiKey: string, sessionId: string): voidReleases 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.
| apiKey | string | API key the session was rented with |
| sessionId | string | id of the session to release |
await stopBrowser(apiKey, sessionId);void| UNKNOWN_ERROR | the stop API rejected the request |
Types
BrowserScaleError
classBrowserScaleError is the single error type thrown by all SDK methods. It wraps either: - a client-side validation failure (bad locator, missing patterns, …) - a server-side gRPC error (Connect's ConnectError, available as cause)
Catch it with instanceof BrowserScaleError:
try { await browser.click(css("#btn")); } catch (e) { if (e instanceof BrowserScaleError) { ... } }
Button
type aliastype Button = "left" | "right" | "middle"Mouse button used by CloudBrowser.click.
ClickAction
type aliastype ClickAction = "click" | "press" | "release"Mouse phase performed by CloudBrowser.click.
ClickOpts
interfaceOptional customization for CloudBrowser.click. All fields are optional; missing or zero values mean "use the server default".
| inFrame? | string | Override 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? | Button | Mouse button. Default "left". |
| clickCount? | number | 1 = 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
interfaceCookieParam is one entry returned by getCookies() or passed to setCookies().
| name | string | |
| value | string | |
| url? | string | |
| domain | string | |
| path | string | |
| secure? | boolean | |
| httpOnly? | boolean | |
| sameSite? | string | |
| expires? | number | |
| priority? | string | |
| sourceScheme? | string | |
| sourcePort? | number | |
| partitionKey? | CookiePartitionKey |
CookiePartitionKey
interfacePartition metadata for partitioned cookies (CHIPS).
| topLevelSite | string | |
| hasCrossSiteAncestor | boolean |
DOMResult
interfaceDOMResult is the full-tree DOM snapshot returned by CloudBrowser.getDOM, plus its sha256:8 hash for cheap change detection.
| hash | string | |
| dom | string |
DragResult
interfaceDragResult is the outcome of a CloudBrowser.drag gesture: the resolved source element and the start/end coordinates of the performed drag.
| success | boolean | |
| frameId | string | |
| backendNodeId | number | |
| startX | number | |
| startY | number | |
| endX | number | |
| endY | number |
ElementResult
interfaceElementResult 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.
| success | boolean | |
| frameId | string | |
| backendNodeId | number | |
| isVisible | boolean | |
| bounds | Rect | |
| rootX | number | |
| rootY | number |
EvaluateResult
interfaceEvaluateResult 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.
| value | T | |
| backendNodeId | number | |
| isVisible | boolean | |
| bounds | Rect |
FillOpts
interfaceOptional customization for CloudBrowser.fill. All fields are optional; missing values mean "use the server default".
| inFrame? | string | Override 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? | boolean | true wipes the field's existing content with Ctrl+A, Delete before typing. Default (false) appends to whatever is already in the field. |
FrameInfo
interfaceFrameInfo describes a single frame within a page's frame tree.
GetDOMOpts
interfaceDepth used by getDOM(). 0 = whole tree (default), >0 = limited depth.
| depth? | number |
GetObservationOpts
interfaceOptional customization for CloudBrowser.getObservation.
| maxElementsPerFrame? | number | Default 200. |
| maxTextLength? | number | Default 200. |
Header
interfaceHeader is a single HTTP header (name/value pair) on an intercepted request or response.
| name | string | |
| value | string |
HeaderModification
interfaceHeaderModification is one entry passed to CloudBrowser.modifyRequest. Write it as a plain object literal.
| action | HeaderModificationAction | "add" inserts a new header, "edit" replaces an existing header's value, "remove" drops the header. |
| name | string | Header name the action applies to. |
| value? | string | Header value for add/edit; ignored for remove. |
| before? | string | Positions an "add" immediately before the named existing header; otherwise the header is appended at the end. Ignored for edit/remove. |
| after? | string | Positions an "add" immediately after the named existing header. Mirror of before; ignored for edit/remove. |
HeaderModificationAction
type aliastype HeaderModificationAction = "add" | "edit" | "remove"Action verb for a HeaderModification.
InspectResult
interfaceInspectResult describes the topmost element hit at viewport-relative (x, y). backendNodeId === 0 means nothing was found at that position.
| backendNodeId | number | |
| frameId | string | |
| tagName | string | |
| textContent | string | |
| isVisible | boolean | |
| bounds | Rect |
InterceptedRequest
interfaceInterceptedRequest describes an outgoing request captured by CloudBrowser.waitForAnyRequest.
| method | string | |
| url | string | |
| headers | Header[] | |
| body | string | |
| resourceType | string |
InterceptedResponse
interfaceInterceptedResponse describes a network response captured by CloudBrowser.waitForAnyResponse.
| url | string | |
| statusCode | number | |
| headers | Header[] | |
| body | string |
LoadHTMLOpts
interfaceOptional customization for CloudBrowser.loadHTML.
| headers? | { name: string; value: string }[] | Extra headers to attach to the synthetic response. |
| statusCode? | number | Default 200. |
ObservationResult
interfaceObservationResult is the compact page snapshot returned by CloudBrowser.getObservation — the visible, interactive elements rendered as prompt-friendly text and as JSON.
| text | string | |
| json | string |
PageInfo
interfacePageInfo describes an open page (tab or popup) inside a browser context.
ReadCanvasOpts
interfaceOptional customization for CloudBrowser.readCanvas.
| inFrame? | string | Override 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? | number | Encode quality 0-100 for "jpeg"/"webp" (ignored otherwise). Default 90. |
| sx? | number | Optional 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 |
ReadCanvasResult
interfaceReadCanvasResult 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).
| success | boolean | |
| frameId | string | |
| backendNodeId | number | |
| dataBase64 | string | |
| width | number | |
| height | number | |
| originClean | boolean |
Rect
interfaceRect describes a position and size in CSS pixels.
| x | number | |
| y | number | |
| width | number | |
| height | number |
RentResponse
interfaceRentResponse is the result of renting a browser via the REST API.
| sessionId | string | |
| grpcUrl | string | |
| countryCode | string | |
| timezone | string | |
| acceptLanguage | string | |
| fingerprint | string |
RequestPattern
interfaceRequestPattern 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.
| url | string | |
| abort? | boolean | Default false. |
ScreenshotOpts
interfaceOptional customization for CloudBrowser.screenshot.
| format? | "png" | "jpeg" | "webp" | Image format: "png" (default), "jpeg", or "webp". |
| quality? | number | Encode quality 0-100 for "jpeg"/"webp" (ignored for "png"). Default 90. |
ScreenshotResult
interfaceScreenshotResult 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.
| dataBase64 | string | |
| width | number | |
| height | number |
SelectOptionResult
interfaceSelectOptionResult reports which option a selectByXxx call ended up selecting.
| selectedIndex | number | |
| selectedValue | string | |
| selectedText | string |
SelectOpts
interfaceOptional customization for CloudBrowser.selectByIndex, CloudBrowser.selectByValue and CloudBrowser.selectByText.
| inFrame? | string | Override 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? | boolean | false suppresses change/input events. Default (true) fires the standard events after the selection. |
StorageItem
interfaceStorageItem is a single localStorage key/value pair.
| key | string | |
| value | string |
StorageOriginEntry
interfaceStorageOriginEntry 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.
| origin | string | |
| items | StorageItem[] |
WaitOpts
interfaceOptional customization for CloudBrowser.wait / CloudBrowser.waitForAny.
| timeoutMs? | number | Default DefaultWaitTimeoutMs (30 000 ms). |
WaitResult
interfaceWaitResult is the outcome of a CloudBrowser.wait / CloudBrowser.waitForAny call: which condition matched (index, in argument order) and where the matched element lives.
| index | number | |
| frameId | string | |
| backendNodeId | number | |
| isVisible | boolean | |
| bounds | Rect |
WaitUntil
type aliastype WaitUntil = "load" | "domcontentloaded" | "networkidle"Lifecycle event CloudBrowser.navigate waits for before returning.