# PageSpeed Insights & Core Web Vitals Checker: Causes & Fixes (`crawlwise/pagespeed-core-web-vitals`) Actor

Bulk PageSpeed Insights and Lighthouse website speed test for Core Web Vitals: real-user LCP, INP and CLS pass or fail, why each page fails (LCP element, layout shifts, slow third-party scripts), a 3-month trend and fixes ranked for the failing metric.

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

## Pricing

from $2.80 / 1,000 page tests

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

Test a list of pages on **Google PageSpeed Insights** and get, for each one, more than a score: does it **pass Core Web Vitals for real visitors**, **why it fails**, **which fix helps the metric that fails**, and **whether it is getting better or worse**. Mobile and desktop in the same row.

Most PageSpeed tools stop at "LCP 4.1 s, score 62". This one also tells you *which element* is the LCP, where its time goes (server, load delay, download, render delay), which elements cause layout shift, and which third-party scripts (tag managers, chat widgets, pixels) cost the most.

### What you get for every URL

| Field | What it tells you |
|---|---|
| `summary` | One plain-English verdict per device, ready for a client report: pass or fail, by how much, the main cause, the first fix, and a worsening trend |
| `coreWebVitals` | `pass`, `fail` or `no-data` for **real Chrome visitors** (LCP, INP, CLS at the 75th percentile, last 28 days). This is what Google uses in ranking |
| `fieldUrl` | The address the real-user data belongs to. Google looks it up by the address it actually tested, which can differ from the one you gave (a redirect to a mobile or country site) |
| `fieldScope` | `page`, or `origin` when the page has too little traffic and Google reports the whole site instead. Said openly, never mixed up |
| `failing` | Each failing metric with its value, the "good" limit and **the gap to close**, e.g. LCP 3.4 s, limit 2.5 s, gap 900 ms |
| `lcp` | **The LCP element** (CSS selector and text) and its time breakdown: time to first byte, resource load delay, resource load duration, element render delay. The biggest part is named |
| `layoutShifts` | The elements that move while the page loads, with their shift score |
| `thirdParties` | Third-party code ranked by main-thread time and size. The site's own CDN on another domain (e.g. ebaystatic.com on ebay.com) is left out: it is not a third party you can remove |
| `labBlocked` | `true` when bot protection showed Google's lab test a check page instead of the site. The real-user verdict still holds; the lab causes do not, and the summary says so |
| `fixes` | Lighthouse fixes with estimated savings, **the ones that improve your failing metric first**, each with `fixesFailing` |
| `trend` | From the Chrome UX Report History: LCP, INP and CLS now versus about 3 months ago, `better`, `worse` or `stable` |
| `lab` | Lighthouse performance, accessibility, best practices and SEO scores; LCP, FCP, TBT, CLS, Speed Index, server response time, page weight, requests |
| `failedSeoChecks` | Lighthouse SEO checks the page fails |
| `reportUrl` | The same test on pagespeed.web.dev |

The table view shows the verdict, the failing metrics and the score per device. A second view lists causes and fixes for mobile. The run summary (`SUMMARY` in the key-value store) counts pages that pass or fail and lists the fixes that come up most often across your list.

### Example

Real result for `https://www.python.org`, run on Apify on 18 October 2026, desktop part shortened:

```json
{
  "url": "https://www.python.org/",
  "summary": "Mobile passes Core Web Vitals for real visitors. Desktop fails Core Web Vitals for real visitors: CLS 0.13 (limit 0.1). The biggest layout shift is body#homepage > div#touchnav-wrapper > div#content (0.122).",
  "mobileCoreWebVitals": "pass",
  "desktopCoreWebVitals": "fail",
  "desktopFailing": "CLS",
  "desktop": {
    "coreWebVitals": "fail",
    "fieldScope": "page",
    "field": {
      "LCP": {
        "p75": 1876,
        "rating": "good"
      },
      "INP": {
        "p75": 17,
        "rating": "good"
      },
      "CLS": {
        "p75": 0.13,
        "rating": "needs-improvement"
      }
    },
    "failing": [
      {
        "metric": "CLS",
        "source": "field",
        "value": 0.13,
        "rating": "needs-improvement",
        "good": 0.1,
        "gap": 0.03
      }
    ],
    "trend": {
      "scope": "page",
      "from": "2026-07-18",
      "to": "2026-10-10",
      "LCP": {
        "then": 2395,
        "now": 2178,
        "changePct": -9,
        "direction": "stable"
      },
      "INP": {
        "then": 14,
        "now": 17,
        "changePct": 21,
        "direction": "stable"
      },
      "CLS": {
        "then": 0.13,
        "now": 0.13,
        "changePct": 0,
        "direction": "stable"
      }
    },
    "lab": {
      "performance": 94,
      "seo": 92,
      "lcpMs": 1030,
      "cls": 0.124
    },
    "lcp": {
      "selector": "li.flex-active-slide > div.slide-code > pre > code",
      "breakdownMs": {
        "timeToFirstByte": 3,
        "elementRenderDelay": 452
      },
      "biggestPart": {
        "part": "elementRenderDelay",
        "ms": 452
      }
    },
    "layoutShifts": [
      {
        "selector": "body#homepage > div#touchnav-wrapper > div#content",
        "element": "Get Started\n\nWhether you're new to programming or an experienced developer, it'…",
        "score": 0.122
      }
    ],
    "thirdParties": [
      {
        "name": "Google CDN",
        "mainThreadMs": 99,
        "transferKb": 110
      }
    ],
    "fixes": [
      {
        "id": "render-blocking-insight",
        "title": "Render-blocking requests",
        "estimate": "Est savings of 570 ms",
        "savingsMs": {
          "LCP": 550,
          "FCP": 550
        },
        "savingsKb": null,
        "fixesFailing": []
      }
    ]
  },
  "reportUrl": "https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fwww.python.org%2F&form_factor=mobile"
}
```

The page passes on mobile, but on desktop real visitors see a layout shift of 0.13, above the 0.1 limit. The row names the element that moves (`div#content`, 0.122 of the 0.124 measured in the lab). Note that the biggest Lighthouse saving (render-blocking requests) is **not** marked as fixing the failing metric: it speeds up LCP, which already passes. The trend shows CLS has stayed at 0.13 for three months, so it is not a one-off.

### Where the data comes from

Only Google's official APIs: **PageSpeed Insights API v5** (Lighthouse run on Google's servers, plus real-user data from the Chrome UX Report) and the **Chrome UX Report History API** for the trend. No scraping, no browser on our side. The same test as pagespeed.web.dev. The Actor has its own Google API key, so you do not need one.

**Lab vs field.** Lab data is one page load on a simulated device: it shows the causes and moves a few points between runs. Field data is what real visitors experienced over 28 days: it decides pass or fail. A page can score 77 in the lab and still pass for real users, or the other way round, so both are returned. Google only has field data for pages and sites with enough Chrome traffic; for a small site the verdict is `no-data` and the row falls back to the lab numbers, saying so. Since INP cannot be measured in a lab, Total Blocking Time stands in for it there.

### Input

| Field | Default | |
|---|---|---|
| `urls` | `["https://www.python.org"]` | Up to 500 pages per run. Bare domains work |
| `devices` | `both` | `both`, `mobile` (what Google ranks on) or `desktop` |
| `trend` | `true` | Add the 3-month trend. No extra charge |
| `apiKey` | empty | Optional: your own Google API key, for very large lists |

```json
{ "urls": ["https://www.python.org", "https://www.bbc.com/news"], "devices": "both", "trend": true }
```

### Pricing

**$0.004 per page test** (one page on one device with a complete report). Mobile and desktop for one page is two tests, $0.008. Lower on Bronze, Silver and Gold plans. The trend and the run summary are free. Pages Google cannot load, rate limits and timeouts are **not charged**; the row says why.

1,000 pages on mobile cost about $4. Google takes 10 to 60 seconds per test; the Actor runs up to 8 tests at a time.

### Use it for

- **SEO audits and client reports**: paste the `summary` into the report; the `fixes` with `fixesFailing` are the to-do list.
- **Prospecting**: run a list of sites, keep the ones that fail Core Web Vitals, and open with the exact cause.
- **Before and after a change**: the `trend` shows whether a redesign, a new host or a plugin clean-up moved real-user numbers.
- **AI agents**: one call, structured JSON with a verdict sentence and ranked fixes. Available through the [Apify MCP server](https://mcp.apify.com).

### API

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

### FAQ

**Why does my score differ from pagespeed.web.dev by a few points?** Every Lighthouse run is a fresh load; scores move between runs on Google's own site too. Use the field verdict and the failing metrics for decisions.

**Why is the verdict `no-data`?** Google publishes real-visitor data only for pages and sites with enough Chrome traffic. The lab numbers and causes are still there.

**Why is there no trend?** Same reason: the history needs enough traffic. When the page has none, the trend uses the whole site and says `scope: origin`.

**Why does the summary say the lab test got a bot-check page?** Some sites (with DataDome, for example) answer Google's lab browser with a captcha. The real-visitor data is unaffected, so the pass or fail verdict and the trend are right, but the element, shift and fix details would describe the captcha page, so they are not presented as causes.

**A page came back with an error.** Google could not load it (site down, blocking Google, not a web page). The reason is in `errorMessage`, and it was not charged.

### Also from Crawlwise

[Website SEO Audit Tool & SEO Checker with AI Search Check](https://apify.com/crawlwise/seo-audit-ai-search): title, meta description, H1, canonical, noindex, JSON-LD, and whether ChatGPT, Claude and Perplexity bots can read the page. Run both on the same list for a full technical check.

Found a problem or need a field? Open an issue on the Issues tab.

# Actor input Schema

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

Pages to test, one per line. Each URL is tested as one page on Google PageSpeed Insights. Up to 500 URLs per run. A bare domain like example.com is read as https://example.com.

## `devices` (type: `string`):

Mobile is what Google ranks on. Both gives mobile and desktop side by side in one row (two page tests).

## `trend` (type: `boolean`):

From Google's Chrome UX Report History: is LCP, INP or CLS getting better or worse for real visitors? Free, no extra charge. Needs enough Chrome traffic on the page or the site.

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

Not needed: the Actor has its own key. Add yours only for very large lists, so tests count against your own free daily Google allowance. Enable 'PageSpeed Insights API' (and 'Chrome UX Report API' for trends) on it.

## Actor input object example

```json
{
  "urls": [
    "https://www.python.org"
  ],
  "devices": "both",
  "trend": true
}
```

# Actor output Schema

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

One row per URL: Core Web Vitals verdict for real visitors, what fails and by how much, the cause (LCP element, layout shifts, third parties), fixes and trend, for mobile and desktop.

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

How many pages pass or fail Core Web Vitals and the fixes that come up most often across the list.

# 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://www.python.org"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlwise/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://www.python.org"] }

# Run the Actor and wait for it to finish
run = client.actor("crawlwise/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://www.python.org"
  ]
}' |
apify call crawlwise/pagespeed-core-web-vitals --silent --output-dataset

```

## MCP server setup

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