# Bulk PageSpeed & Lighthouse Checker: Core Web Vitals Monitor (`starwebdigital/pagespeed-core-web-vitals-checker`) Actor

Bulk-check URLs or a whole sitemap with Google's official PageSpeed Insights API. Lighthouse scores, real-user Core Web Vitals (LCP, INP, CLS) from CrUX and top fixes. Schedule it for regression alerts. $4 per 1,000 checks.

- **URL**: https://apify.com/starwebdigital/pagespeed-core-web-vitals-checker.md
- **Developed by:** [Datawell](https://apify.com/starwebdigital) (community)
- **Categories:** SEO tools, Developer tools, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 url checkeds

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 Insights & Core Web Vitals Checker

**Run Google PageSpeed Insights on hundreds of URLs, or a whole sitemap, in one run.** For every page you get the four Lighthouse scores (performance, accessibility, best practices, SEO), **Core Web Vitals (LCP, INP, CLS, FCP, TTFB)** from both the Lighthouse lab test and real Chrome users (CrUX), and the top fixes with estimated savings. Export to CSV, Excel or JSON, or schedule it weekly and get flagged when a page gets slower.

- ✅ **Official Google APIs:** [PageSpeed Insights API v5](https://developers.google.com/speed/docs/insights/v5/get-started) + [Chrome UX Report (CrUX) API](https://developer.chrome.com/docs/crux/api). No scraping.
- ✅ **Bulk:** paste a URL list or an XML sitemap (sitemap index and `.xml.gz` supported), mobile, desktop or both.
- ✅ **Real-user data:** 28-day p75 field data from CrUX, with whole-site fallback when a page has too little traffic.
- ✅ **Monitor mode:** compares each run with the last one and flags score drops and Core Web Vitals that go from pass to fail.
- ✅ **$4 per 1,000 checks**, failed checks free, **try it without a Google key** (10 checks per run).

Built by **Datawell**.

### What does the PageSpeed & Core Web Vitals Bulk Checker do?

It sends each URL to Google's PageSpeed Insights API, the same service behind [pagespeed.web.dev](https://pagespeed.web.dev). Google's servers run Lighthouse on the page and return the report; the Actor adds real-user Core Web Vitals from the CrUX API, flattens everything into **one row per URL and device**, and ranks the biggest fixes. It's a bulk Lighthouse / PageSpeed Insights API runner you don't have to build, host or rate-limit yourself.

Use it as a **bulk PageSpeed test**, a **Core Web Vitals checker for multiple URLs**, a **sitemap speed audit**, or a **scheduled Core Web Vitals monitor** for client sites.

### Who uses it

- **SEO and web agencies:** weekly Core Web Vitals checks for every client site, with regression alerts sent to Slack or email.
- **Technical SEO audits:** load a sitemap, sort by worst performance score, and hand developers the top fixes per page.
- **Developers:** run it after a deploy through the API or a webhook to make sure a release didn't slow pages down.
- **Competitor benchmarking:** compare Lighthouse scores and real-user Core Web Vitals across competitors' homepages.
- **Lead generation:** find businesses whose sites fail Core Web Vitals.

### How much does it cost?

Pay per result. No subscription, no monthly minimum.

| Event | Price |
|---|---|
| Run start | $0.01 per run |
| URL checked (1 URL × 1 device, successful checks only) | $0.004 |

| Example | Checks | Cost |
|---|---|---|
| Free trial without a key: 10 URLs, mobile | 10 | **$0.05** |
| 100 URLs from a sitemap, mobile | 100 | **$0.41** |
| 10 client homepages, mobile + desktop, weekly | 20 per run | **$0.09 per week** |
| 500 URLs, mobile + desktop | 1,000 | **$4.01** |

**Failed checks are never charged** (unreachable pages, Google errors, quota limits). Monitor mode and regression alerts cost nothing extra. Set *Maximum cost per run* in the run options and the Actor stops before going over it. Google's APIs are free within your key's quota.

### How to use it

1. Paste your **URLs** (full URLs or bare domains) and/or a **sitemap URL**.
2. Pick **mobile, desktop or both** and the Lighthouse categories you need.
3. Optional: paste your own **Google API key** for unlimited runs (see below). Leave it empty to try 10 checks per run on Datawell's shared key.
4. Click **Start**, then download the dataset as CSV, Excel or JSON, or use it through the Apify API, integrations (Google Sheets, Slack, Zapier, Make, n8n) or webhooks.

#### Free trial and adding your own Google API key

- **Without a key:** each run checks up to **10 URL × device combinations** (10 URLs on mobile, or 5 on both) using Datawell's shared key. Anything above 10 is skipped and **not charged**, and the log tells you so. The shared key is used by everyone trying the Actor, so on busy days it can run out; those checks come back with `errorCode: SHARED_KEY_QUOTA` and are **not charged**.
- **With your own key:** no Actor limit; only Google's quota on your key applies. Getting one takes about 2 minutes, no billing needed:
  1. Open the [Google Cloud credentials page](https://console.cloud.google.com/apis/credentials), create or pick a project, and click *Create credentials → API key*.
  2. Enable the [PageSpeed Insights API](https://console.cloud.google.com/apis/library/pagespeedonline.googleapis.com) and, for real-user data, the [Chrome UX Report API](https://console.cloud.google.com/apis/library/chromeuxreport.googleapis.com) in that project.
  3. Optional but recommended: restrict the key to those two APIs.
  4. Paste the key into **Google API key**. It's a secret input: encrypted, sent to Google in a request header, never logged or saved to the dataset.

### Example inputs

**Sitemap audit (100 pages, mobile, performance + SEO):**

```json
{
  "apiKey": "YOUR_GOOGLE_API_KEY",
  "urls": ["https://docs.apify.com/"],
  "sitemapUrl": "https://docs.apify.com/sitemap.xml",
  "maxSitemapUrls": 100,
  "maxUrls": 100,
  "strategy": "mobile",
  "categories": ["performance", "seo"]
}
```

**Monitor 10 client sites weekly (mobile + desktop, regression alerts):** create a [schedule](https://docs.apify.com/platform/schedules) that runs weekly with this input.

```json
{
  "apiKey": "YOUR_GOOGLE_API_KEY",
  "urls": ["https://client-one.com/", "https://client-two.com/", "...up to 10 sites"],
  "strategy": "both",
  "maxOpportunities": 3,
  "monitorMode": true,
  "monitorStateKey": "agency-clients-weekly",
  "minScoreDrop": 5
}
```

**Quick multi-URL check (no key needed, 5 checks = $0.03):**

```json
{
  "urls": ["https://www.wikipedia.org/", "https://web.dev/", "https://developer.mozilla.org/", "https://www.gov.uk/", "https://github.com/"],
  "strategy": "mobile"
}
```

### Output: what you get for each URL

One dataset row per URL and device:

| Field | What it is |
|---|---|
| `performanceScore`, `accessibilityScore`, `bestPracticesScore`, `seoScore` | Lighthouse category scores, 0–100 |
| `passesCoreWebVitals` | Real-user assessment: LCP, INP and CLS all "good" at p75 |
| `fieldLcpMs`, `fieldInpMs`, `fieldCls` | Real-user 75th percentile values (28-day CrUX data) |
| `labLcpMs`, `labCls`, `labTbtMs` | Lighthouse lab values from this run |
| `coreWebVitals` | Full detail: lab + field LCP, INP, CLS, FCP, TTFB with pass/fail, field data source (page or origin) and collection period |
| `opportunities` | Biggest improvements with estimated savings in ms and bytes, e.g. "Reduce unused JavaScript" |
| `finalUrl`, `fetchTime`, `lighthouseVersion` | Where the page ended up after redirects, and when Google tested it |
| `status`, `errorCode`, `errorMessage` | `ok`, or a clear error (page unreachable, rate limited, invalid key). Failed checks are free. |
| `regression`, `regressionReasons`, `previous` | Monitor mode: what got worse since the last run |

Pass/fail uses Google's "good" thresholds: LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1, FCP ≤ 1.8 s, TTFB ≤ 0.8 s. Lighthouse can't measure INP in a lab, so lab TBT ≤ 200 ms is used as the lab stand-in.

#### Sample output item (shortened)

```json
{
  "url": "https://www.example-shop.com/",
  "strategy": "mobile",
  "status": "ok",
  "performanceScore": 41,
  "accessibilityScore": 92,
  "bestPracticesScore": 96,
  "seoScore": 100,
  "passesCoreWebVitals": false,
  "fieldLcpMs": 3100,
  "fieldInpMs": 150,
  "fieldCls": 0.05,
  "fieldDataSource": "crux-api:url",
  "labLcpMs": 7862,
  "opportunities": [
    { "id": "unused-javascript", "title": "Reduce unused JavaScript", "estimatedSavingsMs": 1350, "estimatedSavingsBytes": 412000 }
  ],
  "regression": false
}
```

### Monitor mode: scheduled Core Web Vitals monitoring

Turn on **Monitor mode**, give the set a **state key** (for example `client-acme`), and schedule the Actor daily or weekly. Each run compares with the previous run for the same key and flags a regression when:

- any Lighthouse category score drops by at least **N points** (default 5), or
- a Core Web Vital (field or lab) goes from **pass to fail**.

Regressed rows get `regression: true` with plain-English reasons. The list is also saved as `REGRESSIONS` in the key-value store, so an Apify integration can post it to Slack or email it. Use one state key per client to keep their histories separate.

### Integrations and API

- **Apify API / SDKs:** start runs and read results from your own code (JavaScript, Python, REST).
- **Schedules:** run weekly or daily with no server to maintain.
- **Integrations:** Google Sheets, Slack, Zapier, Make, n8n, webhooks.
- **AI agents:** callable through the Apify MCP server.

### Google quotas: read this before large runs

- **PageSpeed Insights:** Google doesn't publish a fixed number; your per-minute and per-day limits are in your Cloud project under *APIs & Services → PageSpeed Insights API → Quotas*. The Actor rate-limits itself (default 60 requests/min, configurable), retries with exponential backoff on `429`, and **stops cleanly if your daily quota runs out**. Checks that didn't happen aren't charged.
- **CrUX API:** free, 150 queries per minute per project. If it isn't enabled on your key, the Actor falls back to the field data inside the PageSpeed response.
- **Speed:** each check takes Google about 10–30 seconds. With 4 parallel checks expect roughly 10–20 URLs per minute.

### FAQ

**Why do my scores differ from pagespeed.web.dev?** Lighthouse lab scores vary from run to run with network and server load. Field data (`passesCoreWebVitals`, `field*`) is a 28-day real-user aggregate and is what Google uses as a Search signal.

**A page has no field data.** CrUX only covers pages and sites with enough Chrome traffic. With *origin fallback* on, the whole-site value is used and `fieldDataSource` ends in `:origin`.

**Can I check pages behind a login or on localhost?** No. Google's servers must be able to reach the page publicly.

**Do I need a Google API key?** Not to try it: runs without a key check up to 10 URL × device combinations. For anything bigger, add your own free key.

**Is my API key safe?** It's a secret input, encrypted at rest, sent to Google in a request header, and never written to logs or the dataset.

### Disclaimer

This is an unofficial tool by Datawell. It is **not affiliated with, endorsed by, or sponsored by Google**. PageSpeed Insights, Lighthouse, Chrome UX Report and Core Web Vitals are Google products and terms; data comes from Google's public APIs under the applicable API key and Google's terms of service. Results are provided as-is, without warranty; lab scores can vary between runs.

# Actor input Schema

## `apiKey` (type: `string`):

Your own free Google Cloud API key with the <b>PageSpeed Insights API</b> enabled (and optionally the <b>Chrome UX Report API</b> for real-user data). Leave empty to try the Actor on up to <b>10 checks per run</b> (1 check = 1 URL x 1 device) with Datawell's shared key. With your own key there is no limit. Free to create, no billing needed: <a href='https://developers.google.com/speed/docs/insights/v5/get-started' target='_blank'>Google's get-started guide</a>. Stored encrypted, never logged.

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

Pages to test. Full URLs or bare domains (https:// is added). Duplicates are removed.

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

Add every page from an XML sitemap (sitemap index and .xml.gz supported). Combined with the URL list above.

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

Stop collecting sitemap URLs after this many.

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

Hard cap on the number of URLs checked in one run (each URL is checked once per device strategy). Without your own API key the free trial checks at most 10 URL x device combinations per run.

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

Lighthouse device emulation. 'Both' runs two checks per URL.

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

Categories to score (0-100).

## `useCruxApi` (type: `boolean`):

Fetch 28-day real-user Core Web Vitals (p75) from the Chrome UX Report API with your key; falls back to the field data embedded in PageSpeed Insights if the CrUX API is not enabled on your key.

## `fieldDataOriginFallback` (type: `boolean`):

When a single page has too little traffic for CrUX, use the whole site's (origin) real-user data instead. The result says which was used.

## `maxOpportunities` (type: `integer`):

How many of the biggest improvement opportunities (with estimated savings) to include.

## `locale` (type: `string`):

Optional locale for audit titles, e.g. en, de, fr, es.

## `monitorMode` (type: `boolean`):

Remember results between runs (per state key) and flag regressions: a category score drop of at least N points, or a Core Web Vital going from pass to fail. Schedule the Actor daily or weekly.

## `monitorStateKey` (type: `string`):

Name for this monitored set (e.g. a client or site). Runs with the same key compare against each other.

## `minScoreDrop` (type: `integer`):

Flag a regression when any category score drops by at least this many points versus the previous run.

## `concurrency` (type: `integer`):

How many PageSpeed requests run at the same time. Each takes ~10-30 s.

## `maxRequestsPerMinute` (type: `integer`):

Global rate limit for your key. The Actor also slows down automatically and retries with exponential backoff when Google answers 429.

## `maxRetries` (type: `integer`):

Retries for rate limits (429), timeouts and transient Google/Lighthouse errors.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout for the PageSpeed Insights API.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com/",
    "https://www.wikipedia.org/",
    "https://web.dev/"
  ],
  "maxSitemapUrls": 50,
  "maxUrls": 200,
  "strategy": "mobile",
  "categories": [
    "performance",
    "accessibility",
    "best-practices",
    "seo"
  ],
  "useCruxApi": true,
  "fieldDataOriginFallback": true,
  "maxOpportunities": 5,
  "monitorMode": false,
  "monitorStateKey": "default",
  "minScoreDrop": 5,
  "concurrency": 4,
  "maxRequestsPerMinute": 60,
  "maxRetries": 4,
  "requestTimeoutSecs": 120
}
```

# Actor output Schema

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

Lighthouse category scores, Core Web Vitals lab and field values with pass/fail, top opportunities, and error status per URL + strategy.

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

JSON summary: checks OK/failed/skipped, errors by code, retries, field-data sources, regressions, budget stop, free-trial notice.

## `regressions` (type: `string`):

List of URL + strategy pairs that regressed since the previous monitor run, with reasons. Only present in monitor mode.

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

// Run the Actor and wait for it to finish
const run = await client.actor("starwebdigital/pagespeed-core-web-vitals-checker").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/",
        "https://web.dev/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("starwebdigital/pagespeed-core-web-vitals-checker").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/",
    "https://web.dev/"
  ]
}' |
apify call starwebdigital/pagespeed-core-web-vitals-checker --silent --output-dataset

```

## MCP server setup

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

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/KYKfLMRz4OabARdmr/builds/92UqmGbN9peAnM2GM/openapi.json
