# Bulk PageSpeed Insights & Core Web Vitals Checker + Monitor (`tobbyb/pagespeed-core-web-vitals-monitor`) Actor

Core Web Vitals and PageSpeed bulk checker: run PageSpeed Insights (Lighthouse) on many URLs, a sitemap or a whole site. No API key needed. Real-user LCP, INP, CLS pass/fail with 6-month trends, top fixes, competitor comparison, Slack/Discord alerts and an HTML report.

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

## Pricing

from $4.50 / 1,000 page audit (per url, per device)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

## Bulk PageSpeed Insights & Core Web Vitals Monitor

**Check PageSpeed and real-user Core Web Vitals for a whole sitemap in minutes, and get a Slack, Discord or Teams alert when real users start seeing a slower site. $0.006 per page audit, no Google API key needed.**

| Proof | |
|---|---|
| Speed (same 5 URLs, mobile, 18 Oct 2026) | **53 s**, vs 145–195 s for the three other Apify PageSpeed/SEO Actors that returned results; two more returned 0 results (raw HTTP 429 errors) |
| Real-user data | The only Actor in that test that returned Chrome UX Report (CrUX) field data: LCP, INP, CLS with pass/fail and a 6-month trend |
| Alerts without lab noise | On a 30-URL monitoring run with the default settings, **0 alerts** fired. Alerting on Lighthouse lab scores raised 7 false alerts on the same pages, because lab scores moved by up to 18 points between runs |
| Price | $0.006 per page audit, no start fee, failed URLs free |

**Bulk PageSpeed Insights and Core Web Vitals checker for hundreds of URLs, a sitemap or a whole website. No API key needed.** Paste your URLs and click Start. For every page you get Google Lighthouse scores, **real-user Core Web Vitals (LCP, INP, CLS) with pass/fail and a 6-month trend**, the **top fixes ranked by time saved**, what makes LCP slow, a screenshot, and a link to the full PageSpeed report. Schedule it and it **alerts you in Slack, Discord or Teams when pages get slower**.

It uses Google's official APIs (PageSpeed Insights API v5, Chrome UX Report API and CrUX History API). No scraping, no proxies, no 4 GB headless browser.

### Use cases: who it's for

- **SEO and web agencies:** audit a client's whole sitemap in minutes, then send a polished HTML report and a Sheets-ready table.
- **Developers and site owners:** catch slow releases with scheduled monitoring. Alerts are based on real-user data, so lab noise doesn't trigger them.
- **Growth and sales teams:** use the **bulk Core Web Vitals checker for domains** (field data only, about $1 per 1,000 URLs) to qualify leads or benchmark a market.
- **Anyone comparing against competitors:** rank your pages against rival sites side by side.

### Quick start

Paste URLs or a site address and click **Start**. The same input as JSON:

```json
{
  "urls": ["https://web.dev/", "https://www.example.com/"],
  "sitemapUrls": ["https://www.example.com/"],
  "maxSitemapUrls": 50,
  "device": "mobile",
  "monitorName": "acme-weekly",
  "alertWebhookUrl": "https://discord.com/api/webhooks/…"
}
```

Leave `monitorName` and `alertWebhookUrl` empty for a one-off audit. A shortened real result is in [Sample output](#sample-output-real-run-webdev-mobile-3-lab-runs). From code:

```bash
curl -X POST "https://api.apify.com/v2/acts/tobbyb~pagespeed-core-web-vitals-monitor/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" -d '{"urls":["https://web.dev/"],"device":"mobile"}'
```

There is also an n8n community node: `n8n-nodes-pagespeed-core-web-vitals-monitor` ([GitHub](https://github.com/tobbytools/n8n-nodes-pagespeed-core-web-vitals-monitor)).

### What you get for each page

| | |
|---|---|
| **Scores** | Performance, Accessibility, Best Practices, SEO (Lighthouse 13) |
| **Core Web Vitals (real users)** | `coreWebVitals` PASSED / FAILED / NO_DATA. p75 LCP, INP, CLS, FCP, TTFB with good / needs-improvement / poor ratings and % of good visits. Page level, falling back to the whole site (origin) for low-traffic pages |
| **6-month trend** | Weekly real-user p75s for up to 40 weeks (CrUX History API), `improving / stable / worsening`, and how many weeks the page has been passing or failing |
| **Lab metrics** | LCP, FCP, TBT, CLS, Speed Index, TTI, main-thread work, JavaScript execution time. Optional **median of 2–5 runs** with min–max spread |
| **Why it's slow** | Top fixes with estimated **ms and KB saved**, LCP element and LCP breakdown (server / load delay / load time / render delay), layout-shift culprits, third-party cost, failed accessibility/SEO checks |
| **Context** | Screenshot, redirect chain, HTTP status, page weight, request count, DOM size, Lighthouse/Chrome version, link to the full PageSpeed Insights report |
| **Monitoring** | Status vs your previous runs (`new / stable / improved / regressed`), an exact list of what got worse for real users, and lab changes listed as info (`labChanges`) |

Every run also saves:

- a **shareable HTML report**: summary gauges, real-user distribution, site-wide top fixes, competitor ranking, page groups, expandable per-page details and errors with fixes;
- a `SUMMARY` JSON.

#### Sample output (real run, web.dev, mobile, 3 lab runs)

```json
{
  "url": "https://web.dev/", "device": "mobile",
  "performanceScore": 46, "accessibilityScore": 90, "bestPracticesScore": 96, "seoScore": 92,
  "coreWebVitals": "FAILED", "coreWebVitalsScope": "url",
  "fieldLcpMs": 3051, "fieldInpMs": 218, "fieldCls": 0.01,
  "fieldLcpTrend": "worsening", "cwvStreakWeeks": 25,
  "labLcpMs": 3935, "labTbtMs": 1165, "labCls": 0.118, "labRunsCount": 3, "performanceScoreRange": 14,
  "topOpportunity": "Render-blocking requests (~2300 ms)",
  "lcpBreakdown": { "ttfbMs": 4, "renderDelayMs": 648 },
  "thirdParties": { "count": 7, "top": [{ "entity": "Google Tag Manager", "transferKiB": 586, "mainThreadMs": 1363 }] },
  "opportunities": [{ "title": "Reduce unused CSS", "estimatedSavingsMs": 450, "estimatedSavingsKiB": 140 }],
  "monitoringStatus": "stable",
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/…/records/screenshot-mobile-…",
  "psiReportUrl": "https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fweb.dev%2F&form_factor=mobile"
}
```

### How to check Core Web Vitals for many URLs

1. Paste URLs (one per line). Or add a **sitemap**, or just your **site address**: the sitemap is found automatically through `robots.txt`.
2. Pick **Mobile**, **Desktop** or **Both**.
3. Click **Start**. Results stream into the dataset. Export to **CSV, Excel, JSON or Google Sheets** (use the *Flat table* view), or connect to Zapier, Make or n8n.

No Google API key is needed. See [Use your own Google API key](#use-your-own-google-api-key) for big jobs.

### Bulk Core Web Vitals checker (field data only)

Turn off **Run Lighthouse lab test** to fetch only real-user Chrome UX Report data. It takes about 1 second per URL, so thousands of URLs or competitor domains cost **$0.001 each**. Pages without enough Chrome traffic are **not charged**. The 6-month trend is included.

### Core Web Vitals monitoring with alerts

1. Set **Monitor name** (e.g. `acme-shop-weekly`).
2. Add a **Slack / Discord / Microsoft Teams webhook** (or any https endpoint for JSON).
3. Create an Apify **Schedule** (daily or weekly).

**What triggers an alert (default): real-user data only.** Each run compares every page's Chrome UX Report data with the previous run. You get an alert, and a `regressed` status, when:

- Core Web Vitals go from PASSED to FAILED,
- a real-user p75 (LCP, INP, CLS, FCP, TTFB) gets 10%+ worse,
- a real-user rating gets worse (e.g. good → needs improvement).

**Lab (Lighthouse) changes are shown but not alerted.** Each row lists them in `labChanges` / `labChangeSummary`, and the report shows them in an "info only" section, compared with the median of earlier runs. We made this the default after measuring it. Google's lab tests are noisy: on 30 unchanged sites, scores moved by up to 18 points between runs 10 minutes apart. Even with medians and two-run confirmation, lab-based alerting still raised 2–7 false alerts per run. Real-user data is a 28-day aggregate, so it doesn't jump around like that.

**Want lab alerts too?** Turn on **Also alert on lab changes**. *Lighthouse runs per page* then defaults to 3 (median of 3 runs, charged as extra lab runs), and a lab drop must repeat on two runs in a row before it alerts. One-off dips are shown as *suspected*.

**Overlapping runs.** Don't run two schedules with the same monitor name at the same time. If it happens anyway, the Actor detects the other run and logs a warning. When saving, it merges both runs' results page by page (newest test wins) instead of overwriting them, and records this in the summary (`monitorConcurrency`).

### Compare with competitors

Add competitor pages under **Competitor URLs**. The report and `SUMMARY.comparison` rank all sites by:

- performance,
- Core Web Vitals pass rate,
- real-user LCP / INP / CLS.

They also show how far you are from the best competitor.

### Page groups

Add groups like `Blog = /blog/` or `Products = ^/product/` to see pass rate, medians and the slowest page per template. Without groups, pages are grouped by their first folder.

### Use your own Google API key

Without a key, the Actor uses its own shared Google quota, limited to **200 page audits per run**. If the shared daily quota ever runs out, you get a clear `SHARED_QUOTA_EXHAUSTED` message (not charged). For big or frequent jobs, add your free key (2 minutes):

1. In the [Google Cloud console](https://console.cloud.google.com/), create a project.
2. Enable **PageSpeed Insights API** and **Chrome UX Report API**.
3. Go to Credentials → **Create credentials → API key**, and paste it into **Google API key**. It is stored encrypted and never appears in the output.

Your key gets about 25,000 PageSpeed audits per day. The CrUX API allows 150 requests per minute. The Actor rate-limits itself and retries with back-off.

### Errors never stop the run and are never charged

Each URL that can't be audited becomes a row with `errorCode`, a plain-English `errorTitle` and a concrete `howToFix`. Examples:

| errorCode | errorTitle | howToFix |
|---|---|---|
| `HTTP_404` | Page not found (404) | Fix or remove the link, or audit the URL it moved to |
| `NOT_HTML` | Not a web page | PDFs, images and JSON are skipped |
| `DNS_FAILURE` | Domain does not exist | Check the spelling |
| `BLOCKED_BY_INTERSTITIAL` | Blocked by a bot check | Allow-list PageSpeed in your firewall |

Others include `TIMEOUT`, `PSI_UNABLE_TO_PROCESS`, `SITEMAP_ERROR` and `SHARED_QUOTA_EXHAUSTED`. 404s, non-HTML files and dead domains are caught by a quick pre-check **before** a PageSpeed call is spent. PageSpeed itself happily scores a 404 page as "100", and we catch that too.

### How it compares

| | This Actor | pagespeed.web.dev | Search Console Core Web Vitals report | Other Apify PageSpeed Actors (our 5-URL test) |
|---|---|---|---|---|
| Many URLs or a sitemap in one go | Yes (up to 500 URLs per run; 200 audits per run on the shared key) | One URL at a time | Your own verified sites only, grouped by URL pattern | Yes |
| Real-user (CrUX) Core Web Vitals | Yes, page and origin, with a 6-month trend | Yes, one URL at a time | Yes | None returned it |
| Lighthouse lab scores and fixes | Yes, top fixes with ms/KB saved | Yes | No | 16 and 49 fields in the two that ran Lighthouse |
| Competitor sites | Yes, ranked side by side | One at a time | Your own sites only | – |
| Webhook alerts when real users get a slower site | Slack, Discord, Teams or any webhook | No | – | – |
| Export | CSV, Excel, JSON, Google Sheets, HTML report | No bulk export | – | Dataset |
| Errors (404, bot wall, quota) | Typed error item with a fix, not charged | Error message | – | 2 of 5 returned raw 429 errors and 0 results |

### Pricing (pay per event)

| Event | Price |
|---|---|
| Page audit (one URL on one device: lab + real-user data + trend) | **$0.006** |
| Extra Lighthouse run for the median (only if runs per page > 1) | $0.003 |
| Field-data-only lookup (free when the page has no CrUX data) | $0.001 |
| Regression detected (monitoring) | $0.002 |
| HTML report (once per run, optional) | $0.01 |

There is no start fee and failed URLs are free. Examples:

- 500 URLs × mobile = **$3.00**
- weekly monitoring of 100 URLs × both devices ≈ **$1.21** per run
- 1,000 domains, field data only ≈ **$1**

### FAQ

**Why do PageSpeed scores change between runs?** Lighthouse runs in a simulated slow phone on Google's servers, and results vary (we saw ±18 points). Use *Lighthouse runs per page* (median) and rely on the real-user Core Web Vitals, which are 28-day aggregates.

**Lab vs field data, which one matters?** Google ranks with **field data** (real Chrome users, CrUX). Lab data explains *why* a page is slow. You get both.

**Why does a page show `NO_DATA`?** CrUX only covers pages with enough Chrome traffic. Then the whole site's (origin) data is used and marked `coreWebVitalsScope: "origin"`.

**Is INP available in the lab?** No. INP needs real interactions, so it comes from CrUX. TBT is the lab proxy.

**Why is lab TTFB sometimes 2–5 ms?** Google measures it from its own datacenter, often next to your CDN. Use field TTFB for the real-world value.

**Do I need a Google API key?** No. Runs use a shared Google quota, up to 200 page audits per run. For bigger or frequent jobs, add your own free key (see above).

**How do I get alerts in Discord or Slack?** Set **Monitor name** and **Alert webhook URL**, then create an Apify Schedule. Alerts fire on real-user regressions by default.

**Can I audit pages behind a login?** No. Google must be able to load the page publicly.

*Not affiliated with or endorsed by Google. PageSpeed Insights, Lighthouse and Chrome UX Report are Google products, used through their public APIs.*

# Actor input Schema

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

Pages to test, one URL per line (use Bulk edit to paste hundreds). Bare domains like example.com get https:// added. Duplicates are removed. Invalid entries become error items and never stop the run.

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

Sitemap URLs to expand into pages (sitemap indexes, .xml.gz and plain-text sitemaps are supported, and huge sitemaps are streamed). You can also enter just a site, e.g. https://www.example.com: the Actor finds its sitemap through robots.txt. A broken sitemap URL also falls back to robots.txt.

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

Upper limit of page URLs taken from all sitemaps together (keeps costs predictable).

## `sitemapIncludePattern` (type: `string`):

Only take sitemap URLs matching this regular expression, e.g. /blog/

## `sitemapExcludePattern` (type: `string`):

Skip sitemap URLs matching this regular expression, e.g. .pdf$|/tag/

## `device` (type: `string`):

Run the audit for mobile, desktop, or both. 'Both' produces two results (and two charges) per URL.

## `competitorUrls` (type: `array`):

Optional. Competitor pages to test the same way, e.g. their homepage and key landing pages. Results get role = competitor, and the report and SUMMARY rank every site side by side (performance, Core Web Vitals pass rate, real-user LCP/INP). Competitors are charged like your pages and are not monitored for regressions.

## `pageGroups` (type: `array`):

Optional. Group pages by template to see which part of the site is slow, one per line as Name = regex, e.g. 'Blog = /blog/' or 'Products = ^/(p|product)/'. The first match wins. Without groups, pages are grouped by the first folder of the path. Rollups (pass rate, medians, worst page) are in SUMMARY and the report.

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

Lighthouse categories to score. All four cost the same.

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

Not needed: without a key the Actor uses its own shared key, limited to 200 page audits per run. Add your own free key (PageSpeed Insights API + Chrome UX Report API enabled) for big jobs, frequent schedules or your own ~25,000 audits/day. It is stored encrypted and never appears in the output. See README → 'Use your own Google API key'.

## `includeFieldData` (type: `boolean`):

Adds Chrome UX Report p75 LCP, INP, CLS, FCP and TTFB for the page and its origin, plus the Core Web Vitals pass/fail assessment.

## `includeFieldHistory` (type: `boolean`):

Adds weekly real-user p75 LCP, INP, CLS, FCP and TTFB for up to 40 weeks from the Chrome UX Report History API, with a trend (improving / stable / worsening), the current pass/fail streak and when it started. Free, about 1 extra second per page.

## `fieldHistoryWeeks` (type: `integer`):

How many weekly data points to fetch (each one is a 28-day window). 25 ≈ 6 months, 40 = maximum.

## `labData` (type: `boolean`):

Turn off for the fast, low-cost bulk Core Web Vitals checker: real-user Chrome UX Report data only (no Lighthouse), about 1 second per URL, ideal for checking thousands of pages or competitor domains. Pages without CrUX data are not charged.

## `labRuns` (type: `integer`):

Lab scores vary from run to run (we measured up to 18 points on unchanged pages). Run Lighthouse 2–5 times per page and report the MEDIAN of every score and metric plus the min–max spread (labRuns.spread). Default: 1, or 3 when monitoring with lab alerts turned on. Each extra run is charged as 'extra lab run'.

## `monitorName` (type: `string`):

Give this URL set a name, e.g. 'acme-shop-weekly', and schedule the Actor. Each run is compared with earlier runs of the same monitor (kept in a named key-value store). By default you are alerted when real-user Core Web Vitals get worse; lab changes are listed as info. Don't run two schedules with the same name at the same time: the Actor detects overlap, warns and merges instead of overwriting.

## `labAlerts` (type: `boolean`):

Off by default: alerts and the 'regression' charge come only from real-user Chrome UX Report data (field p75 LCP/INP/CLS/FCP/TTFB getting worse, a rating downgrade, or Core Web Vitals PASSED → FAILED). Lab score changes still appear in every row as info (labChanges, labChangeSummary). Turn this on to also alert on lab drops; then 'Lighthouse runs per page' defaults to 3 (median) and a lab drop must repeat on 2 runs in a row. Why off: Google's lab tests are noisy. In our live tests on 30 unchanged sites, lab-based alerting still raised 2–7 false alerts per run.

## `scoreDropThreshold` (type: `integer`):

Only used when 'Also alert on lab changes' is on (it always shapes the info-only labChanges list). Flag a regression when a Lighthouse score (0–100) drops by at least this many points. In live tests, PageSpeed lab scores for the same page varied by a median of 2 and up to 18 points between runs 10 minutes apart.

## `labMetricThresholdPct` (type: `integer`):

Only used when 'Also alert on lab changes' is on (it always shapes the info-only labChanges list). Flag a regression when a lab metric (LCP, FCP, TBT, CLS, Speed Index) gets worse by at least this percentage and by a minimum absolute amount (LCP/FCP +500 ms, TBT +150 ms, CLS +0.05, Speed Index +1 s). Lab TTFB is not alerted on: Google measures it from its own datacenter.

## `labRequireRatingChange` (type: `boolean`):

Only used when 'Also alert on lab changes' is on (it always shapes the info-only labChanges list). When on (recommended), a lab metric only counts as a regression if its rating also gets worse (good → needs improvement → poor). PageSpeed lab data is noisy. In live tests, turning this off flagged 14 of 46 unchanged pages between two runs; with it on, 4 of 46.

## `confirmLabRegressions` (type: `boolean`):

Only used when 'Also alert on lab changes' is on (it always shapes the info-only labChanges list). Recommended. Lab results are compared with the median of your earlier runs. A lab-only drop must show up in two runs in a row before it is alerted and charged; a one-off dip is listed as 'suspected' instead. Field (real-user) data and Core Web Vitals pass→fail changes always alert at once. In 3 live test runs of 30 unchanged sites this cut false alerts from 19 to 2.

## `fieldMetricThresholdPct` (type: `integer`):

Flag a regression when a real-user p75 metric gets worse by at least this percentage. A Core Web Vitals pass→fail or a good→poor rating change is always flagged.

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

Get a message when pages get slower. Paste a Slack Incoming Webhook (hooks.slack.com/…), a Discord webhook (discord.com/api/webhooks/…), a Microsoft Teams Workflows webhook, or any https endpoint (it receives JSON). Stored encrypted.

## `alertOn` (type: `string`):

When the webhook is called. Without a monitor name there are no regressions, so use one of the other options.

## `generateHtmlReport` (type: `boolean`):

Saves a shareable HTML report (key REPORT in the run's key-value store). Charged once per run.

## `saveScreenshots` (type: `boolean`):

Saves the above-the-fold screenshot Lighthouse took (about 20 KB JPEG per page) to the run's key-value store. The output gets a `screenshotUrl` and the HTML report shows thumbnails. Free.

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

How many improvement opportunities (with estimated savings) to include per result.

## `maxFailedAudits` (type: `integer`):

How many failing accessibility / SEO / best-practice audits to list per result.

## `saveFullResults` (type: `boolean`):

Also store the complete raw PageSpeed Insights response for each result in the key-value store (large).

## `preflightCheck` (type: `boolean`):

Before calling PageSpeed, quickly fetch the URL headers: records the redirect chain and status, and skips (without charging) URLs that are PDFs/images/JSON, return 404/410, or have a non-existent domain.

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

Locale for Lighthouse audit titles, e.g. en, de, fr, es, pt-BR.

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

Safety cap on unique URLs per run (before multiplying by devices).

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

How many PageSpeed audits run at the same time. Without an API key this is forced to 1.

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

Client-side rate limit to stay inside Google's per-minute quota. 429 responses are retried with exponential backoff anyway.

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

Retries for transient failures (rate limits, timeouts, Google 5xx, flaky page loads). Permanent errors such as 404 or non-HTML are not retried.

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

How long to wait for one PageSpeed Insights response. Heavy pages can take PSI 60-150 s; a timed-out URL is retried once.

## Actor input object example

```json
{
  "urls": [
    "https://web.dev/",
    "https://developer.mozilla.org/en-US/"
  ],
  "maxSitemapUrls": 50,
  "device": "mobile",
  "categories": [
    "performance",
    "accessibility",
    "best-practices",
    "seo"
  ],
  "includeFieldData": true,
  "includeFieldHistory": true,
  "fieldHistoryWeeks": 25,
  "labData": true,
  "labAlerts": false,
  "scoreDropThreshold": 15,
  "labMetricThresholdPct": 25,
  "labRequireRatingChange": true,
  "confirmLabRegressions": true,
  "fieldMetricThresholdPct": 10,
  "alertOn": "regressions",
  "generateHtmlReport": true,
  "saveScreenshots": true,
  "maxOpportunities": 10,
  "maxFailedAudits": 10,
  "saveFullResults": false,
  "preflightCheck": true,
  "locale": "en",
  "maxUrls": 500,
  "maxConcurrency": 5,
  "maxRequestsPerMinute": 60,
  "maxRetries": 3,
  "requestTimeoutSecs": 180
}
```

# Actor output Schema

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

All pages: scores, Core Web Vitals pass/fail, real-user and lab metrics, top fix, screenshot.

## `sheets` (type: `string`):

Same rows with only flat columns, ready for Sheets, Excel or Looker Studio.

## `report` (type: `string`):

Shareable report with summary, top fixes, competitors, page groups, per-page details and errors.

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

Averages, pass rate, regressions, top fixes, page groups, competitor ranking, alert status.

## `screenshots` (type: `string`):

Above-the-fold screenshot per page and device.

# 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/",
        "https://developer.mozilla.org/en-US/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tobbyb/pagespeed-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/",
        "https://developer.mozilla.org/en-US/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("tobbyb/pagespeed-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/",
    "https://developer.mozilla.org/en-US/"
  ]
}' |
apify call tobbyb/pagespeed-core-web-vitals-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tobbyb/pagespeed-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/FfIfT9SUkpzUeZt4r/builds/yfbgLGvEb8uGik4ms/openapi.json
