# PageSpeed & Core Web Vitals Monitor (`isnard/pagespeed-monitor`) Actor

Run Google PageSpeed Insights on a list of URLs (mobile, desktop or both). Get the performance score, LCP, CLS, TBT, FCP, TTFB, real-user Core Web Vitals, top fixes, and a regression flag versus the previous run.

- **URL**: https://apify.com/isnard/pagespeed-monitor.md
- **Developed by:** [Matias Isnard](https://apify.com/isnard) (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.00 / 1,000 url auditeds

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

## PageSpeed & Core Web Vitals Monitor

Give it a list of URLs. For each URL and device (mobile, desktop or both) it runs one Google PageSpeed Insights audit and returns:

- the Lighthouse performance score (0-100), plus accessibility, best practices and SEO scores if you ask for them
- lab values from that run: LCP, CLS, TBT, FCP, TTFB (server response time) and Speed Index
- real-user Core Web Vitals from the Chrome UX Report (LCP, CLS, INP, FCP, TTFB at the 75th percentile), for the URL and for the whole origin, when Google has enough traffic data
- the 5 failed performance audits with the largest estimated time savings
- the change since the previous run for the same URL and device, and a `regressed` flag when the score dropped by more than your threshold

Run it on a schedule and you have a simple performance monitor: filter the dataset on `regressed = true` to see which pages got slower.

The audits run on Google's servers through the official PageSpeed Insights API v5. The Actor does not load the pages itself.

### Input

```json
{
    "urls": ["https://example.com/", "https://example.com/pricing"],
    "apiKey": "YOUR_OWN_KEY",
    "strategy": "both",
    "categories": ["performance", "seo"],
    "regressionThreshold": 5
}
```

- `urls` (required): pages to audit. `https://` is added when missing. Duplicates are audited once. Invalid lines are skipped with a warning and are not charged.
- `apiKey`: your own PageSpeed Insights API key (free, see below). It is a secret input, stored encrypted, and only sent to Google. Optional, but requests without a key share one small global quota and usually fail with HTTP 429.
- `strategy`: `mobile` (default), `desktop` or `both`. `both` means two audits and two results per URL.
- `categories`: performance is always included. Add `accessibility`, `best-practices` or `seo` to get those scores too.
- `regressionThreshold` (default 5): points of performance score the page may lose before `regressed` becomes true. Lighthouse scores move a few points between runs even when nothing changed, so values below 5 give false alarms.

### How to get a free PSI API key

1. Open https://console.cloud.google.com/ and sign in with a Google account.
2. Create a project (top bar, project picker, New project). Any name works.
3. Go to APIs & Services, Library, search for "PageSpeed Insights API" and click Enable.
4. Go to APIs & Services, Credentials, Create credentials, API key. Copy the key.
5. Recommended: click the new key, under API restrictions choose "Restrict key" and select only PageSpeed Insights API. Save.
6. Paste the key into the `apiKey` field of this Actor.

The default quota is shown in the Cloud Console under APIs & Services, PageSpeed Insights API, Quotas (commonly 25,000 queries per day per project, check yours there).

### Output

One dataset item per URL and device. Shortened example:

```json
{
    "url": "https://example.com/",
    "strategy": "mobile",
    "audited_at": "2026-10-29T06:00:12+00:00",
    "final_url": "https://example.com/",
    "lighthouse_version": "13.6.0",
    "performance_score": 72,
    "scores": { "performance": 72, "seo": 92 },
    "lab": { "lcp_ms": 3850, "cls": 0.02, "tbt_ms": 310, "fcp_ms": 1900, "ttfb_ms": 420, "speed_index_ms": 3100 },
    "field_url": {
        "id": "https://example.com/",
        "overall_category": "AVERAGE",
        "lcp_ms": { "p75": 2900, "category": "AVERAGE" },
        "cls": { "p75": 0.05, "category": "FAST" },
        "inp_ms": { "p75": 180, "category": "FAST" },
        "fcp_ms": { "p75": 1700, "category": "FAST" },
        "ttfb_ms": { "p75": 900, "category": "AVERAGE" }
    },
    "field_origin": { "...": "same shape, for the whole origin" },
    "opportunities": [
        { "id": "image-delivery-insight", "title": "Improve image delivery", "savings_ms": 1200, "savings_bytes": null, "display_value": "Est savings of 450 KiB" }
    ],
    "warnings": [],
    "previous_score": 81,
    "previous_audited_at": "2026-10-22T06:00:09+00:00",
    "score_delta": -9,
    "lab_delta": { "lcp_ms": 900, "cls": 0.0, "tbt_ms": 120, "fcp_ms": 150, "ttfb_ms": 30, "speed_index_ms": 400 },
    "regressed": true,
    "errors": []
}
```

Notes on the fields:

- Lab data has TBT instead of INP: INP needs real user interaction, so it only exists in field data.
- `field_url` and `field_origin` are null when the Chrome UX Report has no data (common for small sites and new pages). When Google only has origin data, `field_url` is null and `field_origin` is filled.
- `savings_ms` is Lighthouse's own estimate. Opportunities overlap, so the savings do not add up.
- On the first run for a URL and device, `previous_score` and `score_delta` are null and `regressed` is false.

Problems are reported in `errors` for that URL instead of failing the run.

### Comparison with the previous run

After each successful audit the Actor saves the score and lab values in a named key-value store called `pagespeed-monitor-history` in your Apify account, one record per URL and device. The next run reads it to compute `score_delta`, `lab_delta` and `regressed`. Only the last run is kept. Delete the store to start over.

### Scheduling example

1. In Apify Console open this Actor and fill in the input (URLs, your API key, strategy). Click Save as a new task.
2. Go to Schedules, Create new schedule. Cron expression `0 6 * * 1` runs every Monday at 06:00 (UTC by default, you can pick a time zone).
3. Add the task to the schedule and save.
4. Optional: in the task's Integrations tab add an email or Slack integration on "Run succeeded", then open the run's dataset and filter on `regressed`.

You can also call the dataset API with `?clean=true&fields=url,strategy,performance_score,score_delta,regressed` to get a small table for a dashboard or a spreadsheet.

### Errors, retries and quota

- HTTP 429 (per-minute limit) and 5xx responses are retried up to 3 times, waiting 10, 30 and 90 seconds (or what the `Retry-After` header says).
- When the daily quota is used up, or the key is invalid or not enabled, the remaining URLs are not sent to Google. They get a result with the reason in `errors`.
- A page Lighthouse cannot load (DNS error, 404, timeout on Google's side) returns an error from the API. It is reported for that URL and not charged.
- At most 4 audits run at the same time.

### Pricing

Pay per event: you pay for each URL and device that was audited successfully (`url-audited` event). Failed audits and skipped URLs are not charged. Using your own API key costs nothing on the Google side within the free quota. You can cap the cost of a run with the "Maximum cost per run" setting; the Actor stops sending new audits when the cap is reached.

### Data source and terms

Results come from the Google PageSpeed Insights API and are subject to the Google APIs Terms of Service (https://developers.google.com/terms). The requests are made with your API key, so the usage counts against your own Google Cloud project. This Actor is not made by, affiliated with or endorsed by Google.

Google has announced that it plans to stop including Chrome UX Report field data in the PageSpeed Insights API, without a date (https://developers.google.com/speed/docs/insights/v5/get-started). When that happens `field_url` and `field_origin` will be null and the lab values, scores and opportunities keep working.

### Limitations

- Lab values come from a single Lighthouse run on Google's servers, with simulated throttling. They vary between runs. Use the threshold, and look at trends over several runs before acting.
- Pages behind a login, on a private network or blocking Google's crawler cannot be audited.
- Field data is a rolling 28-day window, so it changes slowly after a fix.
- The history keeps only the previous run, not a full time series. Export the datasets if you want long-term charts.

### Run locally

```bash
python -m venv .venv
.venv/Scripts/pip install -r requirements.txt   # on Linux/macOS: .venv/bin/pip
## put input in storage/key_value_stores/default/INPUT.json
.venv/Scripts/python -m my_actor
python -m unittest discover -s tests -t .
```

Author: Matias Isnard.

# Actor input Schema

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

Pages to audit, one per line. https:// is added when the scheme is missing. Duplicates are audited once.

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

Your own free key from Google Cloud (see the README). Optional, but without a key requests share a small global quota and often fail with HTTP 429. The key is stored encrypted and is only sent to Google.

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

Run the audit as a mobile device, as a desktop, or both (two audits and two results per URL).

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

Performance is always audited. Add other categories to get their 0-100 scores too. Each extra category makes the audit a bit slower.

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

A result gets regressed = true when the performance score dropped by more than this many points since the previous run for the same URL and device. Lighthouse scores vary a few points between runs, so 5 or more avoids false alarms.

## Actor input object example

```json
{
  "urls": [
    "https://example.com/",
    "https://www.wikipedia.org/"
  ],
  "strategy": "mobile",
  "categories": [
    "performance"
  ],
  "regressionThreshold": 5
}
```

# Actor output Schema

## `results` (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://example.com/",
        "https://www.wikipedia.org/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("isnard/pagespeed-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://example.com/",
        "https://www.wikipedia.org/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("isnard/pagespeed-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://example.com/",
    "https://www.wikipedia.org/"
  ]
}' |
apify call isnard/pagespeed-monitor --silent --output-dataset

```

## MCP server setup

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