Skip to main content

Control Flow

Repeat actions in a loop or run them only when a condition is met.

loop

Iterate over matching DOM elements or repeat a set of actions a fixed number of times.

each — loop over elements

Queries the page for all matching elements, then runs do once per element. The iteration count is fixed at the start — elements added to the DOM mid-loop are not picked up.

$this is available to target children of the current element.

[
{
"loop": {
"each": ".product-card",
"do": [
{ "getText": "$this h3", "options": { "push": "titles" } },
{ "getText": "$this .price", "options": { "push": "prices" } }
]
}
}
]

repeat — run N times

Runs do a fixed number of times. No DOM query is involved. $this is not available.

[
{
"loop": {
"repeat": 5,
"do": [
{ "scrollY": 800 },
{ "wait": 1000 }
]
}
}
]

Both modes are capped at 100 iterations by default. Override with "options": { "maxIterations": 200 }.

$this prefix

Inside an each loop, $this refers to the current element. Use it to target children within each iteration instead of searching the whole page:

UsageMeaning
$thisThe current element itself
$this .child.child inside the current element
$this > spanDirect span child of the current element

Selectors without $this always query the full page — you can mix scoped and global selectors in the same loop.

Options

OptionTypeDefaultDescription
maxIterationsnumber100Maximum number of iterations for both each and repeat

Unsupported inside loop

screenshot, waitForResponse, and loop cannot be used directly inside a loop's do array.

To use a loop inside another loop, wrap the inner loop in an if block.


if

Run actions only when a condition is true. Optionally run different actions when it is false using else.

Value: { condition, then, else? }

Check whether an element exists on the page:

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

Compare an element's text or property:

[
{
"if": {
"condition": {
"selector": ".item-count",
"property": "textContent",
"operator": ">",
"value": "0"
},
"then": [
{ "click": "#checkout" }
]
}
}
]

Check a cached value:

[
{
"if": {
"condition": {
"cached": "totalItems",
"operator": ">=",
"value": "10"
},
"then": [
{ "click": "#bulk-discount" }
]
}
}
]

Condition types

TypeExampleDescription
String".selector"Checks whether a matching element exists
Object with selector{ selector, property, operator, value }Reads a property from an element and compares it
Object with cached{ cached, operator, value }Compares a cached value

Operators

OperatorDescription
==Equal
!=Not equal
>Greater than
<Less than
>=Greater than or equal
<=Less than or equal
containsText contains value
notContainsText does not contain value
matchesRegex match
emptyValue is empty or null
notEmptyValue is not empty or null

Nesting

loop and if can be nested inside each other. waitForResponse cannot be used inside if branches.


Example: Scrape Paginated Data

Loop through 5 pages, extract product data from each, and click "next" if available.

Note: to use a loop (the each) inside another loop (the repeat), the inner loop is wrapped in an if block.

[
{ "openNewTab": "https://example.com/products?page=1" },
{
"loop": {
"repeat": 5,
"do": [
{ "waitForElement": ".product-card" },
{
"if": {
"condition": ".product-card",
"then": [
{
"loop": {
"each": ".product-card",
"do": [
{ "getText": "$this .name", "options": { "push": "names" } },
{ "getText": "$this .price", "options": { "push": "prices" } }
]
}
}
]
}
},
{
"if": {
"condition": "a.next-page",
"then": [
{ "click": "a.next-page" },
{ "wait": 2000 }
]
}
}
]
}
},
{ "getSavedData": ["names", "prices"] }
]
[
{ "openNewTab": "https://example.com" },
{ "waitForEvent": "networkidle" },
{
"if": {
"condition": ".cookie-banner",
"then": [
{ "click": ".cookie-banner .accept" },
{ "wait": 500 }
]
}
},
{ "screenshot": "viewport" }
]

Example: Check Element Text

[
{ "openNewTab": "https://example.com/status" },
{ "waitForElement": "#status" },
{
"if": {
"condition": {
"selector": "#status",
"property": "textContent",
"operator": "contains",
"value": "Success"
},
"then": [
{ "click": "#proceed" }
],
"else": [
{ "screenshot": "viewport" }
]
}
}
]