# Playwright Browser Automation Runner | No-Code JSON Workflows (`produkdigitalali/playwright-browser-automation-runner`) Actor

Run deterministic Playwright browser workflows from JSON: navigate, click, fill, extract, assert, wait, and screenshot without deploying a Playwright script.

- **URL**: https://apify.com/produkdigitalali/playwright-browser-automation-runner.md
- **Developed by:** [ProdukDigitalAli](https://apify.com/produkdigitalali) (community)
- **Categories:**
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 browser actions

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Playwright Browser Automation Runner | No-Code JSON Workflows

Run deterministic Playwright browser workflows from JSON — navigate, click, fill, press, select, extract, assert, wait, and screenshot — without writing or deploying a Playwright script.

This Actor is designed for **public, login-free browser automation**, smoke tests, QA checks, extraction from JavaScript-rendered pages, and backend/n8n/Make workflows that need predictable structured output and bounded cost.

### Quick start

#### 1. Verify a page and extract its heading

```json
{
  "startUrl": "https://example.com",
  "actions": [
    {
      "type": "waitForSelector",
      "locator": { "by": "css", "value": "h1" }
    },
    {
      "type": "extractText",
      "locator": { "by": "css", "value": "h1" }
    },
    {
      "type": "textContains",
      "locator": { "by": "css", "value": "h1" },
      "expected": "Example Domain"
    }
  ]
}
```

#### 2. Fill and submit a public form with semantic Playwright locators

```json
{
  "startUrl": "https://www.selenium.dev/selenium/web/web-form.html",
  "actions": [
    {
      "type": "fill",
      "locator": { "by": "label", "value": "Text input" },
      "value": "Playwright on Apify"
    },
    {
      "type": "click",
      "locator": { "by": "role", "value": "button", "name": "Submit", "exact": true }
    },
    {
      "type": "urlContains",
      "expected": "submitted-form.html"
    },
    {
      "type": "screenshot",
      "name": "submitted-form",
      "fullPage": true
    }
  ]
}
```

#### 3. Extract text from a JavaScript-rendered page

```json
{
  "startUrl": "https://quotes.toscrape.com/js/",
  "actions": [
    {
      "type": "waitForSelector",
      "locator": { "by": "css", "value": ".quote" }
    },
    {
      "type": "extractText",
      "locator": { "by": "css", "value": ".quote .text", "index": 0 }
    }
  ]
}
```

### When to use this Actor

Use it when you want to:

- run scheduled browser smoke tests without maintaining a Playwright codebase;
- automate a deterministic public form or UI flow;
- extract a small number of fields from JavaScript-rendered pages;
- verify text or URL assertions in CI, n8n, Make, or API workflows;
- capture screenshots as run artifacts;
- generate browser workflows programmatically from a stable JSON contract.

For large recursive crawling jobs, use a crawler-oriented Actor instead. For natural-language, non-deterministic browser goals, use an AI browser agent rather than this deterministic runner.

### Supported actions

| Action | Required fields | What it does |
| --- | --- | --- |
| `goto` | `url` | Navigate to another public HTTP(S) URL. |
| `waitForSelector` | `locator` | Wait for an element state: `visible`, `hidden`, `attached`, or `detached`. |
| `click` | `locator` | Click one element. |
| `fill` | `locator`, `value` | Fill an input-like element. |
| `press` | `locator`, `key` | Press a key/chord on an element. |
| `selectOption` | `locator`, `value` or `values` | Select one or more option values. |
| `extractText` | `locator` | Return element text, capped at 200,000 characters. |
| `extractAttribute` | `locator`, `attribute` | Return an HTML attribute value. |
| `textContains` | `locator`, `expected` | Pass only if element text contains the expected substring. |
| `urlContains` | `expected` | Pass only if the current URL contains the expected substring. |
| `screenshot` | none | Save a PNG screenshot to the default key-value store. |
| `wait` | `durationMs` | Wait for a bounded duration. |

`timeoutMs` can override the default timeout for an individual action where relevant. `goto` also supports `waitUntil` values `load`, `domcontentloaded`, `networkidle`, and `commit`.

### Playwright locator model

The Actor supports both CSS selectors and Playwright's user-facing semantic locators:

```json
{ "by": "css", "value": "article h2" }
{ "by": "text", "value": "Continue", "exact": true }
{ "by": "role", "value": "button", "name": "Submit", "exact": true }
{ "by": "label", "value": "Email" }
{ "by": "placeholder", "value": "Search" }
{ "by": "testId", "value": "checkout-button" }
```

If a locator intentionally matches several elements, use zero-based `index`:

```json
{
  "by": "css",
  "value": ".product-card .title",
  "index": 2
}
```

Playwright strictness is preserved. Prefer semantic locators or stable attributes over brittle positional CSS selectors.

### Output

The default dataset contains **one row for each executed workflow step**, followed by a final summary row. This makes it easy for downstream tools to inspect individual actions without unpacking a large nested audit object.

Example step row:

```json
{
  "rowType": "step",
  "actorVersion": "0.1.0",
  "timestamp": "2026-09-06T06:00:00.000Z",
  "inputHash": "...",
  "stepIndex": 1,
  "action": "extractText",
  "passed": true,
  "currentUrl": "https://example.com/",
  "value": "Example Domain",
  "durationMs": 12
}
```

Example summary row:

```json
{
  "rowType": "summary",
  "actorVersion": "0.1.0",
  "timestamp": "2026-09-06T06:00:01.000Z",
  "inputHash": "...",
  "passed": true,
  "currentUrl": "https://example.com/",
  "durationMs": 821,
  "totalSteps": 3,
  "passedSteps": 3,
  "failedSteps": 0,
  "chargedActions": 3,
  "chargeLimitReached": false,
  "blockedEgress": 0,
  "transferredBytes": 45123,
  "warnings": []
}
```

Screenshots are stored as PNG records in the default key-value store and referenced from the corresponding step row.

### Stop or continue after an error

`stopOnError` defaults to `true`.

- `true`: stop after the first failed action and write the final summary.
- `false`: record the failed step and continue with later actions when possible. This is useful when you intentionally place a `screenshot` action after an assertion so you still capture evidence if the assertion fails.

Failed workflow actions are not charged as successful browser actions.

### Pricing

The launch billing model is **Pay Per Event** with the primary event:

- `browser-action`: **$0.004 per successfully completed action** ($4 per 1,000 successful actions).

Examples for the browser-action portion of a run:

| Successful actions | Browser-action charge |
| ---: | ---: |
| 5 | $0.020 |
| 20 | $0.080 |
| 100 | $0.400 |

The Actor checks the run's maximum charge limit before starting each billable action and stops before the next action when no charge budget remains. Apify may separately show its standard Actor-start event according to the Actor's pricing configuration.

### Safety and limits

The MVP intentionally keeps a narrow security boundary:

- public `http://` and `https://` destinations only;
- ports 80 and 443 only;
- localhost, loopback, RFC1918/private, link-local, metadata, multicast, documentation/reserved ranges, and non-public IPv6 ranges are blocked;
- hostname resolution is checked before connecting, and mixed public/private DNS answers are rejected;
- redirects and browser subresources remain behind the same validating local egress proxy;
- URL-embedded credentials are rejected;
- service workers are blocked, downloads are disabled, uncontrolled popups are closed, QUIC is disabled, and non-proxied WebRTC UDP is restricted;
- maximum 100 actions per run;
- maximum 60 seconds per action and 180 seconds per workflow;
- maximum 300 proxied browser connections and 64 MiB aggregate browser transfer per run;
- screenshot artifacts are capped at 8 MiB;
- extracted strings are capped at 200,000 characters per action;
- arbitrary user-supplied JavaScript/Node.js execution is **not** supported.

This Actor is **not** positioned as a CAPTCHA bypass or access-control evasion tool. A `403`, `429`, CAPTCHA, robots/terms restriction, or target-specific anti-bot rule should be treated as a target limitation.

#### Do not put secrets into workflow action values

The MVP is optimized for public/login-free flows. Actor input is persisted by the Apify run, so do not place passwords, API keys, session cookies, authorization headers, or other secrets inside `actions[].value` or URLs. Query parameters with common secret-like names are redacted from result URLs, but that does not turn the input itself into a secret store.

### Proxy configuration

`proxyConfiguration` uses Apify's standard proxy input editor. You can disable proxies, use Apify Proxy if available to your account, or provide supported custom HTTP(S) proxies.

The upstream proxy does **not** replace the Actor's destination safety checks: public-destination validation remains in front of browser egress.

### Reliability notes

Browser automation depends on target HTML, network availability, and site behavior. Use stable selectors, explicit assertions, and sensible timeouts. A page that changes its markup can cause a selector to fail even when the Actor runtime is healthy.

### API, n8n, and Make

The Actor input is deliberately JSON-first. Typical integrations create an input object with `startUrl` and `actions`, run the Actor, then consume default dataset rows and optional screenshot artifacts.

For n8n or Make, map upstream fields into the JSON `actions` array and branch on the final dataset row where `rowType` is `summary` and `passed` is `true` or `false`.

For direct API usage, use the standard Apify Actor run endpoints generated on the Actor's **API** tab after publication.

### Browser automation family

| Tool | Choose it when... |
| --- | --- |
| Puppeteer Browser Automation Runner | You want simple Chrome/Puppeteer-style deterministic workflows. |
| Selenium Browser Automation Runner | You think in WebDriver locators or maintain Selenium-based automation knowledge. |
| Chrome DevTools Automation Runner | You need low-level CDP commands, rendering/emulation controls, or diagnostics. |
| **Playwright Browser Automation Runner** | You want modern deterministic workflows with Playwright semantic locators and step-by-step dataset rows. |
| Browser Network & HAR Capture Inspector | You need XHR/fetch/API diagnostics and HAR export. |
| Browser Use AI Task Runner | You want natural-language goals and can accept non-deterministic AI-driven actions. |
| Playwright MCP Browser Automation Server | You need a live browser controlled one tool call at a time by an MCP agent. |

The last three are separate product shapes in the developer-automation portfolio; use the product that matches the job rather than choosing by framework name alone.

### Support

When reporting an issue, include:

- the smallest public URL that reproduces it;
- the minimal action list;
- the failed `stepIndex`, `action`, `errorCode`, and `errorMessage`;
- whether a proxy was enabled;
- the Actor run ID.

Never post passwords, API keys, cookies, proxy credentials, or other secrets in an issue.

# Actor input Schema

## `startUrl` (type: `string`):

Public HTTP(S) URL opened before the first action. Private, loopback, link-local, metadata, reserved, and credential-bearing URLs are rejected.

## `actions` (type: `array`):

Ordered JSON actions. Supported types: goto, waitForSelector, click, fill, press, selectOption, extractText, extractAttribute, textContains, urlContains, screenshot, wait. Action-specific validation is enforced by the Actor before the browser starts.

## `stopOnError` (type: `boolean`):

Stop the workflow after the first failed action. Disable to continue and record later step results.

## `actionTimeoutMs` (type: `integer`):

Default per-action timeout in milliseconds.

## `navigationTimeoutMs` (type: `integer`):

Default timeout for startUrl and goto navigation in milliseconds.

## `maxRunSeconds` (type: `integer`):

Hard workflow budget in seconds, excluding a small amount of Actor startup/shutdown overhead.

## `proxyConfiguration` (type: `object`):

Optional Apify Proxy or custom HTTP(S) proxy. The Actor keeps its own public-destination checks in front of the upstream proxy.

## Actor input object example

```json
{
  "startUrl": "https://example.com",
  "actions": [
    {
      "type": "waitForSelector",
      "locator": {
        "by": "css",
        "value": "h1"
      }
    },
    {
      "type": "extractText",
      "locator": {
        "by": "css",
        "value": "h1"
      }
    },
    {
      "type": "textContains",
      "locator": {
        "by": "css",
        "value": "h1"
      },
      "expected": "Example Domain"
    }
  ],
  "stopOnError": true,
  "actionTimeoutMs": 15000,
  "navigationTimeoutMs": 30000,
  "maxRunSeconds": 120,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One dataset row per executed workflow step, followed by a final summary row.

## `screenshots` (type: `string`):

PNG screenshots created by explicit screenshot actions.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrl": "https://example.com",
    "actions": [
        {
            "type": "waitForSelector",
            "locator": {
                "by": "css",
                "value": "h1"
            }
        },
        {
            "type": "extractText",
            "locator": {
                "by": "css",
                "value": "h1"
            }
        },
        {
            "type": "textContains",
            "locator": {
                "by": "css",
                "value": "h1"
            },
            "expected": "Example Domain"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("produkdigitalali/playwright-browser-automation-runner").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrl": "https://example.com",
    "actions": [
        {
            "type": "waitForSelector",
            "locator": {
                "by": "css",
                "value": "h1",
            },
        },
        {
            "type": "extractText",
            "locator": {
                "by": "css",
                "value": "h1",
            },
        },
        {
            "type": "textContains",
            "locator": {
                "by": "css",
                "value": "h1",
            },
            "expected": "Example Domain",
        },
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("produkdigitalali/playwright-browser-automation-runner").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrl": "https://example.com",
  "actions": [
    {
      "type": "waitForSelector",
      "locator": {
        "by": "css",
        "value": "h1"
      }
    },
    {
      "type": "extractText",
      "locator": {
        "by": "css",
        "value": "h1"
      }
    },
    {
      "type": "textContains",
      "locator": {
        "by": "css",
        "value": "h1"
      },
      "expected": "Example Domain"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call produkdigitalali/playwright-browser-automation-runner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,produkdigitalali/playwright-browser-automation-runner"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/oU6fGV4WIFOJYLAmX/builds/SWCV5wnZUth7eIXlt/openapi.json
