# Lighthouse Batch Audits & Regression Alerts (`peerless_columbine/lighthouse-batch-audit-monitor`) Actor

Audit public pages with Lighthouse13.5.0, export raw and HTML reports, and compare persistent category baselines.

- **URL**: https://apify.com/peerless_columbine/lighthouse-batch-audit-monitor.md
- **Developed by:** [tingyou333 zhuang](https://apify.com/peerless_columbine) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$20.00 / 1,000 delivered lighthouse audits

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Lighthouse Batch Audits & Regression Alerts

Audit public website pages with official Lighthouse13.5.0. Collect performance,
accessibility, best practices and SEO scores in one ordered batch, download the
full raw JSON and optional HTML reports, and compare category scores with a
persistent baseline. No Google API key is required.

These are **lab measurements**, not real-user/CrUX field data. Scores vary with
network and CPU conditions. A regression alert identifies a measured drop; it
does not establish that a deployment caused it. Inspect partial results and error rows before using scores in a release decision.

### Quick start

Run with the prefilled public page or supply your own URL:

```json
{"url":"https://example.com"}
```

For a batch, use `urls` and set a delivery limit:

```json
{"urls":["https://example.com","https://example.org"],"maxItems":2}
```

Each run creates Dataset rows and a KVS `OUTPUT` collection. Download the raw
Lighthouse JSON through each successful row; enable `saveHtmlReport` for HTML.
Use a retained `monitoringStoreId` across scheduled runs to compare baselines.
The first observation creates a baseline; a later measured decline at or above
your threshold can trigger one JSON webhook.

### Input

Supply `url` for one page or `urls` for a batch. If both are present, `url` comes
first. At least one URL is required; the form prefill is not a runtime default.
Missing `https://` is added. Public HTTP(S) ports1–65535 are supported; port0,
credentials and private/internal destinations are rejected. Unicode hostnames
and URL components are encoded before navigation; `originalRequestedUrl` keeps
the original string. Normalized duplicates are skipped without another audit,
row or event charge. This is page auditing, not site crawling or authentication.

| Input | Default | Behavior |
|---|---|---|
| `url` | none | One public URL |
| `device` | `mobile` | `mobile` or `desktop` |
| `categories` | all4 | `performance`, `accessibility`, `best-practices`, `seo`; empty also means all |
| `saveHtmlReport` | `false` | Save the full official HTML report |
| `throttling` | `simulated` | Official `simulate`; `devtools` uses DevTools throttling; `none` uses `provided` |
| `monitoringMode` | `false` | Compare and persist category baselines |
| `regressionThreshold` | `5` | A category drop of at least1–50 points triggers a breach |
| `alertWebhookUrl` | none | Public HTTP(S) JSON endpoint; one POST on a breach |
| `urls` | none | Up to500 total input entries, processed sequentially |
| `monitoringStoreId` | this run's default KVS | Existing KVS ID to reuse across runs |
| `maxItems` | `10` | At most500 successful delivered audits; errors and duplicates are free |
| `maxCostUsd` | `1` | Audit **event-charge** ceiling,0–1000; excludes platform compute/storage/API usage |
| `auditTimeoutSecs` | `180` | Per browser attempt,30–180 seconds; initial attempt plus at most3 source retries |
| `maxRunSecs` | `3300` | Source batch deadline,30–3300 seconds |
| `maxTotalNetworkMiB` | `128` | Aggregate browser proxy traffic,16–512MiB |

Use4096MB memory for Chrome plus Node. Each attempt gets a fresh browser profile. All attempts
for one URL share up to16MiB of two-way proxy traffic,128 connections,1024 intercepted page requests,
64KiB per POST body, and8MiB per raw JSON/HTML report.
Metadata rows are limited to256KiB; the batch stops before its `OUTPUT` collection
could exceed8MiB. Input URLs are limited to8192 characters. Background browser traffic
also passes through the public proxy's connection/byte limits. Fixed DoH traffic
is separate, tightly bounded resolver overhead. The batch deadline stops source
work; SDK reads/writes have separate30-second request timeouts. These limits can
produce free diagnostic rows on heavy pages.

### Results and reports

Dataset rows contain final `url`, normalized `requestedUrl`, `device`,
`timestamp`, four0–100 scores, `coreWebVitals`, `failingAudits`, `opportunities`,
`lighthouseVersion`, `fetchTime`, `userAgent`, `throttling`, `categories`, optional
`htmlReportKey`/`htmlReportUrl`, optional `regression`, and `error`/`errorMessage`.
Scores of unrequested categories are null. Removed/unavailable measurements,
including TTI when absent from Lighthouse13, stay null rather than zero. Metric
ratings use the documented mobile reference thresholds, including desktop runs.
Top10 failing audits use0–100 scores below50; opportunities are sorted by
potential time savings. They are recommendations, not guaranteed improvements.
`userAgent` prefers Lighthouse's observed network UA; `hostUserAgent` and
`browserVersion` retain the host browser provenance. A missing network UA uses
an explicitly labelled top-level fallback and a partial warning.

Every successful row points to its complete raw LHR and SHA256. When requested,
the official HTML report is saved under `lighthouse-report-…`. `OUTPUT` in the
default KVS contains the same full rows in `results`, an execution/input binding
and summary. `SUMMARY` lists delivered/errors/partials, skipped duplicates,
unprocessed URLs, caps and unknown delivery/billing states. Dataset exports are
available through normal Apify JSON/CSV/Excel/XML exports; choose fields when exporting nested reports to a table. For HTML downloaded from Apify, use its original
attachment response when validating stored bytes; the ordinary HTML endpoint
may add a security prefix.

New audits also save a report-hash-bound main-document sidecar extracted from
Lighthouse's actual CDP `DevtoolsLog` by its official `MainResource` computation.
It records the observed document URL, request/frame ID, HTTP status, MIME type
and completion state. Category subsets such as SEO-only do not need to run the
performance category or produce the optional `network-requests` display audit.
Per-host two-way relay counters expose background browser transfer as well as
page traffic; neither is subtracted from the original16MiB audit allowance.

Source failures, HTTP errors, missing requested scores, launch failures and
timeouts create explicit **free error rows**, not empty successful audits.
Lighthouse warnings or blocked subresources remain visible as `partial` and
`network.denied`; a scored page is not a claim that every resource loaded.
Inspect the summary even if the platform run itself says SUCCEEDED.

Known temporary main-document HTTP failures (408/429/500/502/503/504) may get
**up to3 retries after the initial attempt: at most4 source attempts**. A retry
requires a complete CDP document response bound to that attempt's actual LHR,
known resource counters, and remaining event allowance, URL budgets and batch time.
There is a1-second cooldown between attempts. The primary product's private
retry interpretation and strategy are unknown; this is our disclosed policy,
not a claim to reproduce its hidden behavior exactly.

The original per-URL traffic/connection/request caps and aggregate network and
batch deadlines do not reset on retry. A shorter native run timeout can stop the
process before all4 attempts;180 seconds is a per-attempt ceiling, not funding
for four complete180-second audits. Unsafe URLs,403/404, missing/invalid reports,
unverified document outcomes, resource limits, launch errors and interruptions
are not retried. Only the final outcome is delivered and, if applicable, charged
once; exhausted source failures are still one free diagnostic row.

`SOURCE-<id>` retains each attempt's intent, error, observed main-document status,
network counters and report hash, plus the cumulative totals and actual attempt
count. Prior failed raw LHR bodies are not all retained; their hash-only references
are not downloadable raw-report proofs. The final available full raw report remains
in the existing report artifact. `RUN_PLAN.maximumSourceAttemptsPerUrl` is a ceiling;
`SOURCE.actualAttempts` counts known started calls; it is null for an unfinished
intent, whose attempt count and source-start outcome remain unknown. An unfinished or
uncertain SOURCE journal stops replay. Uncertain delivery, append, charge, baseline
or webhook operations never enter the source retry loop.

### Persistent monitoring and webhooks

The first successful delivery stores a baseline without an alert. For later
runs, set `monitoringStoreId` to the same existing KVS ID (shown in the row's
`regression` object). Empty uses the run's default store and does not promise
cross-run persistence. The resource picker requests read/write access only to
the selected store. URL, device, categories, throttling, Lighthouse version and
browser version separate baseline namespaces. A changed browser version starts
a new baseline instead of creating a spurious regression.

Successful, acknowledged deliveries update the baseline. A threshold breach
produces one JSON POST containing delivery ID, URL, device, timestamp and the
score deltas. There is no redirect or automatic retry. Non2xx statuses are
reported; uncertain POST outcomes are retained as unknown. Webhook URLs are not
included in receipts, and response text is not stored. A timeout never triggers
another charge or audit. Use a JSON endpoint or an automation adapter; this
payload is not a promise of a vendor-specific Slack/Discord message format.

Do not overlap scheduled runs that share a baseline. KVS has no atomic
compare-and-swap: a visible concurrent change stops the update, but simultaneous
writers cannot be guaranteed a global order. Keep the baseline store retained
for as long as monitoring is required.

### Billing and interrupted runs

The price is **$0.02 per successful delivered audit ($20 per1,000)**, including
platform usage. One `lighthouse-audit` event is charged for each delivered audit
(including disclosed partials). There is no startup fee. Diagnostic error rows are free. Successful Dataset rows are charged once as audits; duplicates and skipped next URLs are not delivered
again. The pricing guard rejects separately priced synthetic events. The pinned
SDK may internally track unpriced dataset writes; that is not a confirmed fee. Reports,
the dataset row and `OUTPUT` are stored before the explicit charge. The current
Store price and platform terms determine charges. Platform charges can exist in FREE or
owner QA runs and are not inferred as zero.

An uncertain append, charge, baseline write or webhook stops automatic replay.
Durable `SOURCE-…`, `DELIVERY-…`, `MONITOR-…`, `WEBHOOK-…` and `POSTPROCESS-…`
journals preserve acknowledged positives. Missing journals beneath a positive
summary stop new source work. Retain the original run storage and reconcile
unknowns before resuming; starting another run is not a reconciliation method.

### Local use

Install the hash-locked Python requirements and frozen Node dependencies with
scripts disabled; install Playwright Chromium. Node24 and Python3.12 are used by
the Dockerfile. Then run:

```sh
python -m src.local --input examples/single.json --storage ./local-output
```

This executes a real local audit and does not initialize Apify or charge an
event. Use a shared `--monitoring-directory` for local cross-run monitoring.
`LIGHTHOUSE_NODE` and `LIGHTHOUSE_CHROME_PATH` are operator-only executable paths,
not URL/input options. Local outputs do not prove cloud acceptance or competitor
parity. Offline tests reuse an already collected official LHR and clearly
identified controlled fixtures; they do not run the primary Actor.

Lighthouse is Apache-2.0 licensed; its license is included in `assets`.

# Actor input Schema

## `url` (type: `string`):

Required for single-URL mode. In batch mode supply urls instead, or combine url then urls. Prefill is not a runtime default.

## `device` (type: `string`):

Emulate mobile or desktop device

## `categories` (type: `array`):

Which Lighthouse categories to run (leave empty for all). Note: PWA category was removed in Lighthouse 12.

## `saveHtmlReport` (type: `boolean`):

Store the full HTML report in key-value store

## `throttling` (type: `string`):

Network throttling preset

## `monitoringMode` (type: `boolean`):

Persist scores per URL and alert only when a category drops vs prior run. Essential for scheduled site-health tracking.

## `regressionThreshold` (type: `integer`):

Trigger alert if any Lighthouse category drops by at least this many points vs prior run (0-100 scale)

## `alertWebhookUrl` (type: `string`):

Optional public HTTP(S) endpoint for one JSON regression POST; no redirect/retry. No source content or response text is retained in webhook receipts.

## `urls` (type: `array`):

Ordered public URL batch, up to500 input entries in total. Normalized duplicate URLs are skipped free. Supply url or urls.

## `monitoringStoreId` (type: `string`):

Existing KVS ID for cross-run baselines. Empty uses this run’s default store; reuse its ID on later runs. Do not overlap runs sharing a baseline.

## `maxItems` (type: `integer`):

Successful delivered audits, including disclosed partials. Free errors and skipped duplicates do not consume this quantity.

## `maxCostUsd` (type: `number`):

PPE event-charge ceiling; not a ceiling on platform compute, transfer, storage or API costs. In FREE/owner QA mode this does not price platform usage.

## `auditTimeoutSecs` (type: `integer`):

Seconds per isolated browser attempt, including startup and cleanup. Known transient source HTTP failures may retry up to3 times within the same URL resource caps and batch deadline; delivery and charge are never retried.

## `maxRunSecs` (type: `integer`):

Stops starting/continuing source audits. SDK storage operations have separate30-second request timeouts.

## `maxTotalNetworkMiB` (type: `integer`):

Aggregate observed browser proxy traffic, both directions. Each audit has a16MiB proxy ceiling; reaching a ceiling returns a free error.

## Actor input object example

```json
{
  "url": "https://example.com",
  "device": "mobile",
  "categories": [
    "performance",
    "accessibility",
    "best-practices",
    "seo"
  ],
  "saveHtmlReport": false,
  "throttling": "simulated",
  "monitoringMode": false,
  "regressionThreshold": 5,
  "maxItems": 10,
  "maxCostUsd": 1,
  "auditTimeoutSecs": 180,
  "maxRunSecs": 3300,
  "maxTotalNetworkMiB": 128
}
```

# Actor output Schema

## `auditResults` (type: `string`):

No description

## `rawOutput` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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 = {
    "url": "https://example.com"
};

// Run the Actor and wait for it to finish
const run = await client.actor("peerless_columbine/lighthouse-batch-audit-monitor").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 = { "url": "https://example.com" }

# Run the Actor and wait for it to finish
run = client.actor("peerless_columbine/lighthouse-batch-audit-monitor").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 '{
  "url": "https://example.com"
}' |
apify call peerless_columbine/lighthouse-batch-audit-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,peerless_columbine/lighthouse-batch-audit-monitor"
        }
    }
}
```

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/rWHeewuzD7C66bD7R/builds/ZYK4NBb1br7choDqA/openapi.json
