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.