Control Flow Actions
Loop over elements and conditionally execute actions.
loop
Iterate over matching DOM elements or repeat actions a fixed number of times.
Loop Over Elements
Use each with a CSS selector to iterate over every matching element. The special $this prefix targets elements within the current iteration's scope.
{
"loop": {
"each": ".product-card",
"do": [
{ "getText": "$this h3", "options": { "push": "titles" } },
{ "getText": "$this .price", "options": { "push": "prices" } }
]
}
}
Repeat N Times
Use repeat to execute actions a fixed number of times.
{
"loop": {
"repeat": 5,
"do": [
{ "scrollY": 800 },
{ "wait": 1000 }
]
}
}
$this Selector
Inside a loop with each, $this refers to the current element. Append a child selector to target descendants:
| Usage | Meaning |
|---|---|
$this | The current element itself |
$this .child | .child element within current element |
$this > span | Direct span child of current element |
Nested Actions in Loop
You can use most actions inside a loop. The following are not supported inside loops: screenshot, waitForResponse, loop (no nested loops).
if
Conditionally execute actions based on element existence or data comparison.
Simple Condition (Selector Exists)
If the condition is a string, it checks if a matching element exists:
{
"if": {
"condition": ".error-message",
"then": [
{ "screenshot": "viewport" }
],
"else": [
{ "click": "#continue" }
]
}
}
Object Condition (Property Comparison)
Use an object condition for more complex checks:
{
"if": {
"condition": {
"selector": ".item-count",
"property": "textContent",
"operator": ">",
"value": "0"
},
"then": [
{ "click": "#checkout" }
]
}
}
Check Cached Data
{
"if": {
"condition": {
"cached": "totalItems",
"operator": ">=",
"value": "10"
},
"then": [
{ "click": "#bulk-discount" }
]
}
}
Comparison Operators
| Operator | Description |
|---|---|
== | Equal |
!= | Not equal |
> | Greater than |
< | Less than |
>= | Greater than or equal |
<= | Less than or equal |
contains | Text contains value |
notContains | Text does not contain value |
matches | Regex match |
empty | Value is empty/null |
notEmpty | Value is not empty/null |
Nested Control Flow
You can nest if and loop inside each other:
{
"loop": {
"each": ".product",
"do": [
{
"if": {
"condition": {
"selector": "$this .in-stock",
"operator": "notEmpty"
},
"then": [
{ "click": "$this .add-to-cart" }
]
}
}
]
}
}
Example: Scrape Paginated Data
{
"actions": [
{ "openNewTab": "https://example.com/products?page=1" },
{
"loop": {
"repeat": 5,
"do": [
{ "waitForElement": ".product-card" },
{
"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"] }
]
}
The outer loop uses repeat: 5 — not nested, since the inner loop is inside an if block within the outer loop's do array. Nested loop is supported inside if branches but direct loop inside loop is not allowed.
Example: Conditional Screenshot
{
"actions": [
{ "openNewTab": "https://example.com" },
{ "waitForEvent": "networkIdle" },
{
"if": {
"condition": ".cookie-banner",
"then": [
{ "click": ".cookie-banner .accept" },
{ "wait": 500 }
]
}
},
{ "screenshot": "viewport" }
]
}
Example: Check Element Text
{
"actions": [
{ "openNewTab": "https://example.com/status" },
{ "waitForElement": "#status" },
{
"if": {
"condition": {
"selector": "#status",
"property": "textContent",
"operator": "contains",
"value": "Success"
},
"then": [
{ "click": "#proceed" }
],
"else": [
{ "screenshot": "viewport" }
]
}
}
]
}