# Core Web Vitals Monitor: CrUX + Lighthouse, Trends & Alerts (`benexe/core-web-vitals-monitor`) Actor

Bulk Core Web Vitals for whole sites: real-user CrUX field data (URL with origin fallback), Lighthouse lab audits, 40-week trends and regression alerts between scheduled runs. Sitemap-driven sampling per page template. Bring your own Google API key for unlimited volume.

- **URL**: https://apify.com/benexe/core-web-vitals-monitor.md
- **Developed by:** [Marcel Tomas](https://apify.com/benexe) (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 $4.50 / 1,000 url checked (lab + field)s

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

## Core Web Vitals Monitor: CrUX + Lighthouse, Trends & Alerts

Check Core Web Vitals for a whole site in one run, with **real-user field data** (Chrome UX Report), **Lighthouse lab audits**, **40-week trends**, and **regression alerts** between scheduled runs. Point it at a sitemap and it samples a few URLs per page template (product, post, category…), so you see which template is slow instead of auditing 5,000 near-identical pages.

Built for SEO and WordPress/WooCommerce agencies and in-house web teams who track many sites.

### What you get per URL and device

| Data | Source | Notes |
|---|---|---|
| p75 **LCP, INP, CLS**, FCP, TTFB + GOOD / NEEDS_IMPROVEMENT / POOR + CWV pass/fail | CrUX API (real Chrome users, rolling 28 days) | URL-level when available; otherwise **origin-level fallback** (`field.level`) |
| Lighthouse **performance score**, lab LCP / TBT / CLS / FCP / Speed Index, **top 5 opportunities** | PageSpeed Insights API | Lab data varies between runs (±5 points is normal) |
| Weekly p75 series + trend label (improving / stable / worsening) | CrUX History API | One representative URL per site and template (origin fallback), up to 40 weeks |
| Regression flags vs the previous run | This Actor (`monitorId`) | p75 worse by >10 %, a metric dropping a category, or a lab score drop >15 points |

The key-value store also gets **SUMMARY** (per site, template and device: CWV pass rate, median and worst p75, median lab score) and **REGRESSIONS** (a ready-to-alert list).

### Quick start

1. **One-off audit:** add your sitemap under *Sitemaps* and run it. Lab and field data are on by default.
2. **Weekly monitoring:** set a *Monitor ID* (e.g. `client-acme`), turn *Lab audit* off for the cheapest field-only check, and create an Apify **Schedule** (CrUX History refreshes on Mondays, so Tuesday works well). Hook the *Regressions only* view or the `REGRESSIONS` record to Slack, email or the free n8n / Make template.
3. **Large sites or agencies:** add your own **Google API key** (see below). It removes the free-plan lab limit and uses your own Google quota.

### Input highlights

- `urls` and/or `sitemapUrls` (sitemap index and `.gz` supported; up to 3 levels, 50 files, 50,000 URLs).
- `samplesPerTemplate` (default 3, per site when you add several sites). Deterministic: the same URLs are re-tested every run, so trends compare like with like.
- `templateRules` to override detection with regexes, e.g. `[{"name":"pdp","pattern":"/shop/"}]`.
- `formFactors`: `PHONE`, `DESKTOP`.
- `includeField`, `includeLab`, `includeHistory`, `historyPeriods`.
- `monitorId` and `regressionThresholds`.
- `googleApiKey` (secret, optional).

### Pricing (pay per event)

You pay only for records delivered with data:
| Event | When |
|---|---|
| `url-checked` | A URL × device record with lab data (field data included when available) |
| `field-only-check` | A record with field data only (lab off or failed): the cheap monitoring path |
| `history-trend` | One weekly trend series delivered |

**Never charged:** URLs with no data (`no-field-data`), errors, invalid input, or the part of a run that exceeds your max-charge limit. The run stops cleanly at your budget. *(Prices: see the Pricing tab.)*

### Your own Google API key (BYOK)

1. In Google Cloud Console, create a project and enable **Chrome UX Report API** and **PageSpeed Insights API**. Both are free of charge; no billing account is needed.
2. Create an API key. Restricting it to those two APIs is recommended.
3. Paste it into *Your Google API key*. It is stored as an encrypted secret input and never logged or written to outputs.

Without your own key, runs on the Apify free plan are limited to **25 lab audits per run** (field data is not limited). Paid Apify plans aren't limited this way.

### Good to know

- **CrUX popularity threshold:** many low-traffic URLs have no URL-level data. You then get origin-level data (`field.level: "origin"`), or `no-field-data` (not charged).
- **Field data lags:** it is a rolling 28-day window, so a fix shows up gradually over about 4 weeks. Lab data reacts immediately.
- **Quotas:** CrUX allows 150 queries/min per Google project (this Actor uses ≤120). If Google's quota runs out mid-run, the Actor stops cleanly, keeps what it delivered and charges nothing for the rest.
- Pages behind login, RUM and screenshots are out of scope.

### Data & attribution

Contains Chrome UX Report data by Google, licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Lab data is produced by PageSpeed Insights (Lighthouse). This Actor is not affiliated with or endorsed by Google.

### Related

- [Sitemap & Indexability Auditor](https://apify.com/benexe/sitemap-indexability-auditor): which sitemap URLs Google cannot index, and why (noindex, canonical, robots.txt, redirects, hreflang).

### Support

Open an issue on the Actor's *Issues* tab. Include the run ID; never paste your API key.

# Actor input Schema

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

Pages to check. Provide URLs, sitemaps, or both.

## `sitemapUrls` (type: `array`):

XML sitemap or sitemap index URLs (gzip supported). URLs are grouped by page template (WordPress core, Yoast and Rank Math sitemap names are recognised) and sampled.

## `samplesPerTemplate` (type: `integer`):

URLs tested per template (per site) when using sitemaps. Selection is deterministic: the same URLs are re-tested on every run.

## `templateRules` (type: `array`):

Override template detection. Items: {"name": "product", "pattern": "/shop/"} (regex tested on the URL).

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

Hard cap after sampling.

## `formFactors` (type: `array`):

PHONE and/or DESKTOP (applies to field and lab data).

## `includeField` (type: `boolean`):

28-day p75 LCP, INP, CLS, FCP, TTFB from the Chrome UX Report. Falls back to origin-level data when the URL has too little traffic.

## `includeLab` (type: `boolean`):

Performance score, lab metrics and top 5 opportunities. Turn off for cheap scheduled monitoring (field-only).

## `includeHistory` (type: `boolean`):

Weekly p75 series for one representative URL per site and template (origin fallback) and a trend label.

## `historyPeriods` (type: `integer`):

Number of weekly collection periods (1–40).

## `monitorId` (type: `string`):

Set a name (letters, digits, hyphens) to compare each run with the previous one and flag regressions. The snapshot is stored in your account in the key-value store "cwv-monitor-<id>".

## `regressionThresholds` (type: `object`):

p75IncreasePct: flag when a field p75 worsens by more than this % (default 10). labScoreDrop: flag when the lab score drops by more than this many points (default 15). A field metric moving to a worse category is always flagged.

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

Google Cloud API key with "Chrome UX Report API" and "PageSpeed Insights API" enabled. Removes the free-plan lab limit and uses your own Google quota. Stored encrypted; never logged.

## `healthCheck` (type: `boolean`):

Internal: runs a fixed known-good input and validates the output. Never charged.

## Actor input object example

```json
{
  "urls": [
    "https://web.dev/"
  ],
  "samplesPerTemplate": 3,
  "maxUrls": 100,
  "formFactors": [
    "PHONE"
  ],
  "includeField": true,
  "includeLab": true,
  "includeHistory": false,
  "historyPeriods": 25,
  "regressionThresholds": {
    "p75IncreasePct": 10,
    "labScoreDrop": 15
  },
  "healthCheck": false
}
```

# Actor output Schema

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

All URL × device records (field, lab, history, diff).

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

Records flagged as regressions compared with the previous run of the same Monitor ID.

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

CWV pass rate, median and worst p75, median lab score per site, template and device.

## `regressionList` (type: `string`):

Compact list of regressions, ready for Slack, email, n8n or Make alerts.

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

Counts, Google API calls, budget and stop reason for this 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://web.dev/"
    ],
    "regressionThresholds": {
        "p75IncreasePct": 10,
        "labScoreDrop": 15
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("benexe/core-web-vitals-monitor").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://web.dev/"],
    "regressionThresholds": {
        "p75IncreasePct": 10,
        "labScoreDrop": 15,
    },
}

# Run the Actor and wait for it to finish
run = client.actor("benexe/core-web-vitals-monitor").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://web.dev/"
  ],
  "regressionThresholds": {
    "p75IncreasePct": 10,
    "labScoreDrop": 15
  }
}' |
apify call benexe/core-web-vitals-monitor --silent --output-dataset

```

## MCP server setup

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

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/rUhlz4L4w16Vfo1bn/builds/3DsNu2a2KPuf0cHTw/openapi.json
