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 —
$keyto reuse a saved element (see Data Caching)
Navigation
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:
| Format | Example | Extracts |
|---|---|---|
"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 Cookie
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.
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.
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.
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
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"
}
}
]
Only same-origin iframes are supported. Cross-origin iframes (e.g. embedded third-party widgets) cannot be accessed due to browser security restrictions.