# Bulk Lighthouse Audit – Page Speed & SEO Scores (`luxe_tasks/bulk-website-auditor`) Actor

Bulk Lighthouse audits for up to 500 URLs, mobile and/or desktop: performance, SEO and accessibility scores plus lab metrics. Pay-per-event pricing.

- **URL**: https://apify.com/luxe_tasks/bulk-website-auditor.md
- **Developed by:** [Luxe Tasks](https://apify.com/luxe_tasks) (community)
- **Categories:** SEO tools, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $25.00 / 1,000 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

## Bulk Website Auditor – runs Google Lighthouse

Developer: Luxe_tasks.

This Actor runs **Google Lighthouse 13.6.0** (the open-source auditing tool, Apache-2.0) on a list of public web pages, on mobile, desktop or both, and returns
performance, accessibility, best-practices, SEO and agentic-browsing scores, the key lab metrics (LCP, CLS, TBT, FCP, Speed Index, TTI),
and the full JSON and HTML reports. Lighthouse runs **inside the Actor** in its own Google Chrome browser (from Apify's base image). No Google API key, no PageSpeed Insights API.

### Input

- `urls` (required): up to 500 public `http`/`https` URLs. `file:`, `data:`, other schemes, URLs with credentials, private/loopback addresses are refused (free).
- `formFactor`: `mobile` (default), `desktop`, or `both` (two audits of the same URL, two `audit` events).
- `saveHtmlReport` (default `true`): also store the HTML report (the JSON report is always stored).
- `timeoutSecs` (30–120, default 45): maximum wait for the page to load in one audit.
- Audits run one at a time (`maxConcurrency` is fixed at 1) so results are not distorted by parallel audits on the same CPU.
- Each run has a default timeout of 1 hour (3600 seconds); for very large lists you can raise the timeout in the run options.

#### Fixed measurement profile (not configurable)

Simulated throttling (Lantern), equal to Lighthouse 13.6.0's own presets:
| | network (simulated) | CPU slowdown | screen |
|---|---|---|---|
| mobile | RTT 150 ms, 1.6 Mbps (Slow 4G) | 4x | 412x823 @1.75 |
| desktop | RTT 40 ms, 10 Mbps | 1x | 1350x940 @1 |
Scores are lab measurements on cloud hardware; they vary from run to run and will not equal PageSpeed Insights or Chrome DevTools numbers exactly.

### Output

One dataset record per input URL: `index, url, status, ok, charged, chargedEvents, chargeEvent, httpStatus, error, robots, timingMs` and

- `formFactor`, `lighthouseVersion`, `scores` (0–100 per category), `metrics` (`lcpMs, fcpMs, tbtMs, cls, siMs, ttiMs`), `finalUrl` (query string redacted);
- `audits[]`: one entry per form factor with its own `status`, `error`, `scores`, `metrics`, `requests`, `blockedRequests` and `reports.json` / `reports.html`
  (`{key, link, bytes}` pointing to the key-value store). With `both`, the flat `scores`/`metrics` fields are omitted; use `audits[]`.

Key-value store: the full reports (`audit-...-json`, `audit-...-html`, about 0.3–1 MB each), `SUMMARY` (JSON) and `SUMMARY-csv`
(one line per URL and form factor), and `RUN_SUMMARY` (counts, charged events, stopped domains).

Statuses: `success`, `invalid_input`, `ssrf_blocked`, `robots_disallowed`, `http_error`, `http_403`, `http_429`, `http_5xx`, `blocked_or_captcha`,
`skipped_blocked` (domain stopped earlier in the run), `timeout`, `no_content`, `network_error`, `not_html`, `audit_error`, `save_failed`, `budget_reached`.

### Pricing

Pay per event: **$0.025 per audit ($25 per 1,000 audits) plus a start fee of $0.005 per GB of memory per run (4 GB default, so $0.02 per run).**

- `audit` ($0.025): one per **completed audit whose reports were saved** (so `both` = up to two per URL, $0.05).
- Start fee: Apify's `apify-actor-start` event at $0.005 per GB of memory, charged by Apify once at the start of every run. The Actor runs with
  4 GB (one CPU core, needed for stable scores), so the start fee is 4 x $0.005 = $0.02 per run. Example: 100 URLs on mobile, all audited: 100 x $0.025 + $0.02 = $2.52.
  Failed, blocked, robots-disallowed, stopped-domain, invalid and budget-skipped audits are free (the start fee still applies). The run reserves budget **before** each audit and
  stops at your maximum charge; a URL is not audited if the remaining budget cannot pay for all of its form factors.
  The synthetic `apify-default-dataset-item` event must stay unpriced (the actor refuses to run if it is priced).

### Politeness and safety (kit defaults; may only be tightened)

- robots.txt honoured by default (matched for `LuxeTasksBulkAuditor` and `*`); owner-only override with an attestation naming one registered domain, small runs only.
- At most one audit per second per site origin, one audit at a time; no proxy rotation; no retries beyond one for transient errors.
- **User-Agent.** Every request (page, subresources, redirects, robots.txt) is sent with the unmodified Chrome User-Agent followed by this Actor's token:
  `<Chrome UA> LuxeTasksBulkAuditor/0.1.0 (+https://apify.com/luxe_tasks/bulk-website-auditor; honours robots.txt)`
  (for example `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/139.0.0.0 Safari/537.36 LuxeTasksBulkAuditor/0.1.0 (...)`).
  `HeadlessChrome` is kept and the UA is never rotated or disguised. Because of this, the mobile audit does **not** use Lighthouse's emulated
  Android User-Agent; a site that serves different HTML by User-Agent may show its desktop markup in the mobile viewport.
  Mobile scores may therefore differ from PageSpeed Insights.
- A 403, 429, CAPTCHA or repeated 5xx stops the whole registered domain for the rest of the run (and after a restart); later items are skipped, not charged.
- Every request the browser makes, including iframes, workers and scripts, is checked: private, loopback, link-local and cloud-metadata addresses are refused,
  re-checked after DNS resolution and on every redirect hop. Blocked subresources are listed in `blockedRequests`; the page is still audited without them.
- No logins, cookies or session tokens: cookies are neither sent nor stored. The Actor takes no API keys.

### Acceptable use

> **Acceptable use.** This Actor audits **publicly accessible web pages that you choose**. By running it you confirm that:
>
> 1. You will submit only **public URLs** that you have the right or permission to test, and you will use the output lawfully.
> 2. You will **not** submit pages behind a login, paywall, or other access control, and the Actor does not accept passwords, cookies or session tokens.
> 3. You will **not** use it to get around CAPTCHAs, rate limits, IP blocks, or any other technical measure a site uses to restrict access, and not as a load or stress test.
> 4. You will comply with the target site's terms of use and robots.txt, and with all applicable law, including copyright and data-protection law (GDPR, CCPA and similar). You are the controller of any personal data in your results.
> 5. You will **not** process, store or distribute illegal content, or use the Actor to harass, stalk, or profile individuals.
> 6. You will keep request volumes reasonable. The Actor enforces per-site rate limits and identifies itself in its User-Agent.
>    Report problems through the Actor's **Issues** tab.

### Privacy

> "Results are written only to your run's default Apify storage and are not copied, viewed, or retained by the developer. If your results contain
> personal data, you are the controller and the developer acts only as your processor under Apify's Standard Actor Contract. Apify's retention settings apply."
>
> The full Lighthouse reports contain the URLs (including query strings) of the page and every resource it loaded, page text snippets used by audits,
> and response headers. The dataset and SUMMARY redact query strings; the stored reports do not. Do not audit URLs that carry secrets or personal data in the query string. Anyone you share the run's storage or report links with can see these URLs.

### Third-party software

Lighthouse is © Google LLC, licensed under the Apache License 2.0 (see `NOTICE` and `node_modules/lighthouse/LICENSE`). This Actor is not affiliated
with or endorsed by Google. puppeteer-core (Apache-2.0) is listed in `NOTICE`. Audits run in Google Chrome, which Apify's base image `apify/actor-node-puppeteer-chrome` installs and which is not included in this repository. Chrome is provided under Google's terms (https://www.google.com/chrome/terms/); see `NOTICE`.

### Runtime

Node 24 (`engines`, `.nvmrc`; Lighthouse itself needs Node >= 22.19, tests also pass on Node 22). Needs a Chrome or Chromium binary (`CHROME_PATH` or a standard path);
the Docker image (`apify/actor-node-puppeteer-chrome:24-25.16.0`, pinned by digest) bundles Google Chrome and sets `CHROME_PATH`. Tests: `npm test` (unit), `sh scripts/e2e-netns.sh` (real audits of a local fixture site inside a network namespace with no internet).
Bench: `sh bench/run-bench.sh` (results in `bench/results/`).

Contact: https://apify.com/Luxe_tasks · Issues tab on the Actor page.

# Actor input Schema

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

Pages to audit, e.g. https://example.com/. Pages you own or are authorized to test. URLs with a user name or password are refused; query strings are never logged. Don't include secrets or personal data in query strings.

## `formFactor` (type: `string`):

mobile (Slow 4G, 4x CPU slowdown), desktop (fast network, no CPU slowdown) or both (two audits, two charges).

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

Also save the full Lighthouse HTML report (the JSON report is always saved).

## `timeoutSecs` (type: `integer`):

Maximum wait for the page to load in one audit.

## `maxConcurrency` (type: `integer`):

Items processed in parallel (each site still gets at most 1 request per second).

## `ignoreRobotsTxt` (type: `boolean`):

Only for a site you own or control; requires the attestation below; small runs only.

## `overrideDomainStop` (type: `boolean`):

Only for a site you own or control; requires the attestation below.

## `robotsOwnershipAttestation` (type: `string`):

A statement that you own or control the one registered domain in the input, naming it.

## Actor input object example

```json
{
  "urls": [
    "https://example.com/"
  ],
  "formFactor": "mobile",
  "saveHtmlReport": true,
  "timeoutSecs": 45,
  "maxConcurrency": 1,
  "ignoreRobotsTxt": false,
  "overrideDomainStop": false,
  "robotsOwnershipAttestation": ""
}
```

# Actor output Schema

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

Default dataset, one record per input URL, shown in the 'Results (one row per URL)' view: index, url, formFactor, status (success or the failure reason), error, charged, chargedEvents, scores (0-100 per category), metrics (lcpMs, fcpMs, tbtMs, cls, siMs, ttiMs), finalUrl (query string redacted) and audits\[] (one entry per form factor with its own status, scores, metrics, requests, blockedRequests and reports.json / reports.html links). The 'Audits' view has one row per URL and form factor. Failed, blocked and skipped audits are not charged.

## `reports` (type: `string`):

Default key-value store, records starting with 'audit-': each saved audit has a JSON report named audit-<index>-<host>-<hash>-\<mobile|desktop>-json (application/json) and, when saveHtmlReport is on, an HTML report named audit-<index>-<host>-<hash>-\<mobile|desktop>-html (text/html). The stored reports keep full URLs, including query strings.

## `summaryCsv` (type: `string`):

SUMMARY-csv record: one line per URL and form factor with status, scores and metrics. The same data as JSON is in the SUMMARY record.

## `runSummary` (type: `string`):

RUN_SUMMARY record: totals by status, charged events, budget reached and stopped domains for the run.

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

// Run the Actor and wait for it to finish
const run = await client.actor("luxe_tasks/bulk-website-auditor").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 = { "urls": ["https://example.com/"] }

# Run the Actor and wait for it to finish
run = client.actor("luxe_tasks/bulk-website-auditor").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 '{
  "urls": [
    "https://example.com/"
  ]
}' |
apify call luxe_tasks/bulk-website-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,luxe_tasks/bulk-website-auditor"
        }
    }
}
```

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/VTkrjxbsPMjsn1Skx/builds/mKoumOVSLwCyeQg4x/openapi.json
