browserscale

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2026 License: MIT Imports: 12 Imported by: 0

README

browserscale-go — browserscale SDK for Go

Official Go SDK for browserscale: real Chromium browsers in the cloud, driven over gRPC. Rent an isolated browser session in seconds, automate it with human-like input, intercept network traffic, solve captchas, and watch a live video stream of everything your script does.

Features

  • Real browser sessions as a service — full Chromium in the cloud with pages, frames, cookies, storage and network state. No local binary.
  • Parallel isolated contexts — each task gets its own session, fingerprint and lifecycle; large queues never share browser state. Sessions are browser contexts, not VMs or processes, so they spin up in under 50 ms and fan out to thousands in parallel.
  • Fingerprint & proxy handling — pinnable server-side fingerprints, native Chrome control without CDP/Playwright/Puppeteer leaks, bring your own proxy or let browserscale allocate one.
  • Native engine-level control — automation runs natively inside Chromium itself, not from outside over the DevTools protocol. Nothing is injected, no Runtime.enable, no DevTools handshake — page JS can't observe it. Waits run fully async with no polling loop, and built-in steady-time checks only report an element once it's stable in the DOM.
  • One flat frame tree — main document, same-origin iframes and cross-origin OOPIFs are all just a frameId in one tree, no flattened sessions or per-frame execution-context juggling. Wait/Click act across all frames or a single iframe, and Wait returns the frameId that matched.
  • Human-like interaction — mouse paths use browserscale's own movement algorithm instead of instant synthetic jumps.
  • WebRTC live video stream — watch and control the rented browser live from the browserscale web interface; mouse and keyboard go back over data channels.
  • Captcha support, no third-party solvers — passive anti-bot checks are handled automatically; interactive challenges are solved with SolveCaptcha by browserscale's own AI solver, which learns the known challenge types — puzzle, OCR, slide, hold and more — on its own and keeps improving as they evolve. No token is ever synthesized or fetched from an external API: the challenge is completed in the valid live browser and the provider's own JavaScript issues the token itself — which is why even new or unknown protections pass.
  • Real hardware, real GPUs — sessions run hardware-accelerated on real consumer GPUs, not on VM cores with a WebGL faking layer. Canvas and WebGL readbacks (toDataURL, getImageData) return genuinely rendered pixels — no spoofing layer or fingerprint hash database for new bot protections to unmask.
  • Network control at the source — interception sits in the browser's network stack itself, so every request from every frame (including cross-origin OOPIFs) passes through it; no handler races, nothing slips through. Wait for, block, mock or modify requests and responses without leaving the SDK; mark repeated assets as static with SetStaticPaths to serve them from a server-side cache and cut proxy bandwidth on repeat runs.
  • Agent-friendly observationGetObservation returns a compact text/JSON view of the visible, interactive elements across every frame, each with a node handle to act on, so a model reasons over what matters instead of raw HTML.
  • Flow-optimized, idiomatic Go — context-first methods with explicit errors, Wait races multiple outcomes, JS locators target elements by page logic when CSS is not enough.

Install

go get github.com/browserscale/browserscale-go

Requires Go 1.22+. The gRPC stubs ship precompiled — no protoc needed.

Quickstart

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/browserscale/browserscale-go"
)

func main() {
    ctx := context.Background()

    // Empty proxy fields tell browserscale to allocate a managed proxy server-side;
    // pass your own host/port/creds to bring your own.
    cfg := browserscale.NewBrowserConfig(
        "YOUR_API_KEY", // sk_…
        300,            // rent duration in seconds (5 minutes)
        "", 0, "", "",  // proxy host / port / user / pass
    )

    browser, err := browserscale.RentBrowser(ctx, cfg)
    if err != nil {
        log.Fatal(err)
    }
    defer browser.Close() // always release the session

    if _, err := browser.Navigate(ctx, "https://example.com", 0); err != nil {
        log.Fatal(err)
    }
    if _, err := browser.Wait(ctx, browserscale.CSS("h1")); err != nil {
        log.Fatal(err)
    }

    res, err := browser.Evaluate(ctx, "document.title")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("title:", res.Value)
}

Run it and you should see title: Example Domain. Get an API key from your dashboard.

Documentation

A runnable example lives in examples/simple.

TypeScript

Prefer Node.js or the browser? Use the TypeScript SDK: browserscale-ts.

License

MIT

Documentation

Overview

Package browserscale is the official Go SDK for browserscale — real Chromium browsers in the cloud, driven over gRPC.

Rent an isolated browser session, navigate, interact with the page using human-like input, intercept network traffic, manage cookies, and stream live video — all from idiomatic, context-first Go.

A minimal end-to-end program:

ctx := context.Background()

cfg := browserscale.NewBrowserConfig("YOUR_API_KEY", 300, "", 0, "", "")
browser, err := browserscale.RentBrowser(ctx, cfg)
if err != nil {
	log.Fatal(err)
}
defer browser.Close()

if _, err := browser.Navigate(ctx, "https://example.com", 0); err != nil {
	log.Fatal(err)
}
if _, err := browser.Wait(ctx, browserscale.CSS("h1")); err != nil {
	log.Fatal(err)
}

res, err := browser.Evaluate(ctx, "document.title")
if err != nil {
	log.Fatal(err)
}
fmt.Println("title:", res.Value)

Full documentation, guides and the complete API reference live at https://browserscale.cloud/docs.

Index

Constants

View Source
const (
	// DefaultWaitTimeoutMs is the timeout used by Wait when no
	// browserscale.Timeout(ms) option is passed.
	DefaultWaitTimeoutMs = 30000.0

	// DefaultVisible is the visibility flag baked into browserscale.CSS(...) and
	// browserscale.JS(...). For JS expressions returning a non-Element value
	// (boolean, string, number, plain object) the flag is a no-op.
	// Use .Visible(false) on the returned Locator to opt out.
	DefaultVisible = true

	// DefaultSteadyMs is the steady-time (in ms) baked into browserscale.CSS(...) and
	// browserscale.JS(...). For JS expressions returning a non-Element value the
	// value is a no-op. Use .Steady(ms) (or .Steady(0) to disable) on the
	// returned Locator to override.
	DefaultSteadyMs = 500.0
)
View Source
const AllFrames = "ALL_FRAMES"

AllFrames is the sentinel for "match in every frame on the page". Use it for any field documented as accepting a frameId — both Locator.InFrame and XxxOpts.InFrame.

Variables

View Source
var ApiEndpoint = "https://api.browserscale.cloud"

Functions

func SetApiEndpoint

func SetApiEndpoint(endpoint string)

SetApiEndpoint 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.

@param endpoint - base URL of the rent/stop service, with no trailing slash

@example

browserscale.SetApiEndpoint("https://browserscale.internal.example.com")

func StopBrowser

func StopBrowser(ctx context.Context, apiKey string, sessionId string) error

StopBrowser 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.

@param apiKey - API key the session was rented with @param sessionId - id of the session to release

@throws UNKNOWN_ERROR - the stop API rejected the request

@example

_ = browserscale.StopBrowser(context.Background(), apiKey, sessionId)

Types

type BrowserConfig

type BrowserConfig struct {
	// contains filtered or unexported fields
}

BrowserConfig holds all parameters for renting a browser session. Use NewBrowserConfig with the required fields, then chain optional setters.

func NewBrowserConfig

func NewBrowserConfig(apiKey string, rentDuration int, proxyHost string, proxyPort int, proxyUsername string, proxyPassword string) *BrowserConfig

NewBrowserConfig returns a BrowserConfig populated with the required rental fields. Optional fields are configured via the chainable With… setters before passing the config to RentBrowser.

@param apiKey - API key authenticating the rental @param rentDuration - lifetime of the session in seconds @param proxyHost - upstream proxy host (empty string disables the proxy) @param proxyPort - upstream proxy port (ignored when proxyHost is empty) @param proxyUsername - proxy auth user (empty for unauthenticated proxies) @param proxyPassword - proxy auth password (empty for unauthenticated proxies)

@returns *BrowserConfig ready to be customized further or passed to RentBrowser

@example

cfg := browserscale.NewBrowserConfig("sk_…", 600, "", 0, "", "").
    WithCountryCode("DE").
    WithTimezone("Europe/Berlin")

func (*BrowserConfig) UnstableWithFakeGpu

func (c *BrowserConfig) UnstableWithFakeGpu(renderer string, vendor string, extensions []string) *BrowserConfig

UnstableWithFakeGpu overrides WebGL UNMASKED_RENDERER_WEBGL, UNMASKED_VENDOR_WEBGL and getSupportedExtensions().

Unstable API — likely to be reshaped or removed without notice. Use only when you have a specific WebGL-fingerprint requirement.

@param renderer - value to return for UNMASKED_RENDERER_WEBGL @param vendor - value to return for UNMASKED_VENDOR_WEBGL @param extensions - list returned by getSupportedExtensions()

@returns the modified *BrowserConfig for chaining

@example

cfg.UnstableWithFakeGpu("ANGLE", "Google Inc.", []string{"OES_texture_float"})

func (*BrowserConfig) WithCountryCode

func (c *BrowserConfig) WithCountryCode(countryCode string) *BrowserConfig

WithCountryCode 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.

@param countryCode - ISO-3166 country code (e.g. "DE", "US")

@returns the modified *BrowserConfig for chaining

@example

cfg := browserscale.NewBrowserConfig(apiKey, 600, "", 0, "", "").WithCountryCode("DE")

func (*BrowserConfig) WithFingerprint

func (c *BrowserConfig) WithFingerprint(fingerprint string) *BrowserConfig

WithFingerprint 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.

@param fingerprint - server-side fingerprint id

@returns the modified *BrowserConfig for chaining

@example

cfg := browserscale.NewBrowserConfig(apiKey, 600, "", 0, "", "").WithFingerprint("fp_abc123")

func (*BrowserConfig) WithTimezone

func (c *BrowserConfig) WithTimezone(timezone string) *BrowserConfig

WithTimezone sets the IANA timezone for the rented session.

@param timezone - IANA timezone (e.g. "Europe/Berlin")

@returns the modified *BrowserConfig for chaining

@example

cfg := browserscale.NewBrowserConfig(apiKey, 600, "", 0, "", "").WithTimezone("Europe/Berlin")

type ClickOpts

type ClickOpts struct {
	// InFrame overrides the locator's own frame.
	// Empty = use the locator's frame (or the main frame if none).
	// Pass a specific frameId, or [AllFrames], to search elsewhere.
	InFrame string

	// Button is the mouse button to use.
	// Valid: "left" (default), "right", "middle".
	Button string

	// ClickCount controls single/double-click.
	// 0 or 1 = single click (default), 2 = double-click.
	ClickCount int32

	// Action selects the mouse phase.
	// "" or "click" = full mouseDown+mouseUp (default).
	// "press" only dispatches mouseDown.
	// "release" only dispatches mouseUp at the current cursor position.
	Action string
}

ClickOpts customizes a CloudBrowser.ClickWith call. Zero/empty values mean "use the server default".

type CloudBrowser

type CloudBrowser struct {
	// contains filtered or unexported fields
}

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

One CloudBrowser corresponds to exactly one browser context, which 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.

func ConnectSession

func ConnectSession(ctx context.Context, grpcUrl string, apiKey string, sessionId string) (*CloudBrowser, error)

ConnectSession attaches to an already-running session via gRPC.

Use this when you have a session id and gRPC URL from a previous RentBrowser (for example stored across process restarts). Unlike RentBrowser this does not call the rent API — the session must already exist server-side.

@param grpcUrl - the session's gRPC endpoint as returned by CloudBrowser.GrpcUrl,

e.g. "grpcs://api.browserscale.cloud:443" (TLS) or "grpc://host:port" (plaintext)

@param apiKey - API key authorizing access to the session @param sessionId - id of the existing session to attach to

@returns *CloudBrowser attached to the existing session; the returned

handle owns no rental, so calling Close only closes the gRPC connection

@throws UNKNOWN_ERROR - the gRPC connection could not be opened

@example

browser, err := browserscale.ConnectSession(ctx, "grpcs://api.browserscale.cloud:443", apiKey, sessionId)
if err != nil {
    log.Fatal(err)
}
defer browser.Close()

func RentBrowser

func RentBrowser(ctx context.Context, config *BrowserConfig) (*CloudBrowser, error)

RentBrowser 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. On any failure the partially-rented session is best-effort released.

@param config - rental parameters built with NewBrowserConfig

@returns *CloudBrowser ready to drive the rented session; call

[CloudBrowser.Close] or [CloudBrowser.StopBrowser] when done

@throws UNKNOWN_ERROR - the rent API rejected the request or the gRPC

connection could not be established

@example

cfg := browserscale.NewBrowserConfig("sk_…", 600, "", 0, "", "")
browser, err := browserscale.RentBrowser(ctx, cfg)
if err != nil {
    log.Fatal(err)
}
defer browser.Close()

func (*CloudBrowser) AcceptLanguage

func (c *CloudBrowser) AcceptLanguage() string

AcceptLanguage returns the Accept-Language header value the session was provisioned with.

func (*CloudBrowser) ApiKey

func (c *CloudBrowser) ApiKey() string

ApiKey returns the API key used to rent this session.

func (*CloudBrowser) ClearCookies

func (c *CloudBrowser) ClearCookies(ctx context.Context) error

ClearCookies deletes every cookie in the browser context.

@throws UNKNOWN_ERROR - the cookies could not be cleared

@example

_ = browser.ClearCookies(ctx)

func (*CloudBrowser) ClearStorage

func (c *CloudBrowser) ClearStorage(ctx context.Context, origin string) error

ClearStorage deletes localStorage in the browser context.

@param origin - if non-empty, only this origin's storage is deleted

(e.g. "https://example.com"); empty string deletes all origins

@throws UNKNOWN_ERROR - the storage could not be cleared

@example

// Wipe one origin.
_ = browser.ClearStorage(ctx, "https://example.com")

// Wipe everything.
_ = browser.ClearStorage(ctx, "")

func (*CloudBrowser) Click

func (c *CloudBrowser) Click(ctx context.Context, target *Locator) (*ElementResult, error)

Click 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.

@param target - locator describing what to click; At is also valid

@returns *ElementResult with success, resolved frameId, backendNodeId,

post-scroll isVisible, element bounds and the root-viewport (rootX, rootY)
where the click landed

@throws INVALID_LOCATOR - target is empty or has multiple targets set @throws ELEMENT_NOT_FOUND - no element matched the locator @throws FRAME_NOT_FOUND - the requested frame does not exist @throws CLICK_FAILED - the click could not be dispatched @throws TIMEOUT - the operation exceeded the server-side timeout @throws PAGE_NOT_ALIVE - the page has been closed

@see CloudBrowser.ClickWith for right-click, double-click,

press/release-only, or frame override

@example

_, err := browser.Click(ctx, browserscale.CSS("button.submit"))
if err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) ClickWith

func (c *CloudBrowser) ClickWith(ctx context.Context, target *Locator, opts ClickOpts) (*ElementResult, error)

ClickWith is the customizable variant of CloudBrowser.Click.

@inheritDoc CloudBrowser.Click @param opts - click customization; see ClickOpts

@example

// Right double-click on a context menu trigger.
_, err := browser.ClickWith(ctx, browserscale.CSS("li.menu"), browserscale.ClickOpts{
    Button:     "right",
    ClickCount: 2,
})

func (*CloudBrowser) Close

func (c *CloudBrowser) Close() error

Close is the defer-friendly alias for CloudBrowser.StopBrowser that uses a background context.

@inheritDoc CloudBrowser.StopBrowser

@example

browser, err := browserscale.RentBrowser(ctx, cfg)
if err != nil { log.Fatal(err) }
defer browser.Close()

func (*CloudBrowser) CountryCode

func (c *CloudBrowser) CountryCode() string

CountryCode returns the ISO-3166 country code the server allocated for this session (drives geo-IP and locale defaults).

func (*CloudBrowser) DragBy

func (c *CloudBrowser) DragBy(ctx context.Context, target *Locator, offsetX, offsetY float64) (*DragResult, error)

DragBy 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.

@param target - locator describing the element to pick up @param offsetX - horizontal distance to drag, in CSS pixels @param offsetY - vertical distance to drag, in CSS pixels

@returns *DragResult with the resolved frameId, backendNodeId and the

final cursor position (rootX, rootY) where the drop happened

@throws UNKNOWN_ERROR - the drag could not be performed

@example

_, err := browser.DragBy(ctx, browserscale.CSS(".slider .handle"), 120, 0)
if err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) DragTo

func (c *CloudBrowser) DragTo(ctx context.Context, target *Locator, absoluteX, absoluteY float64) (*DragResult, error)

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

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

@param target - locator describing the element to pick up @param absoluteX - horizontal drop coordinate in the root viewport @param absoluteY - vertical drop coordinate in the root viewport

@returns *DragResult with the resolved frameId, backendNodeId and the

final cursor position (rootX, rootY) where the drop happened

@throws UNKNOWN_ERROR - the drag could not be performed

@example

_, err := browser.DragTo(ctx, browserscale.CSS(".card"), 800, 400)
if err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) Evaluate

func (c *CloudBrowser) Evaluate(ctx context.Context, expression string) (*EvaluateResult, error)

Evaluate runs a JavaScript expression in the page's main frame.

The expression's return value is JSON-serialized server-side and parsed eagerly into EvaluateResult.Value. When the expression returns a DOM element the EvaluateResult.Value is left empty and the element metadata (BackendNodeId, IsVisible, Bounds) is populated instead — use Node(id) in subsequent calls to act on it.

@param expression - JavaScript expression evaluated in the main frame

@returns *EvaluateResult with either Value (for non-Element returns) or

BackendNodeId + IsVisible + Bounds (for Element returns)

@throws UNKNOWN_ERROR - the expression threw or could not be compiled

@example

res, err := browser.Evaluate(ctx, "document.title")
if err != nil {
    log.Fatal(err)
}
fmt.Println(res.Value)

func (*CloudBrowser) EvaluateInFrame

func (c *CloudBrowser) EvaluateInFrame(ctx context.Context, frameId, expression string) (*EvaluateResult, error)

EvaluateInFrame runs a JavaScript expression in the given frame.

Same semantics as CloudBrowser.Evaluate but targets a specific frame instead of the main frame. Useful for evaluating inside OOPIFs (out-of- process iframes) found via CloudBrowser.GetPages.

@inheritDoc CloudBrowser.Evaluate @param frameId - id of the frame to evaluate in; empty falls back to the main frame

@example

pages, _ := browser.GetPages(ctx)
iframeId := pages[0].FrameTree.Children[0].FrameId
_, _ = browser.EvaluateInFrame(ctx, iframeId, "location.href")

func (*CloudBrowser) Fill

func (c *CloudBrowser) Fill(ctx context.Context, target *Locator, text string) (*ElementResult, error)

Fill 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, use CloudBrowser.FillWith with ClearFirst: true.

At is not a valid target — Fill requires an actual element.

@param target - locator describing the input element @param text - text to type into the element

@returns *ElementResult with success, resolved frameId, backendNodeId

and the root-viewport (rootX, rootY) where the element was clicked

@throws INVALID_LOCATOR - target is empty or has multiple targets set @throws ELEMENT_NOT_FOUND - no element matched the locator @throws FRAME_NOT_FOUND - the requested frame does not exist @throws FILL_FAILED - the input could not be filled @throws TIMEOUT - the operation exceeded the server-side timeout @throws PAGE_NOT_ALIVE - the page has been closed

@see CloudBrowser.FillWith for clearing existing content or

overriding the target frame

@example

_, err := browser.Fill(ctx, browserscale.CSS("input[name=email]"), "user@example.com")

func (*CloudBrowser) FillWith

func (c *CloudBrowser) FillWith(ctx context.Context, target *Locator, text string, opts FillOpts) (*ElementResult, error)

FillWith is the customizable variant of CloudBrowser.Fill.

@inheritDoc CloudBrowser.Fill @param opts - fill customization; see FillOpts

@example

// Wipe the field first, then type fresh content.
_, err := browser.FillWith(ctx, browserscale.CSS("input[name=email]"), "user@example.com", browserscale.FillOpts{
    ClearFirst: true,
})

func (*CloudBrowser) Fingerprint

func (c *CloudBrowser) Fingerprint() string

Fingerprint returns the browser fingerprint id in use for this session.

func (*CloudBrowser) GetCookies

func (c *CloudBrowser) GetCookies(ctx context.Context) ([]CookieParam, error)

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

@returns []CookieParam, one per cookie in the context

@throws UNKNOWN_ERROR - the cookies could not be read

@example

cookies, err := browser.GetCookies(ctx)
if err != nil {
    log.Fatal(err)
}
for _, c := range cookies {
    fmt.Println(c.Name, "=", c.Value)
}

func (*CloudBrowser) GetDOM

func (c *CloudBrowser) GetDOM(ctx context.Context, frameId string, depth int32) (string, error)

GetDOM returns a JSON string 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 CloudBrowser.GetObservation instead.

@param frameId - id of the frame to dump; empty targets the main frame @param depth - tree depth: -1 for the full tree, 0 for root only, N for

root + N descendant levels

@returns JSON string in CDP DOM.Node shape

@throws UNKNOWN_ERROR - the DOM could not be retrieved

@example

tree, err := browser.GetDOM(ctx, "", -1)
if err != nil {
    log.Fatal(err)
}
fmt.Println(tree)

func (*CloudBrowser) GetDOMHash

func (c *CloudBrowser) GetDOMHash(ctx context.Context, frameId string) (string, error)

GetDOMHash 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 CloudBrowser.GetDOM only when the hash differs from your last snapshot.

@param frameId - id of the frame to hash; empty targets the main frame

@returns 16-char hex string (the first 8 bytes of sha256 of the DOM JSON)

@throws UNKNOWN_ERROR - the hash could not be computed

@example

hash, err := browser.GetDOMHash(ctx, "")
if err != nil {
    log.Fatal(err)
}
if hash != lastHash {
    // DOM changed → re-fetch
}

func (*CloudBrowser) GetObservation

func (c *CloudBrowser) GetObservation(ctx context.Context, maxElementsPerFrame, maxTextLength int32) (*ObservationResult, error)

GetObservation returns a compact, agent-friendly description of every interactable element currently visible on the page, together with a truncated view of the surrounding text.

The result is 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.

@param maxElementsPerFrame - hard cap on how many elements the server

returns per frame; 0 means use the server default

@param maxTextLength - hard cap on per-element text content length in

characters; 0 means use the server default

@returns *ObservationResult with both a human-readable Text rendering

and a Json payload of the structured observation

@throws UNKNOWN_ERROR - the observation could not be produced

@example

obs, err := browser.GetObservation(ctx, 200, 80)
if err != nil {
    log.Fatal(err)
}
fmt.Println(obs.Text)

func (*CloudBrowser) GetPages

func (c *CloudBrowser) GetPages(ctx context.Context) ([]*PageInfo, error)

GetPages 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).

@returns []*PageInfo for every page currently open in the context

@throws UNKNOWN_ERROR - the pages could not be enumerated

@example

pages, err := browser.GetPages(ctx)
if err != nil {
    log.Fatal(err)
}
for _, p := range pages {
    fmt.Println(p.Url, p.Title)
}

func (*CloudBrowser) GetSelection

func (c *CloudBrowser) GetSelection(ctx context.Context) (string, error)

GetSelection 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.

@returns the selected text, or "" when nothing is selected

@throws UNKNOWN_ERROR - the selection could not be read

@example

sel, err := browser.GetSelection(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Println("user selected:", sel)

func (*CloudBrowser) GetStorage

func (c *CloudBrowser) GetStorage(ctx context.Context, origin string) ([]StorageOriginEntry, error)

GetStorage 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.

@param origin - if non-empty, only this origin is returned

(e.g. "https://example.com"); empty string returns all origins

@returns []StorageOriginEntry, one per origin with localStorage data

@throws UNKNOWN_ERROR - the storage could not be read

@example

storage, err := browser.GetStorage(ctx, "")
if err != nil {
    log.Fatal(err)
}
for _, e := range storage {
    for _, item := range e.Items {
        fmt.Println(e.Origin, item.Key, "=", item.Value)
    }
}

func (*CloudBrowser) GrpcUrl

func (c *CloudBrowser) GrpcUrl() string

GrpcUrl returns the gRPC endpoint the session is connected to.

func (*CloudBrowser) HighlightNode

func (c *CloudBrowser) HighlightNode(ctx context.Context, backendNodeId int32, frameId string) error

HighlightNode 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.

@param backendNodeId - id of the node to highlight, or <= 0 to clear @param frameId - id of the frame the node lives in; empty targets the main frame

@throws UNKNOWN_ERROR - the highlight could not be applied

@example

if err := browser.HighlightNode(ctx, res.BackendNodeId, res.FrameId); err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) InsertText

func (c *CloudBrowser) InsertText(ctx context.Context, text string) error

InsertText 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 CloudBrowser.Click or CloudBrowser.Fill first if you need a specific element to be focused.

@param text - the text to insert at the caret

@throws UNKNOWN_ERROR - the text could not be inserted

@example

if err := browser.InsertText(ctx, "hello world"); err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) InspectAtPosition

func (c *CloudBrowser) InspectAtPosition(ctx context.Context, x, y float64) (*InspectResult, error)

InspectAtPosition 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.

@param x - viewport-relative x in CSS pixels @param y - viewport-relative y in CSS pixels

@returns *InspectResult with the resolved backendNodeId, frameId, tag

name, trimmed textContent, visibility and bounds

@throws UNKNOWN_ERROR - the hit-test failed

@example

res, err := browser.InspectAtPosition(ctx, 200, 300)
if err != nil {
    log.Fatal(err)
}
fmt.Println(res.TagName, res.TextContent)

func (*CloudBrowser) LoadHTML

func (c *CloudBrowser) LoadHTML(ctx context.Context, url, html string, headers []Header, statusCode int32) error

LoadHTML 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 CloudBrowser.Navigate to trigger the load.

@param url - the URL pattern that, when navigated to, returns the html @param html - the response body to serve @param headers - extra response headers (Content-Type is set automatically) @param statusCode - HTTP status code to serve; 0 means 200

@throws UNKNOWN_ERROR - the interceptor could not be installed

@example

_ = browser.LoadHTML(ctx, "https://example.com", "<h1>hi</h1>", nil, 0)
_, _ = browser.Navigate(ctx, "https://example.com", 0)

func (*CloudBrowser) ModifyRequest

func (c *CloudBrowser) ModifyRequest(ctx context.Context, urlPattern, body string, timeoutMs float64, mods []HeaderModification) (*InterceptedRequest, error)

ModifyRequest 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. Pass nil/empty mods to leave headers untouched and only override the body.

@param urlPattern - URL wildcard to wait for @param body - replacement request body; empty leaves the original body @param timeoutMs - per-call timeout in milliseconds; 0 uses the server default @param mods - HeaderModification entries; see HeaderModification for the fields

@returns *InterceptedRequest carrying the method/URL/headers/body that

were actually sent on the wire after modifications were applied

@throws UNKNOWN_ERROR - no matching request appeared within the timeout

@example

req, err := browser.ModifyRequest(ctx, "*/api/me", "", 5000, []browserscale.HeaderModification{
    {Action: browserscale.HeaderModificationAdd, Name: "X-Trace", Value: "abc123"},
    {Action: browserscale.HeaderModificationRemove, Name: "Cookie"},
})
if err != nil {
    log.Fatal(err)
}
fmt.Println("forwarded headers:", req.Headers)

func (*CloudBrowser) MoveTo

func (c *CloudBrowser) MoveTo(ctx context.Context, target *Locator) (*ElementResult, error)

MoveTo 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 random center area (or to the viewport coordinate when target is At).

@param target - locator describing where to move; At is also valid

@returns *ElementResult with the resolved frameId, backendNodeId,

post-scroll isVisible, element bounds and the root-viewport (rootX, rootY)
where the cursor ended up

@throws UNKNOWN_ERROR - the move could not be completed

@example

_, err := browser.MoveTo(ctx, browserscale.CSS("nav .menu"))
if err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) Navigate

func (c *CloudBrowser) Navigate(ctx context.Context, url string, timeoutMs float64) (*NavigateResult, error)

Navigate navigates the page to url.

Returns once the primary main-frame navigation commits (the response is received and a new document is selected), before DOMContentLoaded or load fire. Cross-origin redirects are followed.

@param url - destination URL @param timeoutMs - per-call timeout in milliseconds; 0 uses the server default

@returns *NavigateResult with the final resolved URL and the frameId of

the main frame after navigation

@throws UNKNOWN_ERROR - the navigation failed or timed out

@example

_, err := browser.Navigate(ctx, "https://example.com", 0)
if err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) PressKey

func (c *CloudBrowser) PressKey(ctx context.Context, key, code string, modifiers, location int32) error

PressKey fires a single key-down event.

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

@param key - DOM KeyboardEvent.key value (e.g. "Enter", "a", "ArrowLeft") @param code - DOM KeyboardEvent.code value (e.g. "Enter", "KeyA"); empty falls back to key @param modifiers - bit-flag combination: Alt=1, Ctrl=2, Meta=4, Shift=8 @param location - DOM KeyboardEvent.location: 0=standard, 1=left, 2=right, 3=numpad

@throws UNKNOWN_ERROR - the event could not be dispatched

@example

// Ctrl+A
_ = browser.PressKey(ctx, "a", "KeyA", 2, 0)
_ = browser.ReleaseKey(ctx, "a", "KeyA", 2, 0)

func (*CloudBrowser) ReadCanvas added in v1.1.0

func (c *CloudBrowser) ReadCanvas(ctx context.Context, target *Locator) (*ReadCanvasResult, error)

ReadCanvas 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.

@param target - locator for the <canvas>; CSS, JS or Node

([At] coordinates are not valid — a real element is required)

@returns *ReadCanvasResult with the base64 image in DataBase64, the canvas

Width/Height, resolved frameId/backendNodeId and the OriginClean flag

@throws INVALID_LOCATOR - target is empty, uses At(x,y), or has multiple targets @throws ELEMENT_NOT_FOUND - no element matched the locator @throws FRAME_NOT_FOUND - the requested frame does not exist @throws TIMEOUT - the operation exceeded the server-side timeout @throws PAGE_NOT_ALIVE - the page has been closed

@see CloudBrowser.ReadCanvasWith for format, quality, or a sub-rectangle

@example

res, err := browser.ReadCanvas(ctx, browserscale.CSS("#game canvas"))
if err != nil {
    log.Fatal(err)
}
img, _ := base64.StdEncoding.DecodeString(res.DataBase64)
os.WriteFile("canvas.png", img, 0o644)

func (*CloudBrowser) ReadCanvasWith added in v1.1.0

func (c *CloudBrowser) ReadCanvasWith(ctx context.Context, target *Locator, opts ReadCanvasOpts) (*ReadCanvasResult, error)

ReadCanvasWith is the customizable variant of CloudBrowser.ReadCanvas.

@inheritDoc CloudBrowser.ReadCanvas @param opts - format, quality, sub-rectangle and frame override; see ReadCanvasOpts

@example

// Read the left half of the canvas as JPEG at quality 80.
res, err := browser.ReadCanvasWith(ctx, browserscale.CSS("canvas"),
    browserscale.ReadCanvasOpts{Format: "jpeg", Quality: 80, SW: 150, SH: 300})

func (*CloudBrowser) ReleaseKey

func (c *CloudBrowser) ReleaseKey(ctx context.Context, key, code string, modifiers, location int32) error

ReleaseKey fires a single key-up event.

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

@inheritDoc CloudBrowser.PressKey

@example

_ = browser.PressKey(ctx, "Shift", "ShiftLeft", 0, 1)
_ = browser.ReleaseKey(ctx, "Shift", "ShiftLeft", 0, 1)

func (*CloudBrowser) Screenshot

func (c *CloudBrowser) Screenshot(ctx context.Context, format string, quality int32) (*ScreenshotResult, error)

Screenshot 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.

@param format - "png" (default), "jpeg", or "webp"; pass "" for PNG

@param quality - encode quality 0-100 for "jpeg"/"webp" (ignored for

"png"); pass 0 to use the server default (90)

@returns *ScreenshotResult with the base64 image in DataBase64 and the

physical pixel Width/Height

@throws UNKNOWN_ERROR - the screenshot could not be captured

@example

shot, err := browser.Screenshot(ctx, "png", 0)
if err != nil {
    log.Fatal(err)
}
img, _ := base64.StdEncoding.DecodeString(shot.DataBase64)
os.WriteFile("page.png", img, 0o644)

func (*CloudBrowser) ScrollTo

func (c *CloudBrowser) ScrollTo(ctx context.Context, target *Locator) (*ElementResult, error)

ScrollTo 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.

@param target - locator describing the element to bring into view;

[At] is rejected

@returns *ElementResult with the resolved frameId, backendNodeId,

post-scroll isVisible and the element's bounds after the scroll

@throws UNKNOWN_ERROR - the element could not be scrolled into view

@example

_, err := browser.ScrollTo(ctx, browserscale.CSS("#footer"))
if err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) SelectByIndex

func (c *CloudBrowser) SelectByIndex(ctx context.Context, target *Locator, index int32) (*SelectOptionResult, error)

SelectByIndex 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 CloudBrowser.SelectByIndexWith with SelectOpts.NoEvents).

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

@param target - locator describing the <select> element @param index - zero-based option index

@returns *SelectOptionResult with the resolved selectedIndex,

selectedValue and selectedText after the change

@throws INVALID_LOCATOR - target is empty or has multiple targets set @throws ELEMENT_NOT_FOUND - no element matched the locator @throws FRAME_NOT_FOUND - the requested frame does not exist @throws SELECT_FAILED - the option could not be selected

(out of range, or element is not a <select>)

@throws TIMEOUT - the operation exceeded the server-side timeout @throws PAGE_NOT_ALIVE - the page has been closed

@see CloudBrowser.SelectByIndexWith for suppressing events or

overriding the target frame

@see CloudBrowser.SelectByValue, CloudBrowser.SelectByText

for matching by `value` attribute or visible text instead

@example

_, err := browser.SelectByIndex(ctx, browserscale.CSS("select#country"), 2)

func (*CloudBrowser) SelectByIndexWith

func (c *CloudBrowser) SelectByIndexWith(ctx context.Context, target *Locator, index int32, opts SelectOpts) (*SelectOptionResult, error)

SelectByIndexWith is the customizable variant of CloudBrowser.SelectByIndex.

@inheritDoc CloudBrowser.SelectByIndex @param opts - select customization; see SelectOpts

@example

// Pick the option silently, no input/change events.
_, err := browser.SelectByIndexWith(ctx, browserscale.CSS("select#hidden"), 0, browserscale.SelectOpts{
    NoEvents: true,
})

func (*CloudBrowser) SelectByText

func (c *CloudBrowser) SelectByText(ctx context.Context, target *Locator, text string) (*SelectOptionResult, error)

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

@inheritDoc CloudBrowser.SelectByIndex @param text - the visible option text to match

@example

_, err := browser.SelectByText(ctx, browserscale.CSS("select#country"), "Germany")

func (*CloudBrowser) SelectByTextWith

func (c *CloudBrowser) SelectByTextWith(ctx context.Context, target *Locator, text string, opts SelectOpts) (*SelectOptionResult, error)

SelectByTextWith is the customizable variant of CloudBrowser.SelectByText.

@inheritDoc CloudBrowser.SelectByText @param opts - select customization; see SelectOpts

@example

_, err := browser.SelectByTextWith(ctx, browserscale.CSS("select#country"), "Germany", browserscale.SelectOpts{
    NoEvents: true,
})

func (*CloudBrowser) SelectByValue

func (c *CloudBrowser) SelectByValue(ctx context.Context, target *Locator, value string) (*SelectOptionResult, error)

SelectByValue picks the <option> whose `value` attribute matches the given string exactly.

@inheritDoc CloudBrowser.SelectByIndex @param value - the `value` attribute to match

@example

_, err := browser.SelectByValue(ctx, browserscale.CSS("select#country"), "DE")

func (*CloudBrowser) SelectByValueWith

func (c *CloudBrowser) SelectByValueWith(ctx context.Context, target *Locator, value string, opts SelectOpts) (*SelectOptionResult, error)

SelectByValueWith is the customizable variant of CloudBrowser.SelectByValue.

@inheritDoc CloudBrowser.SelectByValue @param opts - select customization; see SelectOpts

@example

_, err := browser.SelectByValueWith(ctx, browserscale.CSS("select#country"), "DE", browserscale.SelectOpts{
    NoEvents: true,
})

func (*CloudBrowser) SessionId

func (c *CloudBrowser) SessionId() string

SessionId returns the unique server-assigned id for this browser session.

func (*CloudBrowser) SetBlockList

func (c *CloudBrowser) SetBlockList(ctx context.Context, patterns []string) error

SetBlockList 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 a nil/empty slice to clear the blocklist and let everything through.

@param patterns - URL wildcards to block; nil or empty clears the list

@throws UNKNOWN_ERROR - the blocklist could not be applied

@example

_ = browser.SetBlockList(ctx, []string{
    "*.doubleclick.net/*",
    "*googletagmanager.com*",
})

func (*CloudBrowser) SetCookies

func (c *CloudBrowser) SetCookies(ctx context.Context, cookies []CookieParam) error

SetCookies writes the supplied cookies into the browser context.

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

@param cookies - cookies to write; empty slice is a no-op

@throws UNKNOWN_ERROR - the cookies could not be written

@example

secure := true
httpOnly := true
sameSite := "Lax"

_ = browser.SetCookies(ctx, []browserscale.CookieParam{
    {
        Name:     "auth",
        Value:    "tok",
        Domain:   "example.com",
        Path:     "/",
        Secure:   &secure,
        HTTPOnly: &httpOnly,
        SameSite: &sameSite,
    },
})

func (*CloudBrowser) SetProxy

func (c *CloudBrowser) SetProxy(ctx context.Context, proxyHost string, proxyPort int32, proxyUsername, proxyPassword string) error

SetProxy 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.

@param proxyHost - upstream proxy host; empty disables the proxy @param proxyPort - upstream proxy port; ignored when proxyHost is empty @param proxyUsername - proxy auth user (empty for unauthenticated proxies) @param proxyPassword - proxy auth password (empty for unauthenticated proxies)

@throws UNKNOWN_ERROR - the proxy could not be applied

@example

_ = browser.SetProxy(ctx, "proxy.example.com", 8080, "user", "pass")

func (*CloudBrowser) SetStaticPaths

func (c *CloudBrowser) SetStaticPaths(ctx context.Context, blobName string, patterns []string) error

SetStaticPaths 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 (blob storage, CDN, …) is configured server-side. Pass an empty patterns slice to disable caching for this session.

@param blobName - server-side identifier of the snapshot to serve from @param patterns - URL wildcards to redirect to the cache; nil/empty disables

@throws UNKNOWN_ERROR - the static paths could not be configured

@example

_ = browser.SetStaticPaths(ctx, "snap-2026-05", []string{"*.example.com/*"})

func (*CloudBrowser) SetStorage

func (c *CloudBrowser) SetStorage(ctx context.Context, storage []StorageOriginEntry) error

SetStorage 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.

@param storage - entries to write, grouped by origin

@throws UNKNOWN_ERROR - the storage could not be written

@example

_ = browser.SetStorage(ctx, []browserscale.StorageOriginEntry{
    {
        Origin: "https://example.com",
        Items: []browserscale.StorageItem{
            {Key: "token", Value: "abc123"},
            {Key: "theme", Value: "dark"},
        },
    },
})

func (*CloudBrowser) SolveCaptcha

func (c *CloudBrowser) SolveCaptcha(ctx context.Context, timeoutMs, retryAmount int32) (string, error)

SolveCaptcha 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.

@param timeoutMs - how long to wait for a captcha to appear, in

milliseconds; 0 uses the server default (60s)

@param retryAmount - number of retries on a failed solve before giving up

@returns empty string on success — the solution is applied server-side

@throws UNKNOWN_ERROR - no captcha appeared within timeoutMs, or the

detected captcha could not be solved within retryAmount attempts

@example

if _, err := browser.SolveCaptcha(ctx, 0, 2); err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) StopBrowser

func (c *CloudBrowser) StopBrowser(ctx context.Context) error

StopBrowser releases the session and closes the gRPC connection.

Calls the browserscale stop endpoint to release the rental, then closes the underlying gRPC connection. Safe to call multiple times — subsequent calls on a closed connection return an error from the second close.

@throws UNKNOWN_ERROR - the stop API or the gRPC close returned an error

@see CloudBrowser.Close - same operation with a background context

@example

defer browser.StopBrowser(context.Background())

func (*CloudBrowser) Timezone

func (c *CloudBrowser) Timezone() string

Timezone returns the IANA timezone the session was provisioned with (e.g. "Europe/Berlin").

func (*CloudBrowser) Wait

func (c *CloudBrowser) Wait(ctx context.Context, args ...WaitArg) (*WaitResult, error)

Wait blocks until any of the supplied locators matches.

Pass one or more [Locator]s (built with CSS, JS, …) plus optional wait-level arguments such as Timeout. When several locators are supplied, the first one to match wins; the others are abandoned.

Defaults applied automatically:

Node and At are not valid wait conditions — they only make sense as action targets — and produce an error at send time.

@param args - one or more [Locator]s plus optional wait-level options;

at least one [Locator] is required

@returns *WaitResult for the first matching condition (carries the

matched condition's index, frameId, backendNodeId and bounds)

@throws UNKNOWN_ERROR - the wait failed (no condition matched within the

timeout, the condition was invalid, or the call lacked any condition)

@example

// Wait for either a success banner or a JS condition, max 5s.
_, err := browser.Wait(ctx,
    browserscale.CSS(".success"),
    browserscale.JS("window.__ready === true"),
    browserscale.Timeout(5000),
)
if err != nil {
    log.Fatal(err)
}

func (*CloudBrowser) WaitForAnyRequest

func (c *CloudBrowser) WaitForAnyRequest(ctx context.Context, timeoutMs float64, patterns []RequestPattern) (int32, *InterceptedRequest, error)

WaitForAnyRequest 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 patterns[i].Abort is true the request is dropped with an empty 200 response instead of being sent to the network.

@param timeoutMs - per-call timeout in milliseconds; 0 uses the server default @param patterns - one or more URL patterns (with optional Abort flags)

@returns int32 index of the matched pattern, *InterceptedRequest with

the captured method/URL/headers/body, and an error

@throws UNKNOWN_ERROR - the wait timed out or no patterns were supplied

@example

idx, req, err := browser.WaitForAnyRequest(ctx, 5000, []browserscale.RequestPattern{
    {URL: "*/api/login"},
})
if err != nil {
    log.Fatal(err)
}
_ = idx
fmt.Println(req.Method, req.Url)

func (*CloudBrowser) WaitForAnyResponse

func (c *CloudBrowser) WaitForAnyResponse(ctx context.Context, timeoutMs float64, patterns []RequestPattern) (int32, *InterceptedResponse, error)

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

Same shape as CloudBrowser.WaitForAnyRequest but on the response phase. When patterns[i].Abort is true the page receives an empty 200 instead of the real response.

@inheritDoc CloudBrowser.WaitForAnyRequest

@returns int32 index of the matched pattern, *InterceptedResponse with

the captured status/headers/body, and an error

@example

idx, resp, err := browser.WaitForAnyResponse(ctx, 5000, []browserscale.RequestPattern{
    {URL: "*/api/login"},
})
if err != nil {
    log.Fatal(err)
}
_ = idx
fmt.Println(resp.StatusCode)

type CookieParam

type CookieParam struct {
	Name         string
	Value        string
	URL          *string
	Domain       string
	Path         string
	Secure       *bool
	HTTPOnly     *bool
	SameSite     *string
	Expires      *float64
	Priority     *string
	SourceScheme *string
	SourcePort   *int
	PartitionKey *CookiePartitionKey
}

CookieParam is one cookie returned by GetCookies / passed to SetCookies. Name, Value, Domain, and Path are the common required identity fields; optional attributes mirror the browser's CookieParam shape: URL, Secure, HTTPOnly, SameSite, Expires, Priority, SourceScheme, SourcePort, and PartitionKey.

type CookiePartitionKey

type CookiePartitionKey struct {
	TopLevelSite         string
	HasCrossSiteAncestor bool
}

CookiePartitionKey describes CHIPS partitioning metadata for partitioned cookies.

type DragResult

type DragResult struct {
	Success       bool
	FrameId       string
	BackendNodeId int32
	StartX        float64
	StartY        float64
	EndX          float64
	EndY          float64
}

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

type ElementResult

type ElementResult struct {
	Success       bool
	FrameId       string
	BackendNodeId int32
	IsVisible     bool
	Bounds        Rect
	RootX         float64
	RootY         float64
}

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.

type EvaluateResult

type EvaluateResult struct {
	Value         any
	BackendNodeId int32
	IsVisible     bool
	Bounds        Rect
}

EvaluateResult carries the outcome of a JS evaluate call.

If the expression returned a DOM element, BackendNodeId/IsVisible/Bounds are populated and Value is nil. Otherwise Value holds the parsed JSON value (string/number/bool/[]any/map[string]any/nil). On parse failure Value falls back to the raw server string so the caller is never empty- handed.

type FillOpts

type FillOpts struct {
	// InFrame overrides the locator's own frame.
	// Empty = use the locator's frame (or the main frame if none).
	// Pass a specific frameId, or [AllFrames], to search elsewhere.
	InFrame string

	// ClearFirst, when true, wipes the field's existing content with
	// Ctrl+A, Delete before typing. Default (false) appends to whatever
	// is already there.
	ClearFirst bool
}

FillOpts customizes a CloudBrowser.FillWith call. Zero/empty values mean "use the server default".

type FrameInfo

type FrameInfo struct {
	FrameId      string
	Url          string
	IsOOPIF      bool
	HasJSContext bool
	IsLoading    bool
	IsVisible    bool
	AbsoluteRect Rect
	RelativeRect Rect
	Children     []*FrameInfo
}

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

type Header struct {
	Name  string
	Value string
}

Header is a single HTTP header (name/value pair) on an intercepted request or response.

type HeaderModification

type HeaderModification struct {
	// Action selects what happens: HeaderModificationAdd inserts a new
	// header, HeaderModificationEdit replaces an existing header's value,
	// HeaderModificationRemove drops the header.
	Action HeaderModificationAction

	// Name is the header name the action applies to.
	Name string

	// Value is the header value for add/edit; ignored for remove.
	Value string

	// Before positions an "add" immediately before the named existing
	// header; otherwise the header is appended at the end. Ignored for
	// edit/remove.
	Before string

	// After positions an "add" immediately after the named existing
	// header. Mirror of Before; ignored for edit/remove.
	After string
}

HeaderModification is one entry passed to CloudBrowser.ModifyRequest. Build it as a plain struct literal.

type HeaderModificationAction

type HeaderModificationAction string

HeaderModificationAction is the verb of a HeaderModification. Matches the add/edit/remove action strings; use the HeaderModificationXxx constants.

const (
	HeaderModificationAdd    HeaderModificationAction = "add"
	HeaderModificationEdit   HeaderModificationAction = "edit"
	HeaderModificationRemove HeaderModificationAction = "remove"
)

type InspectResult

type InspectResult struct {
	BackendNodeId int32
	FrameId       string
	TagName       string
	TextContent   string
	IsVisible     bool
	Bounds        Rect
}

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

type InterceptedRequest

type InterceptedRequest struct {
	Method       string
	Url          string
	Headers      []Header
	Body         string
	ResourceType string
}

InterceptedRequest describes an outgoing request captured by CloudBrowser.WaitForAnyRequest.

type InterceptedResponse

type InterceptedResponse struct {
	Url        string
	StatusCode int32
	Headers    []Header
	Body       string
}

InterceptedResponse describes a network response captured by CloudBrowser.WaitForAnyResponse.

type Locator

type Locator struct {
	// contains filtered or unexported fields
}

Locator is the universal "what element / what condition" type. It is used both as a wait condition (passed to Wait) and as a target for element actions (passed to Click, Fill, etc.).

Not every field is meaningful in every context:

  • selector / jsExpression → both wait and actions
  • backendNodeId → actions only (Wait rejects it)
  • visible / steadyTime → wait only (silently ignored by actions)
  • x / y → actions only (Wait rejects it)
  • frameId → both, may be overridden by call-level browserscale.InFrame() / browserscale.InAllFrames() options

Use the CSS / JS / Node / At constructors instead of building this struct by hand.

func At

func At(x, y float64) *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 CloudBrowser.Wait returns an error at send time. Note that only Click and MoveTo accept At; Scroll, Drag, Fill and Select all require a real element.

@param x - viewport-relative x in CSS pixels @param y - viewport-relative y in CSS pixels

@returns *Locator usable only as an action target

@example

// Click at canvas-relative coordinates.
_, _ = browser.Click(ctx, browserscale.At(120, 240))

func CSS

func CSS(selector string) *Locator

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

When used in CloudBrowser.Wait, the returned Locator carries the SDK defaults DefaultVisible (true) and DefaultSteadyMs (500). Override per call with Locator.Visible / Locator.Steady (use `.Steady(0)` to disable the steady check).

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

@param selector - CSS selector matching the element

@returns *Locator usable as a wait condition or as an action target

@example

// As a wait condition.
_, _ = browser.Wait(ctx, browserscale.CSS("button.submit"))
// As an action target.
_, _ = browser.Click(ctx, browserscale.CSS("button.submit"))

func JS

func JS(expression string) *Locator

JS 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 Locator.Visible(false) / Locator.Steady(0) on the returned Locator to opt out.

@param expression - JavaScript expression evaluated in the target frame

@returns *Locator usable as a wait condition or as an action target

@example

_, _ = browser.Wait(ctx, browserscale.JS("window.__ready === true"))

func Node

func Node(backendNodeId int32) *Locator

Node targets an element by its DevTools backendNodeId.

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

@param backendNodeId - DevTools backendNodeId of the target element

@returns *Locator usable only as an action target

@example

res, _ := browser.Click(ctx, browserscale.CSS("button.open"))
_, _ = browser.Click(ctx, browserscale.Node(res.BackendNodeId))

func (*Locator) InAllFrames

func (l *Locator) InAllFrames() *Locator

InAllFrames 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.

@returns the same Locator for chaining

@example

_, _ = browser.Wait(ctx, browserscale.CSS("button.consent").InAllFrames())

func (*Locator) InFrame

func (l *Locator) InFrame(id string) *Locator

InFrame scopes this Locator to a specific frameId.

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

@param id - id of the frame to scope to

@returns the same Locator for chaining

@example

pages, _ := browser.GetPages(ctx)
iframeId := pages[0].FrameTree.Children[0].FrameId
_, _ = browser.Click(ctx, browserscale.CSS("button").InFrame(iframeId))

func (*Locator) Steady

func (l *Locator) Steady(ms float64) *Locator

Steady requires 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 the Locator is used as an action target.

@param ms - steady-state duration in milliseconds; 0 disables

@returns the same Locator for chaining

@example

_, _ = browser.Wait(ctx, browserscale.CSS(".banner").Steady(0))

func (*Locator) Visible

func (l *Locator) Visible(v bool) *Locator

Visible enforces 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 the Locator is used as an action target — actions never check visibility before dispatching.

@param v - true to require visibility, false to skip the check

@returns the same Locator for chaining

@example

_, _ = browser.Wait(ctx, browserscale.CSS("#hidden").Visible(false))
type NavigateResult struct {
	FrameId string
	Url     string
}

NavigateResult reports where a CloudBrowser.Navigate call ended up after redirects.

type ObservationResult

type ObservationResult struct {
	Text string
	Json string
}

ObservationResult is the compact page snapshot returned by CloudBrowser.GetObservation — the visible, interactive elements rendered as prompt-friendly text and as JSON.

type PageInfo

type PageInfo struct {
	PageId           string
	BrowserContextId string
	Url              string
	Title            string
	Viewport         Rect
	FrameTree        FrameInfo
}

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

type ReadCanvasOpts added in v1.1.0

type ReadCanvasOpts struct {
	// InFrame overrides the locator's own frame.
	// Empty = use the locator's frame (or the main frame if none).
	// Pass a specific frameId, or [AllFrames], to search elsewhere.
	InFrame string

	// Format is the output encoding.
	// "" or "png" (default), "jpeg", "webp", or "rgba" for the raw
	// unpremultiplied RGBA pixel buffer.
	Format string

	// Quality is the encode quality 0-100 for "jpeg"/"webp" (ignored otherwise).
	// 0 = server default (90).
	Quality int32

	// SX, SY, SW, SH is an optional sub-rectangle in canvas pixels (mirrors
	// getImageData(sx, sy, sw, sh)). The full canvas is read when SW/SH <= 0.
	SX, SY, SW, SH int32
}

ReadCanvasOpts customizes a CloudBrowser.ReadCanvasWith call. Zero/empty values mean "use the server default".

type ReadCanvasResult added in v1.1.0

type ReadCanvasResult struct {
	Success       bool
	FrameId       string
	BackendNodeId int32
	DataBase64    string
	Width         int32
	Height        int32
	OriginClean   bool
}

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 Opts.Format == "rgba". OriginClean reports whether the canvas was untainted (informational; the read succeeds either way).

type Rect

type Rect struct {
	X      float64
	Y      float64
	Width  float64
	Height float64
}

Rect describes a position and size in CSS pixels.

type RequestPattern

type RequestPattern struct {
	URL   string
	Abort bool
}

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.

type ScreenshotResult

type ScreenshotResult struct {
	DataBase64 string
	Width      int32
	Height     int32
}

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.

type SelectOptionResult

type SelectOptionResult struct {
	SelectedIndex int32
	SelectedValue string
	SelectedText  string
}

SelectOptionResult reports which <option> a SelectByXxx call ended up selecting.

type SelectOpts

type SelectOpts struct {
	// InFrame overrides the locator's own frame.
	// Empty = use the locator's frame (or the main frame if none).
	// Pass a specific frameId, or [AllFrames], to search elsewhere.
	InFrame string

	// NoEvents picks the option silently without firing input/change
	// events. Default (false) fires the standard events.
	NoEvents bool
}

SelectOpts customizes a SelectByXxxWith call. Zero/empty values mean "use the server default".

type StorageItem

type StorageItem struct {
	Key   string
	Value string
}

StorageItem is a single localStorage key/value pair.

type StorageOriginEntry

type StorageOriginEntry struct {
	Origin string
	Items  []StorageItem
}

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.

type WaitArg

type WaitArg interface {
	// contains filtered or unexported methods
}

WaitArg is the marker interface for everything Wait accepts: a Locator (treated as a condition) or a wait-level option such as Timeout / InFrame / InAllFrames.

func Timeout

func Timeout(ms float64) WaitArg

Timeout overrides the CloudBrowser.Wait timeout.

When omitted, DefaultWaitTimeoutMs (30s) is used. Pass once per Wait call as one of the variadic arguments.

@param ms - timeout in milliseconds

@returns a WaitArg suitable for passing to Wait

@example

_, _ = browser.Wait(ctx, browserscale.CSS("#done"), browserscale.Timeout(5000))

type WaitResult

type WaitResult struct {
	Index         int32
	FrameId       string
	BackendNodeId int32
	IsVisible     bool
	Bounds        Rect
}

WaitResult is the outcome of a CloudBrowser.Wait / [CloudBrowser.WaitForAny] call: which condition matched (Index, in argument order) and where the matched element lives.

Directories

Path Synopsis
examples
simple command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL