# Bulk PageSpeed & Lighthouse Audit (Core Web Vitals) (`ikas/bulk-pagespeed-lighthouse-audit`) Actor

Audit many URLs or a whole sitemap with Google Lighthouse in one run: mobile + desktop, Core Web Vitals PASS/FAIL, top 5 fixes as columns, failed SEO/accessibility checks, and score changes vs your last run. No API key needed.

- **URL**: https://apify.com/ikas/bulk-pagespeed-lighthouse-audit.md
- **Developed by:** [Jacob](https://apify.com/ikas) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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 PageSpeed & Lighthouse Audit (Core Web Vitals, mobile + desktop)

Paste a list of URLs **or a sitemap** (up to 500 pages per run) and get a Google Lighthouse audit for every page, on **mobile and desktop in the same run**. Each row is one URL and one device, flattened so it drops straight into a spreadsheet or CSV:

- **Scores**: Performance, Accessibility, Best Practices and SEO (0 to 100)
- **Core Web Vitals PASS/FAIL**, plus LCP, CLS and TBT with good / needs-improvement / poor ratings
- **The top 5 fixes as columns** (`fix1`…`fix5`), each with estimated ms and KB saved
- **Failed SEO, accessibility and best-practice checks**, listed by name
- **Changes since your last run** (optional): score deltas, LCP change, and a `regression` flag

You don't need an API key or a Google account. The actor runs the open-source Lighthouse engine in its own headless Chrome on Apify.

### Who it's for

- **SEO and web agencies:** audit every client page (or the whole sitemap) and send a CSV of what to fix first.
- **Developers and site owners:** schedule it weekly with *Compare with my previous run* to catch regressions after a deploy.
- **Lead generation / prospecting:** spot which sites in a list fail Core Web Vitals.

### What you get in one run

- **A list of URLs or a sitemap** (sitemap index files are followed). Up to 500 URLs per run; see *How many URLs per run?* below.
- **Mobile and desktop** audited in the same run, one row each.
- **Flat, CSV-ready columns**: scores, Core Web Vitals values with ratings, a `cwvLabStatus` PASS/FAIL verdict, and `fix1`…`fix5` with estimated savings.
- **Week-over-week compare** (optional): deltas against your previous run and a `regression` flag. Before flagging, it re-checks the page (median of 3 runs) to filter out lab noise.
- **Failed audits are not billed.** You still get an error row with the reason.
- **No Google API key or quota.** Lighthouse runs inside the actor.

It's the same Lighthouse engine PageSpeed Insights uses, but the numbers won't match PSI exactly, because the machine and network are different. If you only need to check a few URLs now and then, the free PageSpeed Insights website is fine. This actor is for lists, sitemaps, CSV exports and scheduled checks.

### How many URLs per run?

Audits run **one at a time**. That's deliberate: running several Lighthouse audits in parallel on one machine makes them compete for CPU and drags scores down. In our cloud tests, 10 audits took 248 s including start-up, which works out to about **140 audits per hour**. Heavy pages (60+ s each) are slower.

| URLs × devices | Audits | Typical time |
|---|---|---|
| 25 × 2 (default) | 50 | ~20–25 min |
| 100 × 2 | 200 | ~1.5 h |
| 250 × 2 | 500 | ~3.5–4 h (near the default 4 h timeout) |

If a run gets close to its timeout, it stops cleanly, saves results and history, and lists the pages it didn't reach in `SUMMARY.notAudited`. For bigger lists, raise the run timeout or split the list across runs.

### Input example

```json
{
  "urls": ["https://www.python.org", "https://apify.com"],
  "sitemapUrl": "https://apify.com/sitemap.xml",
  "maxSitemapUrls": 20,
  "devices": ["mobile", "desktop"],
  "compareToPrevious": true,
  "minPerformanceScore": 80
}
```

### Output example (one row, trimmed)

Real result from an Apify cloud run on Oct 26, 2026. With *Compare with my previous run* turned on, rows also get `performanceDelta`, `lcpDeltaMs` and `regression`.

```json
{
  "url": "https://www.python.org/",
  "device": "mobile",
  "performanceScore": 49,
  "accessibilityScore": 75,
  "bestPracticesScore": 100,
  "seoScore": 83,
  "cwvLabStatus": "FAIL",
  "lcpMs": 6114, "lcpRating": "poor",
  "cls": 0, "clsRating": "good",
  "tbtMs": 941, "tbtRating": "poor",
  "fcpMs": 3451, "ttfbMs": 60, "pageWeightKb": 504, "requestCount": 23,
  "fix1": "Eliminate render-blocking resources", "fix1SavingsMs": 2066,
  "fix2": "Reduce unused CSS", "fix2SavingsMs": 840, "fix2SavingsKb": 129,
  "fix3": "Reduce unused JavaScript", "fix3SavingsMs": 420, "fix3SavingsKb": 59,
  "failedSeoChecks": ["Links do not have descriptive text", "Links are not crawlable"],
  "lighthouseVersion": "12.8.2", "source": "lighthouse"
}
```

There are two dataset views: **Scores & Core Web Vitals** (key columns) and **All fields** (everything, ready for CSV).

### Pricing

You pay per completed audit, where **one audit = one URL on one device**. Mobile + desktop for 10 pages is 20 audits. Failed audits (site down, blocked, timeout) still get an error row but are **never billed**. There's also a small per-run start fee. Current prices are shown on the Pricing tab. To cap spend, set *Max URLs per run* or a maximum charge on the run. Confirmation re-runs for suspected regressions are not billed extra.

### Good to know

- **Lab data, not field data, and it's noisy.** Scores come from a real Chrome run in the Apify cloud with Lighthouse's standard mobile throttling. Light pages are stable: example.com scored 99 to 100 every time. Heavy pages swing a lot. In our own two 4 GB cloud runs about 10 minutes apart, with no site change, python.org mobile scored **72, then 49** (a 23-point swing) and desktop scored 83, then 90. Treat a single run as a snapshot, not a verdict. For monitoring, keep the regression threshold at 10–15 points (default 10) and leave *Confirm regressions* on. It re-audits a flagged page twice and uses the median.
- `cwvLabStatus` uses Google's "good" thresholds: LCP ≤ 2.5 s, CLS ≤ 0.1, and TBT ≤ 200 ms (TBT stands in for INP in lab tests).
- **Your own PSI key (untested beta):** there's an optional mode that sends audits through Google's PageSpeed Insights API under *your* key and adds real-user (CrUX) fields (`fieldLcpMs`, `fieldInpMs`, `fieldCls`, `fieldCwvStatus`). It hasn't been verified end-to-end yet, so leave it empty unless you want to try it. Failed audits are not billed.
- Typical speed is 10 to 60 seconds per audit, depending on how heavy the page is. Pages behind a login, private/internal addresses and bot-walled sites can't be audited and return an unbilled error row.
- The actor defaults to 4 GB of memory, which gives Chrome a full CPU and makes scores more stable. We don't recommend going lower.

### More tools from Ikas

- [Workday Jobs Scraper & New-Job Monitor](https://apify.com/Ikas/workday-jobs-monitor): clean job rows from any Workday career site, with only-new-jobs monitoring.
- [Business Entity Lookup: NJ, KY, ND Secretary of State](https://apify.com/Ikas/sos-business-entity-lookup): official registry status, type and dates for KYB checks.

### Reliability

A daily canary audit checks that every metric still comes back, so breaking changes get caught the same day.

# Actor input Schema

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

One page per line. https:// is added if missing. Each URL x device is one billable audit. Audits run one at a time (about 140 per hour; heavy pages are slower).

## `sitemapUrl` (type: `string`):

e.g. https://example.com/sitemap.xml (sitemap index files are followed). Combined with the URL list.

## `maxSitemapUrls` (type: `integer`):

Takes the first N page URLs from the sitemap.

## `maxUrls` (type: `integer`):

Hard cap on pages per run. Audits run sequentially at roughly 140 per hour, so 25 URLs x 2 devices takes about 20-25 minutes. For bigger lists, raise the run timeout (default 4 h, enough for about 250 URLs x 2 devices) or split the list.

## `devices` (type: `array`):

Mobile, desktop, or both in one run (one row each).

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

Leave all four for full scores. Fewer categories run slightly faster (same price).

## `compareToPrevious` (type: `boolean`):

Adds score deltas, LCP change and a regression flag per URL + device, using a named store in your account. Great with a weekly schedule.

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

Flag a regression when any category score drops by at least this many points (or CWV goes PASS to FAIL). Lab scores on heavy pages can swing 20+ points between runs, so 10-15 is recommended.

## `confirmRegressions` (type: `boolean`):

When a drop is detected, re-audit that page twice and use the median before flagging it. Cuts false alarms from lab noise. Re-runs are not billed extra (still one audit), but take more time.

## `historyStoreName` (type: `string`):

Named key-value store that keeps the last scores. Use different names for separate projects/clients.

## `minPerformanceScore` (type: `integer`):

Adds passesBudget = performance score >= this value.

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

Stores the standard Lighthouse HTML report per audit and adds a link to the row.

## `psiApiKey` (type: `string`):

UNTESTED BETA - leave empty (recommended) to run Lighthouse inside this actor. If you add YOUR OWN Google PSI key, audits go through Google's API under your key and rows also include real-user (CrUX) field data when Google has it. This mode has not yet been verified end-to-end; failed audits are not billed.

## `canaryMode` (type: `boolean`):

Audits one known page and fails the run if any metric is missing. Not billed.

## Actor input object example

```json
{
  "urls": [
    "https://www.python.org",
    "https://apify.com"
  ],
  "maxSitemapUrls": 25,
  "maxUrls": 25,
  "devices": [
    "mobile",
    "desktop"
  ],
  "categories": [
    "performance",
    "accessibility",
    "best-practices",
    "seo"
  ],
  "compareToPrevious": false,
  "regressionThreshold": 10,
  "confirmRegressions": true,
  "historyStoreName": "lighthouse-audit-history",
  "saveHtmlReport": false,
  "canaryMode": false
}
```

# Actor output Schema

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

Overview table.

## `fullResults` (type: `string`):

Every flattened field.

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

Counts of audits, CWV pass/fail, regressions.

# 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://www.python.org",
        "https://apify.com"
    ],
    "devices": [
        "mobile",
        "desktop"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ikas/bulk-pagespeed-lighthouse-audit").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://www.python.org",
        "https://apify.com",
    ],
    "devices": [
        "mobile",
        "desktop",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("ikas/bulk-pagespeed-lighthouse-audit").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://www.python.org",
    "https://apify.com"
  ],
  "devices": [
    "mobile",
    "desktop"
  ]
}' |
apify call ikas/bulk-pagespeed-lighthouse-audit --silent --output-dataset

```

## MCP server setup

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

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/iZS4jsnYjxcudc6P9/builds/dzu3J8B6MYsudxeXu/openapi.json
