# Core Web Vitals Bulk Auditor (Lighthouse) (`madpai/web-vitals-auditor`) Actor

Run open-source Lighthouse on many URLs: lab Core Web Vitals (LCP, CLS, TBT), performance, accessibility, best-practices and SEO scores, and top fixes, mobile or desktop. No API key needed.

- **URL**: https://apify.com/madpai/web-vitals-auditor.md
- **Developed by:** [William Schultz](https://apify.com/madpai) (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

$20.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

## Core Web Vitals Bulk Auditor (Lighthouse)

Runs the open-source Lighthouse engine on a list of public URLs and returns compact, LLM-ready JSON: lab Core Web Vitals (LCP, CLS, and TBT as a stand-in for INP), performance, accessibility, best-practices and SEO scores, the top fixes ranked by estimated savings, and an optional Markdown summary. No API key needed.

This is an independent tool built on Lighthouse; it is not affiliated with or endorsed by Google. Results are lab measurements from an emulated device, not real-user field data.

### Input

- `urls` (required): pages to audit.
- `strategy`: `mobile` (default), `desktop` or `both` (two audits per URL).
- `runsPerUrl` (1–5, default 1): run Lighthouse several times per URL and device and report the median run, plus `metricsMedian`, `metricsRange` and `scoresRange` (min/max). Each completed run is charged as one `lighthouse-audit`.
- `categories`: subset of performance, accessibility, best-practices, seo.
- `includeMarkdown`: add a short readable report per result.
- `blockMedia` (default off): don't download video and audio files during the audit. See Memory below.
- `respectRobotsTxt` / `ownSiteConfirmed`: robots.txt is always respected unless you confirm you own the sites.

### Output

One dataset item per URL per device with `scores`, `metrics`, `coreWebVitalsLab`, `opportunities`, `failedAudits`, `mediaBlocked` and `error` (null on success). Every item, error items included, and the run summary record `mediaBlocked`. A run summary is saved as `OUTPUT_SUMMARY` in the key-value store.

### Memory

The default 2048 MB covers most pages; very heavy news pages (CNN-class) can use about 85% of it. How memory is kept down:

- Each run gathers in a short-lived process (Node heap capped at 640 MB) with its own Chrome, which this Actor launches and always kills, then computes the audits in a second process after Chrome has closed (heap capped at 768 MB). Memory is returned between runs, and each page's JavaScript heap is capped at 512 MB.
- No full-page screenshot or filmstrip, and the trace leaves out the categories Lighthouse records only for the DevTools Performance panel (script-source rundown, frame and latency events). On CNN that halves the trace (35 to 16 MB) and gives identical scores and metrics on the same recording. Screenshots in the trace stay, because Speed Index needs them.
- `blockMedia` (off by default) skips video and audio: every request Chrome marks as media, plus URLs whose path ends in a media extension (.mp4, .webm, .m3u8, .mp3 and similar), with or without a query string. Patterns match the whole URL, so a web app manifest (`site.webmanifest`) or an image such as `a.mov.jpg` still loads. Images, video posters, scripts, CSS and fonts load, so LCP and CLS are measured on the real layout; page weight leaves media out. It saves only a little memory, so it isn't a fix for very heavy pages.

Peak memory (PSS of the whole run, local tests by QA and the developer, 2 Nov 2026; CNN re-measured 5 Nov 2026 with and without the guard proxy, Lighthouse runs one at a time, default 2048 MB):

| Page (mobile) | Peak memory | Share of 2048 MB |
|---|---|---|
| CNN homepage, all categories, default (media loaded) | 1718 to 1747 MB (3 runs, with `--disk-cache-size=1` / `--media-cache-size=1`) | up to 85% |
| CNN homepage, all categories, `blockMedia` on | 1373 to 1567 MB (4 runs) | up to 77% |
| CNN homepage, performance only, media loaded | 1389 to 1468 MB (5 runs) | up to 72% |
| CNN homepage, performance only, `blockMedia` on | 1230 to 1448 MB (5 runs) | up to 71% |
| BBC homepage, performance only, media loaded | 773 to 835 MB (6 runs) | up to 41% |

These are local measurements; the first cloud run confirms them.

Very heavy news pages (CNN-class) can use about 85% of 2048 MB. For big batches of them, set the run's memory to 4096 MB. That doubles the compute charged to your Apify account per run, because Apify bills compute by allocated memory.

Tried and left out: Chrome's `--renderer-process-limit=1` and `--disable-site-isolation-trials` put ad iframes in the page's own renderer. They didn't lower memory (1625 to 1658 MB without the lean trace, 1484 to 1558 MB with it) and changed the results (LCP 10 to 14 s instead of about 47 s, TBT up by about 1.5 s), so audits would no longer match a normal Lighthouse run. `--disable-dev-shm-usage` is on (Docker's /dev/shm is only 64 MB).

### Pricing

Pay per event: one `lighthouse-audit` per completed Lighthouse run, i.e. per URL, device and run (`runsPerUrl`, default 1). Failed or blocked audits are not charged. The price is $20 per 1,000 runs ($0.020 each); one mobile audit of 50 URLs costs $1.00. Very heavy pages can occasionally time out: Lighthouse reports `PAGE_HUNG` when a page stops responding, and you get a free item with `errorCode: lighthouse-error`. Failed audits aren't charged.

### Compliance

Audits run one at a time with at least a second between audits of the same host (longer if robots.txt sets a Crawl-delay), robots.txt is honoured, and no logins, captchas or proxies are used.

- robots.txt is fetched with the Actor's own User-Agent (`WebVitalsAuditor/0.1 (+https://apify.com; robots-aware)`), and Lighthouse page loads send Lighthouse's device User-Agent with the same `WebVitalsAuditor/0.1` token appended. Some sites serve different content to identified tools, so scores can differ slightly from an anonymous Lighthouse run.
- If robots.txt can't be fetched because of a server error, 429 or network error, the host is treated as fully disallowed (RFC 9309) and you get a free item with `errorCode: robots-unavailable`. A 404 robots.txt means no restrictions.
- Private, loopback, link-local and other reserved network addresses are never audited (`errorCode: blocked-private`). If a page redirects to such an address, or to a host whose robots.txt disallows it, the result is discarded and not charged.

# Changelog

This Actor's version history is a separate document: https://apify.com/madpai/web-vitals-auditor/changelog.md

# Actor input Schema

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

Public pages to audit with Lighthouse. One audit per URL per device.

## `strategy` (type: `string`):

Lighthouse device emulation and throttling preset.

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

Lighthouse categories to score. Performance alone is fastest.

## `runsPerUrl` (type: `integer`):

Run Lighthouse this many times per URL and device and report the median run, with min/max of every score and metric. Lab scores vary by several points between runs; 3 or 5 runs give a steadier result. Each completed run is charged as one Lighthouse audit (lighthouse-audit event), so 3 runs cost 3 audits. Failed runs are not charged.

## `includeMarkdown` (type: `boolean`):

Add a short human/LLM-readable summary to each result.

## `respectRobotsTxt` (type: `boolean`):

Skip URLs disallowed by robots.txt. Can only be turned off together with "I own these sites".

## `ownSiteConfirmed` (type: `boolean`):

Only for your own sites; allows turning off robots.txt.

## `blockMedia` (type: `boolean`):

Off by default, so pages load as visitors see them. Turn on to not download video and audio (requests Chrome marks as media, plus URLs ending in .mp4, .webm, .m3u8, .mp3 and similar). Images (including video posters), scripts, CSS, fonts and web app manifests still load. Page weight then leaves media out. The default 2048 MB is enough either way (heavy news homepage, media loaded: about 1.5 to 1.6 GB in local tests).

## Actor input object example

```json
{
  "urls": [
    "https://crawlee.dev"
  ],
  "strategy": "mobile",
  "categories": [
    "performance",
    "accessibility",
    "best-practices",
    "seo"
  ],
  "runsPerUrl": 1,
  "includeMarkdown": true,
  "respectRobotsTxt": true,
  "ownSiteConfirmed": false,
  "blockMedia": false
}
```

# Actor output Schema

## `audits` (type: `string`):

Dataset with one item per URL and device: url, strategy, finalUrl, scores (performance, accessibility, bestPractices, seo), metrics (lcpMs, cls, tbtMs, fcpMs, speedIndexMs, ttiMs, serverResponseMs, totalBytes), coreWebVitalsLab, opportunities, failedAudits, lighthouseVersion, runsRequested, runsCompleted, metricsMedian, metricsRange, scoresRange, runErrors, reportMarkdown, error and errorCode (null on success; not charged), auditedAt.

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

JSON counts for the run: itemsPushed, audits, failed, runsPerUrl, lighthouseRunsCharged, budgetLimitReached, skippedForBudget, finishedAt.

# 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://crawlee.dev"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("madpai/web-vitals-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://crawlee.dev"] }

# Run the Actor and wait for it to finish
run = client.actor("madpai/web-vitals-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://crawlee.dev"
  ]
}' |
apify call madpai/web-vitals-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,madpai/web-vitals-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/msSMWFpMAFuoKDe8J/builds/yd21X9LANFJVwkSWQ/openapi.json
