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" }
]
}
}
]