# PageSpeed Insights Bulk - Core Web Vitals & Lighthouse Audit (`scrapewise/pagespeed-core-web-vitals`) Actor

Bulk PageSpeed Insights audit and page speed analyzer with real Lighthouse, mobile and desktop, no API key: performance score, LCP, TBT, CLS, FCP, TTFB, page size and top 5 fixes. Monitoring mode with regression alerts, full HTML report, optional real-user CrUX data. Failed pages free.

- **URL**: https://apify.com/scrapewise/pagespeed-core-web-vitals.md
- **Developed by:** [Scrapewise Data](https://apify.com/scrapewise) (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

from $30.60 / 1,000 lighthouse audit delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## PageSpeed & Core Web Vitals Checker

PageSpeed & Core Web Vitals Checker runs a **real Google Lighthouse audit on every URL in your list**, on mobile and desktop, the same engine that powers PageSpeed Insights. For each URL you get:

- the **performance score** (0 to 100) for mobile and desktop;
- **lab Core Web Vitals and timings**: LCP, TBT (the lab stand-in for INP), CLS, FCP, Speed Index, time to interactive and server response time (TTFB);
- **page size and number of requests**, split by type (scripts, images, fonts, third parties);
- the **top 5 fixes** with estimated savings in milliseconds and KB;
- optionally the **accessibility, best practices and SEO** scores;
- optionally the **real-user Core Web Vitals from the Chrome UX Report** (p75 LCP, INP, CLS, FCP, TTFB and pass or fail), with your own free Google API key.

**US$ 0.036 per device audit, so US$ 0.072 per URL on mobile and desktop (US$ 72 per 1,000 URLs). No monthly fee, no start fee. Pages that do not load are free.**

### At a glance

- **Price per URL:** US$ 0.072 for mobile + desktop, US$ 0.036 for one device
- **Price per 1,000 URLs:** US$ 72 on both devices, US$ 36 on one
- **Engine:** Lighthouse 13 with Chrome, simulated throttling like PageSpeed Insights
- **Google API key:** Not needed. Optional, only for real-user CrUX data
- **Per URL:** Scores, LCP, TBT, CLS, FCP, Speed Index, TTFB, page size, requests, top 5 fixes
- **Speed:** One audit at a time, about 50 seconds each
- **Failed pages:** Free, with an `errorCode`

### What it fixes

- **A real bulk list, not one URL per run.** Paste hundreds of pages; each one is a row with mobile and desktop side by side.
- **No Google API key and no shared quota.** Lighthouse runs inside the Actor, so there is no `429 quota exceeded` and no key to create. A key is only needed for the optional real-user data.
- **Failed pages cost nothing.** A page that does not load, an invalid link or a Lighthouse crash gives a free row with an `errorCode`, never an empty run or a charge for nothing. If Lighthouse itself breaks on every URL, the run fails loudly instead of finishing "succeeded".
- **Scheduled monitoring with alerts.** Turn on `monitoringMode` and each row brings the previous run's scores, the drops (`regressions`) and `hasRegression`; add a webhook and you get one alert with every page that got slower.
- **The full Lighthouse report when you need it.** `saveHtmlReport` stores the same HTML report you know from Chrome DevTools, one link per audit.

### Switching from another PageSpeed or Lighthouse actor? Your existing input works

Paste the input you already use. These names and values are read as they are:

| You may already send | Read as |
|---|---|
| `url` (one link, or several, one per line), `urlsA` + `urlsB`, `startUrls`, `websites`, `domains` | `urls` |
| `strategy` or `device`: `mobile`, `desktop` (also `both`) | `devices` |
| `categories`: `performance`, `accessibility`, `best-practices`, `seo` | the categories scored (performance is always included) |
| `apiKey` (a Google Cloud key) | `googleApiKey`, used only for the real-user Chrome UX Report data |
| `concurrency` | `maxConcurrency` |
| `throttling`: `simulated`, `devtools`, `none` | the same |
| `saveHtmlReport`, `monitoringMode`, `regressionThreshold`, `alertWebhookUrl` | the same |

When you send `strategy: "desktop"` and leave this form's devices at the default, you get (and pay for) desktop only.

### One real row

`https://www.python.org`, audited on 2026-09-30 in the Apify cloud (desktop block and lists shortened):

```json
{
  "type": "page",
  "url": "https://www.python.org/",
  "mobilePerformance": 65,
  "desktopPerformance": 92,
  "lcpMs": 4419,
  "tbtMs": 531,
  "cls": 0.003,
  "pageSizeKb": 523,
  "requests": 26,
  "mobile": {
    "performanceScore": 65,
    "largestContentfulPaintMs": 4419,
    "totalBlockingTimeMs": 531,
    "cumulativeLayoutShift": 0.003,
    "firstContentfulPaintMs": 3156,
    "speedIndexMs": 3156,
    "serverResponseTimeMs": 1,
    "pageSizeKb": 523,
    "requests": 26,
    "requestsByType": { "stylesheet": { "requests": 4, "kb": 156 }, "script": { "requests": 8, "kb": 133 } },
    "domElements": 884,
    "opportunities": [
      { "id": "render-blocking-insight", "title": "Render-blocking requests", "estimatedSavingsMs": 2050, "estimatedSavingsKb": null },
      { "id": "unused-css-rules", "title": "Reduce unused CSS", "estimatedSavingsMs": 900, "estimatedSavingsKb": 129 },
      { "id": "unused-javascript", "title": "Reduce unused JavaScript", "estimatedSavingsMs": 300, "estimatedSavingsKb": 59 }
    ],
    "lighthouseVersion": "13.5.0"
  },
  "desktop": { "performanceScore": 92, "largestContentfulPaintMs": 1359, "totalBlockingTimeMs": 95, "cumulativeLayoutShift": 0.112 },
  "fieldDataStatus": "NO_API_KEY",
  "scrapedAt": "2026-09-30T23:55:55Z"
}
```

In the same run, `https://www.wikipedia.org` scored 97 on mobile and 100 on desktop.

### What you can do with it

- **SEO audits:** check the speed of every important page of a site, not only the home page.
- **Agency reporting:** schedule a monthly run on your clients' pages and track scores and LCP.
- **Before and after a release:** audit the same URLs before and after a deploy and compare.
- **Competitor benchmarks:** compare your pages with competitors on the same machine and settings.
- **Lead generation:** find slow websites in a list of prospects and offer a speed fix.

### Input

| Field | What it does |
|---|---|
| `urls` | Pages to audit, one per line. `example.com` is read as `https://example.com` |
| `devices` | `mobile`, `desktop` or both (default). You pay only for the audits that run |
| `includeOtherCategories` | Also score accessibility, best practices and SEO. Same price |
| `saveHtmlReport` | Also save the full Lighthouse HTML report of each audit (link in `htmlReportUrl`). Same price |
| `throttling` | `simulated` (default, like PageSpeed Insights), `devtools` or `none` |
| `googleApiKey` | Optional: your own Google Cloud API key with the Chrome UX Report API enabled, for real-user data |
| `maxConcurrency` | Leave empty: one audit at a time per 4 GB of memory |
| `monitoringMode` | Compare every URL with the previous run (for scheduled runs) |
| `regressionThreshold` | Points a score must drop to count as a regression (default 5) |
| `alertWebhookUrl` | Optional: one JSON POST with all regressions at the end of the run |

```json
{ "urls": ["https://www.python.org", "https://www.wikipedia.org"], "devices": ["mobile", "desktop"] }
```

### Schedule it: get told when a page gets slower

Create a schedule (daily or weekly) with this input:

```json
{ "urls": ["https://www.python.org", "https://www.wikipedia.org"], "devices": ["mobile"], "monitoringMode": true, "regressionThreshold": 5, "alertWebhookUrl": "https://hooks.slack.com/services/your/webhook" }
```

The first run saves the scores. From the second run on, each row has `previousRun` (the last scores for that URL and device), `regressions` (for example `{ "device": "mobile", "category": "performance", "from": 72, "to": 61, "drop": 11 }`) and `hasRegression`, and the webhook gets one POST with every page that dropped. The history lives in a key-value store of your own account named `pagespeed-core-web-vitals-history`. Lab scores move a few points between runs, so a threshold of 5 or more avoids noise.

### Output fields

Top level, for the table and CSV: `url`, `input`, `finalUrl`, `mobilePerformance`, `desktopPerformance`, `lcpMs`, `tbtMs`, `cls`, `pageSizeKb`, `requests` (these five from the mobile audit, or desktop when only desktop runs; `labDevice` says which), `coreWebVitalsMobile` (`pass` or `fail` from real users, when there is CrUX data), `fieldDataStatus`, `deviceErrors`, `scrapedAt`.

Per device (`mobile` and `desktop`): `performanceScore`, `accessibilityScore`, `bestPracticesScore`, `seoScore`, `largestContentfulPaintMs`, `totalBlockingTimeMs`, `cumulativeLayoutShift`, `firstContentfulPaintMs`, `speedIndexMs`, `timeToInteractiveMs`, `serverResponseTimeMs`, `maxPotentialFidMs`, `pageSizeKb`, `requests`, `requestsByType`, `domElements`, `opportunities` (`id`, `title`, `displayValue`, `estimatedSavingsMs`, `estimatedSavingsKb`), `warnings`, `lighthouseVersion`.

Real users (`fieldData`, only with your key): per device, `source` (`url`, or `origin` when the page itself has too little traffic), `lcpP75Ms`, `inpP75Ms`, `clsP75`, `fcpP75Ms`, `ttfbP75Ms`, `passesCoreWebVitals`, `collectionPeriod`.

With `monitoringMode`: `previousRun`, `regressions` (`device`, `category`, `from`, `to`, `drop`) and `hasRegression`. With `saveHtmlReport`: `htmlReportUrl` in each device block. With a `throttling` other than `simulated`, the device block says which one ran.

`fieldDataStatus` is `OK`, `NO_API_KEY`, `NO_FIELD_DATA` (Google has no real-user data for the page or its origin), `INVALID_KEY` or `QUOTA`.

#### Error codes (never charged)

| errorCode | Meaning |
|---|---|
| `INVALID_URL` | Not an http or https website link |
| `PAGE_NOT_LOADED` | The page did not load: DNS error, HTTP error (like 403 from a bot wall), crash or hang |
| `AUDIT_TIMEOUT` | The audit took more than 150 seconds |
| `AUDIT_FAILED` | Lighthouse did not finish |
| `NOT_REACHED`, `UNEXPECTED` | The run timeout arrived, or something unforeseen broke; only that URL is lost |

When one device works and the other fails, the row keeps the device that worked, you pay for that one only, and `deviceErrors` explains the other.

### Pricing

| Event | Price |
|---|---|
| Lighthouse audit delivered (one URL on one device) | US$ 0.036 |

1,000 URLs on mobile and desktop cost US$ 72; on mobile only, US$ 36. Duplicates in the input are audited once, failed audits are free, and there is no Apify compute bill on top.

### FAQ

**Why are my scores lower than PageSpeed Insights?**
Lighthouse scores depend on the machine. This Actor uses the same Lighthouse settings as PageSpeed Insights (a Moto G Power on slow 4G for mobile, simulated throttling), but it runs on a cloud CPU that is slower than Google's own servers. Heavy JavaScript pages take longer to run, so TBT and LCP come out higher and the score can come out lower, mostly on mobile. Light pages (like `wikipedia.org`, 97 on mobile) are barely affected. Every URL runs on the same settings, so the numbers work for comparing pages, competitors and releases; like in PageSpeed Insights, a heavy page can move a few points between runs. Use the optional real-user data (CrUX) when you need the numbers Google uses for ranking.

**Why not the PageSpeed Insights API?**
Google's API terms do not allow a service to resell access to its APIs with its own key, and the API without a key is rate-limited to almost nothing. So this Actor runs Lighthouse itself, open source (Apache 2.0), and only calls Google's Chrome UX Report API with your own key, when you give one.

**How do I get the real-user Core Web Vitals?**
Create a free API key in the Google Cloud console, enable the "Chrome UX Report API" on it and paste it in `googleApiKey`. It is free, with 150 queries per minute. Real-user data exists only for pages and sites with enough Chrome traffic.

**Why one audit at a time?**
Running several Lighthouse audits on the same CPU makes each one slower and the scores unstable. One at a time, about 50 seconds per audit, gives stable numbers. To go faster, give the run more memory: each extra 4 GB adds one audit at a time.

**What is TBT and where is INP?**
INP needs real user clicks, so a lab test cannot measure it. Total Blocking Time is Google's lab stand-in for INP. The real INP comes in `fieldData` when you give your key.

### Limitations

- Lab scores come from a cloud CPU and can differ from PageSpeed Insights (see the FAQ).
- Some sites block cloud data centers and answer 403; those rows come back as `PAGE_NOT_LOADED` and are free.
- Pages behind a login cannot be audited.
- Real-user data needs your own Google API key and exists only for pages with enough traffic.
- Lighthouse is an open source project by Google. This Actor is not affiliated with Google.

### Changelog

- **2026-10-08, 0.1:** input of other PageSpeed and Lighthouse actors works as is (`url`, `strategy`, `device`, `categories`, `apiKey`, `concurrency`, `urlsA`/`urlsB`); monitoring mode with previous scores, regressions and an alert webhook; full HTML report; throttling choice; the run fails loudly if Lighthouse breaks on every URL.
- **2026-09-30, 0.1:** first release. Lighthouse 13 audits on mobile and desktop, lab Core Web Vitals, page size, requests, top 5 fixes, optional accessibility, best practices and SEO scores, optional real-user CrUX data with your own key, free error rows.

# Actor input Schema

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

Pages to audit, one per line (https://example.com/pricing). A bare domain like example.com is read as https://example.com. Each URL becomes one row. Audits run one at a time, about 50 seconds each, so 100 URLs on both devices take about 2.5 hours.

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

Each device is one Lighthouse audit and one charge. Pick only mobile or only desktop to pay half: you are charged only for the audits that run.

## `includeOtherCategories` (type: `boolean`):

Adds the other three Lighthouse category scores (0 to 100) to each device. Same price.

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

Saves the complete Lighthouse report of each audit as an HTML page in the run's key-value store and adds its link (htmlReportUrl) to the row. Same price.

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

How Lighthouse slows the network and CPU. Keep 'simulated' to match PageSpeed Insights settings.

## `googleApiKey` (type: `string`):

Optional. Your own free Google Cloud API key with the Chrome UX Report API enabled. With it, each row also gets the p75 LCP, INP, CLS, FCP and TTFB of real Chrome users and whether the page passes Core Web Vitals. Without it, you get the Lighthouse lab data only. The key is used only for your own queries.

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

Leave empty: one audit at a time per 4 GB of run memory, which keeps lab timings stable. More at once is faster but makes scores lower and less stable.

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

Remembers the scores of every URL and device in a key-value store of your account (pagespeed-core-web-vitals-history) and adds previousRun, regressions and hasRegression to each row. Made for scheduled runs: you see at once which pages got slower since last time.

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

A category score (performance, accessibility, best practices, SEO) that drops by this many points or more since the previous run counts as a regression.

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

Optional. When monitoring finds regressions, one JSON POST with all of them is sent here at the end of the run (Slack, Discord, Zapier, Make or your own endpoint).

## Actor input object example

```json
{
  "urls": [
    "https://apify.com",
    "https://www.wikipedia.org"
  ],
  "devices": [
    "mobile",
    "desktop"
  ],
  "includeOtherCategories": false,
  "saveHtmlReport": false,
  "throttling": "simulated",
  "monitoringMode": false,
  "regressionThreshold": 5
}
```

# Actor output Schema

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

No description

## `resultsCsv` (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 = {
    "urls": [
        "https://apify.com",
        "https://www.wikipedia.org"
    ],
    "devices": [
        "mobile",
        "desktop"
    ]
};

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapewise/pagespeed-core-web-vitals"
        }
    }
}
```

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/hALdM200iMXNUFMQV/builds/ab95D3LxlAVBPLJjp/openapi.json
