Skip to main content

Basic Usage

All examples use POST https://api.browserworker.app/v1/ with your Bearer token.

curl -X POST https://api.browserworker.app/v1/ \
-H "Authorization: Bearer <YOUR_BEARER_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "actions": [ ... ] }'

Quick Example

[
{ "open": "https://example.com/login" }, // Navigate to the login page
{ "fill": ["#email", "user@example.com"] }, // Type email into the email field
{ "fill": ["#password", "secret123"] }, // Type password into the password field
{ "click": "#submit-btn" }, // Click the submit button
{ "waitForElement": ".dashboard" }, // Wait for the dashboard to appear
{ "screenshot": "fullpage" } // Capture a full-page screenshot
]

Selectors: Most actions accept a selector to target elements:

  • CSS selector#submit-btn, .my-class, input[name=email]
  • XPath — starts with / or //, e.g. //button[text()='Submit']
  • Cached element$key to reuse a saved element (see Data Caching)

Open a Page

Navigate the current tab to a URL.

[
{ "open": "https://example.com" }
]

Open in New Tab

Open a URL in a new tab. Closes all other tabs by default.

[
{ "openNewTab": "https://example.com" }
]

Keep other tabs open:

[
{
"openNewTab": "https://example.com",
"options": { "closeOthers": false }
}
]

Set Viewport Size

Resize the browser window to exact dimensions [width, height].

[
{ "setViewportSize": [1920, 1080] }
]

Interaction

Interact with page elements using various actions. All interactions automatically scroll the element into view first.

Click

Click on an element.

<button id="submit-btn">Submit</button>
[
{ "click": "#submit-btn" }
]

Fill (Type Text)

Type text into an input field character by character. Clears existing text by default.

<input type="email" name="email" />
<input type="password" name="password" />
[
{ "fill": ["input[name='email']", "hello@example.com"] },
{ "fill": ["input[name='password']", "secret123"] }
]

Keep existing text (append):

[
{
"fill": ["input[name='email']", " extra text"],
"options": { "clear": false }
}
]

Press Key

Press a keyboard key on an element. Supports single keys and combinations with +.

<input type="text" class="search-input" />
[
{ "press": [".search-input", "Enter"] },
{ "press": [".search-input", "Shift+A"] }
]

Press a key without targeting a specific element (sends to active element or body):

[
{ "press": [null, "Escape"] }
]

Hover

Trigger hover on an element. Dispatches mouseenter → mouseover → mousemove events.

<div class="dropdown-menu">
<span class="menu-trigger">Menu</span>
<ul class="menu-items" style="display:none">...</ul>
</div>
[
{ "hover": ".menu-trigger" }
]

Check / Uncheck

Toggle a checkbox or radio button.

<input type="checkbox" id="agree" />
<label for="agree">I agree to terms</label>
[
{ "check": "#agree" }
]
[
{ "uncheck": "#agree" }
]

Select Option

Select an option from a <select> dropdown.

<select id="country">
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="jp">Japan</option>
</select>

By value (default):

[
{ "selectOption": ["#country", "us"] }
]

By visible text:

[
{ "selectOption": ["#country", "United States"], "options": { "by": "text" } }
]

By index (0-based):

[
{ "selectOption": ["#country", 0], "options": { "by": "index" } } // selected "United States"
]

Data Extraction

Get Text

Get the text content of an element.

<h1 class="title">Welcome to My Site</h1>
[
{ "getText": ".title" }
]

Result: "Welcome to My Site"

Save to cache for later use:

[
{ "getText": ".title", "options": { "save": "sectionTitle" } },
{ "fill": ["input[name='search']", "${sectionTitle}"] }
]

Get HTML

Get the inner HTML of an element.

<div class="content"><p>Hello <strong>World</strong></p></div>
[
{ "getHTML": ".content" }
]

Result: "<p>Hello <strong>World</strong></p>"

Get Attribute

Get an attribute value from an element. Format: [selector, attributeName].

<a class="link" href="https://example.com">Example</a>
[
{ "getAttribute": [".link", "href"] }
]

Result: "https://example.com"

Get Value

Get the current value of a form element (<input>, <textarea>, <select>).

<input name="email" value="hello@example.com" />
[
{ "getValue": "input[name='email']" }
]

Result: "hello@example.com"

Extract All

Extract structured data from multiple matching elements. Great for scraping lists, tables, or search results.

[
{ "extractAll": ["<containerSelector>", { <fieldMap> }] }
]

Example HTML:

<div class="product-list">
<div class="item">
<h3>Laptop</h3>
<span class="price">$999</span>
<a href="/laptop">Details</a>
</div>
<div class="item">
<h3>Phone</h3>
<span class="price">$699</span>
<a href="/phone">Details</a>
</div>
</div>

Action:

[
{
"extractAll": [
".product-list .item",
{
"name": "h3",
"price": "span.price",
"link": ["a", "href"]
}
]
}
]

Result:

[
{ "name": "Laptop", "price": "$999", "link": "/laptop" },
{ "name": "Phone", "price": "$699", "link": "/phone" }
]

Field map formats:

FormatExampleExtracts
"selector""h3"Text content of child element
["selector", "attr"]["a", "href"]Attribute of child element

With options:

[
{
"extractAll": [
".item",
{
"name": "h3",
"link": ["a", "href"]
}
],
"options": {
"limit": 5 // get only first 5 matches
}
}
}
]

Finding Elements

All find actions support "save" in options to cache the found element. Reuse it later with $key (dollar sign followed by the key name) in any action that accepts a selector.

Find

Find a target element if it exists or wait for an element to appear in the DOM, then cache it for later use.

<div class="dynamic-element">Loaded!</div>
[
{ "find": ".dynamic-element" }
]

Save to element cache and reuse later by referencing the key with $:

[
{
"find": ".dynamic-element",
"options": {
"save": "myEl"
}
},
{ "click": "$myEl" }
]

Find by Text

Find the innermost element that contains specific text. Case-insensitive.

<div class="buttons">
<button>Cancel</button>
<button>Submit Order</button>
</div>

Exact match (default):

[
{ "findByText": "Submit Order" }
]

Contains match:

[
{ "findByText": "Submit", "options": { "selector": "button", "exact": false } }
]

Save and reuse:

[
{ "findByText": "Submit Order", "options": { "selector": "button", "save": "submitBtn" } },
{ "click": "$submitBtn" }
]

Find by Attribute

Find an element by its attribute value. Format: [attributeName, value]. Contains match by default.

<button data-testid="login-btn">Login</button>
[
{ "findByAttribute": ["data-testid", "login-btn"] }
]

Contains match:

[
{ "findByAttribute": ["data-testid", "login"], "options": { "exact": false } }
]

Save and reuse:

[
{ "findByAttribute": ["data-testid", "login-btn"], "options": { "save": "loginBtn" } },
{ "click": "$loginBtn" }
]

Waiting

Wait (Delay)

Pause for a number of milliseconds.

[
{ "wait": 2000 } // wait for 2 seconds
]

Wait for Element

Wait for an element to appear in the DOM before continuing.

<!-- appears after page loads data -->
<div class="loaded-content">...</div>
[
{ "waitForElement": ".loaded-content" }
]

With custom timeout:

[
{ "waitForElement": ".loaded-content", "options": { "timeout": 10000 } }
]

Wait for Page Load

Wait for all page resources to finish loading (images, scripts, stylesheets, etc.).

[
{ "waitForEvent": "load" }
]

Wait for Network Idle

Wait until there are no active network requests.

[
{ "waitForEvent": "networkidle" }
]

Scrolling

Scroll Vertically

Scroll the page up or down by pixels. Positive = down, negative = up.

[
{ "scrollY": 500 }
{ "scrollY": -200 }
{ "scrollY": 99999 } // scroll to bottom
]

Scroll Horizontally

Scroll the page left or right by pixels. Positive = right, negative = left.

[
{ "scrollX": 300 }
{ "scrollX": -200 }
]

Screenshot

Capture a screenshot. The image is uploaded to cloud storage and a URL is returned.

Capture the visible viewport:

[
{ "screenshot": "viewport" }
]

Capture the full page (auto-scrolls and stitches segments):

[
{ "screenshot": "fullpage" }
]

Capture a specific element:

<canvas id="chart"></canvas>
[
{ "screenshot": "#chart" }
]

File Upload

Upload files to a <input type="file"> element on the page. The extension fetches the file by URL and injects it into the file input using the DataTransfer API.

<form>
<input type="file" id="file-upload" />
<button type="submit">Upload</button>
</form>

Method 1: Direct URL (simplest)

Pass a public URL directly.

[
{ "uploadFile": ["#file-upload", "https://example.com/files/photo.jpg"] },
{ "click": "button[type='submit']" }
]

Multiple files from URLs:

[
{
"uploadFile": [
"#file-upload",
[
"https://example.com/files/photo1.jpg",
"https://example.com/files/photo2.jpg"
]
]
}
]

Method 2: Multipart Form-Data

Send your own local file as binary. Reference uploaded files with $file:N (0-indexed).

curl -X POST https://api.browserworker.app/v1/ \
-H "Authorization: Bearer <YOUR_BEARER_TOKEN>" \
-F 'payload={"actions":[{"uploadFile":["#file-upload","$file:0"]},{"click":"button[type=submit]"}]}' \
-F 'files=@/path/to/my-file.pdf'

Multiple files:

curl -X POST https://api.browserworker.app/v1/ \
-H "Authorization: Bearer <YOUR_BEARER_TOKEN>" \
-F 'payload={"actions":[{"uploadFile":["#file-upload",["$file:0","$file:1"]]}]}' \
-F 'files=@/path/to/photo1.jpg' \
-F 'files=@/path/to/photo2.jpg'

Cookies

Get Cookies

Get all cookies for a URL.

[
{ "getCookies": "https://example.com" }
]

Get all cookies in the browser:

[
{ "getCookies": true }
]

Filter by name and domain:

[
{
"getCookies": "https://example.com",
"options": { "name": "session_id", "domain": ".example.com" }
}
]

Set Cookies

Set one or more cookies.

[
{
"setCookies": [
{ "url": "https://example.com", "name": "token", "value": "abc123" },
{ "url": "https://example.com", "name": "lang", "value": "en" }
]
}
]

With full cookie properties:

[
{
"setCookies": [
{
"url": "https://example.com",
"name": "theme",
"value": "dark",
"path": "/",
"secure": true,
"httpOnly": false,
"expirationDate": 1735689600
}
]
}
]

Delete a specific cookie by name and URL.

[
{ "deleteCookie": { "url": "https://example.com", "name": "token" } }
]

Clear All Cookies

Remove all cookies from the browser.

[
{ "clearCookies": true }
]

Clear cookies for a specific URL only:

[
{ "clearCookies": "https://example.com" }
]

Advanced

Evaluate JavaScript

Run JavaScript code in the page and return the result.

[
{ "evaluate": "return document.title" }
]

Result: "My Page Title"

More complex expression:

[
{ "evaluate": "return document.querySelectorAll('.item').length" }
]

Result: 5

Multi-statement with return value:

[
{ "evaluate": "window.scrollTo(0, document.body.scrollHeight); return document.body.scrollHeight;" }
]

Human-like Click

Click using a realistic mouse movement with bezier curve path. Uses the Chrome Debugger protocol for native input events.

[
{ "clickHuman": "#button" }
]

Fast mode (cursor starts closer to the target):

[
{
"clickHuman": "#button",
"options": {
"mode": "fast"
}
}
]

Human-like Hover

Hover using a realistic bezier curve mouse path without clicking.

[
{ "hoverHuman": ".element" }
]

Fast mode (cursor starts closer to the target):

[
{
"hoverHuman": ".element",
"options": {
"mode": "fast"
}
}
]

Auto Bypass Cloudflare (Verify you are human)

Detect and click through Cloudflare's challenge page using native browser events.

tip

The Cloudflare Turnstile widget takes a moment to load after the page opens. Add a wait or waitForEvent: "networkidle" before this action to give it time to appear.

[
{"wait": 3000}, // wait for Cloudflare widget to load
{ "byPassCloudflare": true }
]

Intercept fetch/XHR Network Responses

Intercept fetch/XHR responses matching a URL pattern. Non-blocking — starts monitoring immediately, and the captured response is returned at the end of the task (after all subsequent actions complete). Matches URL by substring or regex.

[
{ "waitForResponse": "/api/data" }
]

Filter by HTTP method:

[
{ "waitForResponse": "/api/data", "options": { "method": "POST" } }
]

With custom timeout:

[
{ "waitForResponse": "graphql", "options": { "method": "POST", "timeout": 30000 } }
]

Typical usage — set up the listener first, then trigger the action that causes the network request. The captured response is returned at the end of the task:

[
{ "open": "https://example.com/products" },
{ "waitForResponse": "/api/search" }, // start monitoring for search API call...
{ "fill": ["#search-input", "laptop"] },
{ "click": "#search-button" },
{ "waitForElement": ".search-results" }
]

This starts monitoring for /api/search requests, then types a search query and clicks the search button. When the page fires the AJAX call, the response body (e.g. JSON product list) is captured and returned as the task result.


Control Flow

Loop (Repeat N Times)

Run a set of actions multiple times.

[
{
"loop": {
"repeat": 3,
"do": [
{ "click": ".load-more" },
{ "wait": 1000 }
]
}
}
]

Loop (Over Elements)

Loop over each element matching a selector. Use $this to reference the current element. "$this" alone targets the element itself, "$this .child" targets a child within it.

<div class="product-card">
<span class="title">Laptop</span>
<span class="price">$999</span>
</div>
<div class="product-card">
<span class="title">Phone</span>
<span class="price">$699</span>
</div>
[
{
"loop": {
"each": ".product-card",
"do": [
{ "getText": "$this .title", "options": { "push": "titles" } },
{ "getText": "$this .price", "options": { "push": "prices" } },
{ "click": "$this .add-to-cart" }
]
}
}
]

Result:

{
"titles": ["Laptop", "Phone"],
"prices": ["$999", "$699"]
}

With options:

[
{
"loop": {
"each": ".item",
"do": [
{ "getText": "$this", "options": { "push": "items" } }
]
},
"options": {
"maxIterations": 50,
"iframe": "#content-frame"
}
}
]
  • maxIterations — (number, default: 100) Safety limit for maximum iterations.
Unsupported inside loop

screenshot, waitForResponse, and nested loop actions are not supported inside a loop.

If / Else

Run actions conditionally. The condition can be a selector (checks if element exists), a comparison object, or a cached data check.

Selector condition — runs then if element exists:

[
{
"if": {
"condition": "#cookie-banner",
"then": [{ "click": "#accept-cookies" }]
}
}
]

Comparison condition — compare an element's property:

<span class="price">$0.00</span>
[
{
"if": {
"condition": {
"selector": ".price",
"property": "textContent",
"operator": "contains",
"value": "$0.00"
},
"then": [{ "click": ".add-to-cart" }],
"else": [{ "click": ".wishlist" }]
}
}
]

Cached data condition — compare against saved data from dataCache:

[
{
"if": {
"condition": {
"cached": "status",
"operator": "==",
"value": "active"
},
"then": [{ "click": "#activate" }]
}
}
]

Supported operators: ==, !=, >, <, >=, <=, contains, notContains, matches, empty, notEmpty

Both then and else branches support nested loop and if actions.

Unsupported inside if

waitForResponse is not supported inside if branches.


Data Caching

Save extracted data and reuse it in later actions. There are two types of caches:

  • Element cache — stores DOM elements in memory. Reference with $key.
  • Data cache — stores serializable values (strings, arrays). Interpolate with ${key}.

Element Cache

Use find with "save" to store a DOM element, then reference it with $key in any action that accepts a selector.

<div class="modal">
<input class="modal-input" type="text" />
<button class="modal-submit">Submit</button>
</div>
[
{ "find": ".modal-input", "options": { "save": "inputEl" } },
{ "find": ".modal-submit", "options": { "save": "submitBtn" } },
{ "fill": ["$inputEl", "hello@example.com"] },
{ "click": "$submitBtn" }
]

The element is found once and reused — useful when a selector is complex or the element may move in the DOM between actions.

Save and reuse data

Use save to store a value in the data cache, then reference it later with ${key}.

<h1>BrowserWorker</h1>
<input name="search" />
[
{ "getText": "h1", "options": { "save": "title" } },
{ "fill": ["input[name='search']", "${title}"] }
]

This extracts "BrowserWorker" from <h1>, saves it as title, then types it into the search input.

Push to Array

Use push to append values one by one into an array in the data cache.

<ul>
<li class="price">$999</li>
<li class="price">$699</li>
<li class="price">$299</li>
</ul>
[
{ "getText": ".price:nth-child(1)", "options": { "push": "prices" } },
{ "getText": ".price:nth-child(2)", "options": { "push": "prices" } },
{ "getText": ".price:nth-child(3)", "options": { "push": "prices" } }
{ "getSavedData": "prices" } // return result to client
]

Result:

{
"prices": ["$999", "$699", "$299"]
}

Get Saved Data

Retrieve cached data values by keys:

[
{ "getSavedData": ["title", "price", "url"] }
]

Returns an object mapping each key to its cached value (or null if not found).

Clear Saved Data

Clear all saved data from the data cache manually. This is useful when you want to reset accumulated data mid-task.

[
{ "clearSavedData": true }
]

The data cache is cleared automatically in these situations:

  • New task starts — cleared at the beginning of every task
  • Chrome restarts — cleared on every browser launch
Data persists across session tasks

When tasks share the same session.id, the data cache is not cleared between them. This lets you save data in one API call and read it in the next. See Sessions for details.

Escaping

  • ${key} — Interpolates a value from the data cache.
  • $key — References a cached DOM element in the element cache.
  • $$ — Literal dollar sign escape ($$5.00$5.00).

Working with Iframes

Target elements inside an iframe by adding "iframe" to options.

<iframe id="my-iframe">
<!-- inside iframe -->
<button id="btn">Click me</button>
</iframe>
[
{
"click": "#btn",
"options": {
"iframe": "#my-iframe"
}
}
]
Cross-origin iframes

Only same-origin iframes are supported. Cross-origin iframes (e.g. embedded third-party widgets) cannot be accessed due to browser security restrictions.