# PageSpeed & Lighthouse Batch: Core Web Vitals Checker (`kilndata/pagespeed-lighthouse-batch`) Actor

Run Google PageSpeed Insights on a list of URLs. Lighthouse scores (performance, accessibility, best practices, SEO), LCP, CLS, TBT, FCP, Speed Index, real-user field data and the top 5 fixes, for mobile and desktop. Failed URLs are free.

- **URL**: https://apify.com/kilndata/pagespeed-lighthouse-batch.md
- **Developed by:** [Datakiln](https://apify.com/kilndata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 url analyzeds

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 & Lighthouse Batch: Core Web Vitals Checker

Paste a list of URLs and get back, for each one, the four Lighthouse scores (performance, accessibility, best practices, SEO), the main lab metrics (LCP, CLS, TBT, FCP, Speed Index), real-user field data when Google has it, and the five fixes with the biggest estimated saving. Mobile, desktop or both.

It calls the official Google PageSpeed Insights API (`pagespeedonline/v5/runPagespeed`) and passes Google's Lighthouse results through, so the numbers match what pagespeed.web.dev shows.

- One row per URL and device, in a flat table you can open in Excel or Google Sheets.
- Bring your own Google API key for your own quota, or run without one on Google's shared free quota (small, see Limits).
- Retries 429 and 5xx errors with backoff, and honors `Retry-After`.
- Stops by itself when your max cost per run is reached.
- Failed, timed-out and rate-limited analyses are never charged.
- Your API key is never written to the log or the dataset.

### What can I use it for?

| Use | How |
|---|---|
| SEO audits for a client | List the home page, key landing pages and top blog posts; compare the four scores in one table. |
| Before/after checks | Run the same list after a release and compare `performanceScore`, `lcpMs` and `cls`. |
| Monitoring | Schedule the Actor weekly and send the dataset to a sheet or a webhook. |
| Competitor benchmarks | Put your pages and competitors' pages in one run, mobile and desktop. |
| Prospecting | Find slow sites worth pitching: sort by `performanceScore`, read `opportunities`. |
| AI agents and automation | Call it through the Apify API or MCP and let an agent read the JSON. |

### How to use it

1. Paste the URLs in **Page URLs**, one per line.
2. Pick **Device**: mobile, desktop, or both.
3. Optional but recommended for more than a handful of URLs: paste your own **Google API key** (see the FAQ).
4. Start the run and open the **Scores** view.

#### Input example

```json
{
    "urls": ["https://example.com/", "https://www.wikipedia.org/"],
    "strategy": "both",
    "categories": ["performance", "accessibility", "best-practices", "seo"],
    "apiKey": "YOUR_OWN_KEY"
}
```

| Field | Default | What it does |
|---|---|---|
| `urls` | required | Public pages. `https://` is added when missing; duplicates and `#fragments` are removed. |
| `strategy` | `mobile` | `mobile`, `desktop` or `both`. `both` is two analyses (and two charges) per URL. |
| `categories` | all four | Which category scores to return. |
| `apiKey` | none | Your own PageSpeed Insights API key. Stored as a secret. |
| `maxConcurrency` | 5 with a key, 2 without | Analyses at the same time (max 20 with a key, 2 without). |
| `requestTimeoutSecs` | 120 | Time allowed for one analysis. Slow pages can take a minute. |
| `maxRetries` | 3 | Retries on 429, 5xx and network errors. |

### Output

One dataset item per URL and device. Example (shortened; values come from a mocked API response, not a live one):

```json
{
    "inputUrl": "https://example.com/",
    "url": "https://example.com/",
    "strategy": "mobile",
    "status": "ok",
    "errorKind": null,
    "error": null,
    "finalUrl": "https://example.com/",
    "performanceScore": 91,
    "accessibilityScore": 80,
    "bestPracticesScore": 100,
    "seoScore": 90,
    "lcpMs": 2100,
    "cls": 0.01,
    "tbtMs": 80,
    "inpMs": null,
    "fcpMs": 900,
    "speedIndexMs": 1400,
    "fieldData": { "overall": "FAST", "lcp": { "p75": 1700, "rating": "FAST" } },
    "opportunities": [
        { "id": "unused-javascript", "title": "Reduce unused JavaScript", "savingsMs": 300, "savingsBytes": 90000, "displayValue": null }
    ],
    "lighthouseVersion": "12.0.0",
    "analyzedAt": "2026-10-13T10:00:00.000Z"
}
```

| Field | Meaning |
|---|---|
| `performanceScore`, `accessibilityScore`, `bestPracticesScore`, `seoScore` | Lighthouse scores, 0 to 100. |
| `lcpMs`, `cls`, `tbtMs`, `fcpMs`, `speedIndexMs` | Lab metrics from the Lighthouse run. |
| `inpMs` | Lab INP when Lighthouse reports it. It usually does not; use `tbtMs` as the lab signal and `fieldData.inp` for real users. |
| `fieldData` | Real-user numbers (Chrome UX Report, 75th percentile) when the URL has enough traffic. `null` for small sites. |
| `opportunities` | Up to 5 fixes with the largest estimated saving (`savingsMs`, `savingsBytes`). |
| `status` | `ok`, `failed`, `invalid-input` or `not-processed`. Only `ok` rows are charged. |
| `errorKind`, `error` | Why a row has no result: `timeout`, `rate-limit`, `quota-daily`, `invalid-key`, `key-forbidden`, `bad-url`, `server`, `network`, `bad-response`, `cost-limit`. |

A `SUMMARY` record in the key-value store lists how many analyses were charged and failed, and why the run stopped, if it did.

### How much does it cost?

You pay per event: **`url-analyzed`**, once for each URL and device read successfully. The price is shown on this page. Examples: 100 URLs on mobile is 100 events; 100 URLs on both devices is 200 events.

Not charged: failed analyses, timeouts, 429 and quota errors, invalid URLs, and rows marked `not-processed`.

Set a maximum cost per run in the run options. The Actor works out how many analyses that covers and stops there; the remaining URLs come back as free `not-processed` rows.

Google's API itself is free up to the quota of your key. You only pay Google if you choose to raise that quota yourself.

### Limits

- **Without an API key you share Google's free quota with everyone else.** It is small and is often used up for the day. When that happens the run stops with a clear message and charges nothing. For anything beyond a few URLs, use your own key.
- Google measures from its own servers. Scores move a few points between runs of the same page; that is normal for Lighthouse. Run twice if a decision depends on 3 points.
- Lab data is a simulation. `fieldData` is what real Chrome users saw, and only exists for pages with enough traffic.
- Public pages only. Pages behind a login, a paywall or a firewall that blocks Google's servers will fail.
- One analysis takes about 10 to 60 seconds, so large lists take a while. Higher concurrency helps only if your key's quota allows it.
- The Actor does not crawl. It analyzes exactly the URLs you give it.
- Google can change the API response. If a field disappears, the matching output field becomes `null` instead of the run failing.

### Is it legal?

It uses Google's public API as documented, with your key or the free shared quota, and reads only performance data about public pages. It collects no personal data and needs no login. You are responsible for following the [Google APIs Terms of Service](https://developers.google.com/terms) when you use your own key.

### FAQ

**How do I get an API key?** In Google Cloud Console, create a project, enable the PageSpeed Insights API, and create an API key. It is free and gives you 25,000 requests a day. Restrict the key to that API if you can.

**Is my key safe?** It is a secret input field, it is sent only to Google, and it is removed from every log line and error message. Use a key you can revoke.

**Why did my run stop with "daily quota"?** The shared free quota (or your key's quota) is gone for today. Nothing was charged. Add your key or run again tomorrow.

**Why is `inpMs` empty?** Lighthouse lab runs do not measure real interactions, so there is usually no lab INP. Look at `tbtMs` and at `fieldData.inp`.

**Can I run it on a schedule?** Yes, with Apify Schedules. Keep the same URL list to compare runs.

**Is it the same as pagespeed.web.dev?** It uses the same API and Lighthouse engine, so scores should match within the usual run-to-run variation.

Not affiliated with or endorsed by Google. PageSpeed Insights and Lighthouse are Google products.

# Actor input Schema

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

One per line. Public pages only (no login, no localhost). A missing https:// is added. Duplicates are removed. Each URL is analyzed exactly as written, so list the pages you care about (home, product, blog).

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

Lighthouse emulation. "both" runs mobile and desktop for every URL (two analyses, two charges per URL).

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

Which category scores to return. Performance is the one behind LCP, CLS, TBT and the fix list.

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

Your own key for the PageSpeed Insights API (free in Google Cloud Console). Without it the Actor uses the shared free quota of Google, which is small and often used up; with it you get your own quota (25,000 requests a day). The key is never logged or saved in the results.

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

Default 5 with your own key, 2 without (the maximum without a key). Maximum 20.

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

Google can take 10 to 60 seconds on slow pages. A timeout is a failure and is not charged.

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

Retries for 429, 5xx and network errors, with exponential backoff (honors Retry-After).

## Actor input object example

```json
{
  "urls": [
    "https://example.com/",
    "https://www.wikipedia.org/"
  ],
  "strategy": "mobile",
  "categories": [
    "performance",
    "accessibility",
    "best-practices",
    "seo"
  ],
  "requestTimeoutSecs": 120,
  "maxRetries": 3
}
```

# Actor output Schema

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

No description

## `summary` (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("kilndata/pagespeed-lighthouse-batch").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("kilndata/pagespeed-lighthouse-batch").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 kilndata/pagespeed-lighthouse-batch --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kilndata/pagespeed-lighthouse-batch"
        }
    }
}
```

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/Xv1xfJ2J2BG8cBccp/builds/zuxk5hPqMNoueTw7n/openapi.json
