# Bulk PageSpeed Insights Checker: Core Web Vitals & Lighthouse (`latamdatatools/pagespeed-bulk-checker`) Actor

Check hundreds of URLs with Google PageSpeed Insights: Lighthouse performance, accessibility, best practices and SEO scores, LCP, CLS, INP, TBT, FCP, TTFB, real-user Core Web Vitals (CrUX) and top fixes. Mobile and desktop. Bring your own free API key for big lists.

- **URL**: https://apify.com/latamdatatools/pagespeed-bulk-checker.md
- **Developed by:** [Julian Genuario](https://apify.com/latamdatatools) (community)
- **Categories:** SEO tools, Developer tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 url analyseds

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

## Bulk PageSpeed Insights Checker: Core Web Vitals & Lighthouse

Check the speed of hundreds of pages in one run with Google's official **PageSpeed Insights API (v5)**. You get one clean row per URL and device:

- **Lighthouse scores** (0-100): performance, accessibility, best practices, SEO
- **Lab metrics**: LCP, CLS, TBT, FCP, Speed Index, Time to Interactive and server response time (TTFB)
- **Real-user data (Chrome UX Report / CrUX)** when Google has it: LCP, INP, CLS, FCP and TTFB at the 75th percentile, with Google's FAST / AVERAGE / SLOW rating, for the URL and for the whole origin
- **Core Web Vitals verdict**: PASSED or FAILED, using Google's thresholds (LCP ≤ 2.5 s, INP ≤ 200 ms, CLS ≤ 0.1)
- **Top opportunities**: the biggest performance problems on the page, with estimated savings in ms / KB and the metrics they affect
- **Monitoring mode**: compare with the previous run and flag regressions. Works well with an Apify schedule.

Mobile, desktop or both. Sitemaps are supported.

### What does Bulk PageSpeed Insights Checker do?

Bulk PageSpeed Insights Checker runs Google PageSpeed Insights (Lighthouse) on a list of URLs and returns performance, accessibility, best practices and SEO scores, lab metrics (LCP, CLS, TBT, FCP, Speed Index) and real-user Core Web Vitals from the Chrome UX Report (CrUX), for mobile and desktop. Use it for site speed audits, Core Web Vitals monitoring and performance regression alerts.

### Who is it for

- **SEO agencies and freelancers** auditing client sites or prospect lists
- **Web developers** checking a whole site after a release
- **E-commerce teams** watching product and category pages every week
- **Lead generation**: find businesses with slow websites and offer them help

### How to use it

1. Paste your URLs (one per line) or add a sitemap URL.
2. Pick the device (mobile, desktop or both) and the Lighthouse categories.
3. Optional but recommended for more than ~200 checks: paste **your own free Google API key** (see below).
4. Run. Export the results as JSON, CSV, Excel or via API, or connect them to Google Sheets, Make, Zapier, etc.

#### Input example

```json
{
  "urls": ["https://www.wikipedia.org/", "https://web.dev/"],
  "strategy": "both",
  "categories": ["performance", "accessibility", "best-practices", "seo"],
  "maxOpportunities": 5,
  "compareWithPreviousRun": false
}
```

### Output example (one row, shortened)

```json
{
  "url": "https://web.dev/",
  "strategy": "mobile",
  "status": "ok",
  "performanceScore": 49,
  "accessibilityScore": 90,
  "bestPracticesScore": 100,
  "seoScore": 92,
  "lab": { "lcpMs": 4056, "fcpMs": 3906, "cls": 0.117, "tbtMs": 1142, "speedIndexMs": 4947, "ttiMs": 12174, "ttfbMs": 3 },
  "coreWebVitals": "FAILED",
  "coreWebVitalsSource": "url",
  "fieldData": { "overallCategory": "SLOW", "lcpMs": 3057, "inpMs": 226, "cls": 0.01, "fcpMs": 3062, "ttfbMs": 1740, "...": "categories per metric" },
  "originFieldData": { "...": "same fields for the whole site" },
  "opportunities": [
    { "id": "render-blocking-insight", "title": "Render-blocking requests", "displayValue": "Est savings of 2,550 ms", "score": 0, "estimatedSavingsMs": 2550, "estimatedSavingsKb": 0, "affectedMetrics": ["LCP", "FCP"] }
  ],
  "previous": null,
  "regression": null,
  "lighthouseVersion": "13.5.0",
  "analysisTime": "2026-10-26T16:04:30.282Z"
}
```

Every row has exactly the same keys. `fieldData` is `null` when Google has no real-user data for that exact URL (common for small sites); `originFieldData` may still be there. Pages that cannot be analysed (site down, blocked, timeout) come back as rows with `status: "failed"` and the reason in `error`, and **they are not charged**.

The dataset has three views: *Scores & Core Web Vitals*, *Real-user data (CrUX)* and *Top opportunities*. A `RUN_SUMMARY` record in the key-value store shows how many checks succeeded, failed, were retried or skipped.

### Your own Google API key (free, 2 minutes)

The Actor works out of the box with a shared key, limited to **200 checks per run** and shared by all users (Google allows ~25,000 checks per day per key). For bigger lists, use your own free key:

1. Open https://console.cloud.google.com/ and create a project (no billing account or credit card needed).
2. Go to *APIs & Services → Library*, search **PageSpeed Insights API** and click **Enable**.
3. Go to *APIs & Services → Credentials → Create credentials → API key*. Restrict it to the PageSpeed Insights API.
4. Paste the key in the **Your Google API key** input. It is stored as a secret input and never written to the dataset or the log.

With your own key you can check up to 10,000 URL × device combinations per run and run up to 8 checks in parallel.

### Monitoring and regressions

Turn on **Compare with previous run** and schedule the Actor (daily or weekly). Each row then includes `previous` (score and metric changes since the last run) and `regression: true` when the performance score dropped by at least the threshold you set (default 10 points). History is kept in your own named key-value store `pagespeed-history`.

Note: lab scores naturally move a few points between runs. Use a threshold of 10 or more to avoid noise.

### Pricing

Pay per event: **USD 4 per 1,000 analysed URLs** (USD 0.004 per URL and device). "Both" devices counts as two analyses. Failed URLs are free. Apify's platform usage is included.

### Limits and good behaviour

- Uses only Google's official API. Your target sites are loaded by Google's Lighthouse servers, not by this Actor.
- Each check takes 10-40 seconds at Google. Parallel checks: up to 3 with the shared key, up to 8 with your own key.
- Automatic retries with back-off on rate limits (HTTP 429) and Google server errors (5xx). If the daily quota of the key is used up, or the key is invalid, the run stops cleanly and tells you why.
- The run respects your *max charge per run*: it never analyses more URLs than you will pay for.
- If no URL could be analysed, the run is marked as **failed** (never a silent empty success).

### Why use Bulk PageSpeed Insights Checker

- **One row per URL with both lab and field data:** Lighthouse scores, LCP, CLS, TBT, FCP, Speed Index, TTFB, plus real-user Core Web Vitals (CrUX) for the URL and the origin, and the PASSED / FAILED verdict.
- **Bulk input:** a list of URLs and/or sitemaps, mobile, desktop or both.
- **Regression monitor:** with `compareWithPreviousRun` each row shows the score change and a `regression` flag when it drops by more than your threshold.
- **Works without a Google key** for up to 200 analyses per run; add your own free key for up to 10,000.
- **Failed URLs are not charged.**

### Cost examples

Prices at the current pay-per-event rates (see Pricing). You can set a maximum charge per run in the run options.

| Job | Approximate cost |
|---|---|
| 100 URLs, mobile | about $0.40 |
| 100 URLs, mobile + desktop | about $0.80 |
| Weekly check of 50 URLs (mobile), one month | about $0.80 |

### Use it from your code, an integration or an AI agent

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("latamdatatools/pagespeed-bulk-checker").call(run_input={
    "urls": [
        "https://www.wikipedia.org/",
        "https://web.dev/"
    ],
    "strategy": "mobile"
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

**JavaScript / Node.js** (`npm install apify-client`):

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('latamdatatools/pagespeed-bulk-checker').call({
  "urls": [
    "https://www.wikipedia.org/",
    "https://web.dev/"
  ],
  "strategy": "mobile"
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**No code:**

- **Schedules:** run it daily or weekly from Apify Console (Schedules), with the same input saved as a Task.
- **Integrations:** send results to Google Sheets, Slack, email, Make, Zapier, n8n or Airbyte from the Integrations tab, or call your own webhook when a run finishes.
- **AI agents:** add it as a tool in Claude, Cursor, VS Code and other MCP clients through the Apify MCP server (`https://mcp.apify.com`), so the agent can run Bulk PageSpeed Insights Checker and read the results.

### FAQ

**Why are my scores different from pagespeed.web.dev?** It is the same API and the same Lighthouse, but lab tests vary between runs (network, server load). Field data (CrUX) is the stable, real-user number.

**Why is `fieldData` empty?** Google only publishes real-user data for pages and origins with enough Chrome traffic.

**Can I check pages behind a login?** No. PageSpeed Insights only loads public pages.

**Do I need a Google API key?** No for up to 200 analyses per run. For bigger lists, create a free PageSpeed Insights key (see the section above) and paste it in `apiKey`. The key is stored as a secret input.

**Can I run it on every deploy?** Yes. Call it from your CI with the Apify API (see the code above) and fail the pipeline if a row comes back with `regression: true`.

Questions or a missing field? Open an issue in the Issues tab.

# Actor input Schema

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

One page URL per line. "https://" is added if missing. Duplicates are removed.

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

Optional: sitemap.xml or sitemap index URLs. Page URLs found in them are added to the list (up to the limit below).

## `maxUrlsFromSitemaps` (type: `integer`):

Upper limit of page URLs read from all sitemaps together.

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

Mobile, desktop or both. "Both" produces two rows (and two analyses) per URL.

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

Which Lighthouse scores to compute. Core Web Vitals, lab metrics and opportunities come with "performance".

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

Optional. A free Google Cloud API key with the PageSpeed Insights API enabled (no billing needed, about 25,000 checks per day). Without it the Actor uses its shared key, limited to 200 analyses per run and shared by all users. See the README for the 2-minute setup.

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

How many of the biggest performance problems (with estimated savings) to include per row. 0 = none.

## `compareWithPreviousRun` (type: `boolean`):

Stores the scores of each URL in the named key-value store "pagespeed-history" and adds score changes and a regression flag on the next run. Use it with a schedule.

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

A row is flagged as a regression when the performance score dropped by at least this many points since the previous run.

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

Language for opportunity titles, e.g. en, es, pt-BR, de, fr.

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

How many URLs are analysed at the same time. Each check takes 10-40 s at Google. Capped at 3 with the shared key and at 8 with your own key.

## Actor input object example

```json
{
  "urls": [
    "https://www.wikipedia.org/",
    "https://web.dev/"
  ],
  "maxUrlsFromSitemaps": 50,
  "strategy": "mobile",
  "categories": [
    "performance",
    "accessibility",
    "best-practices",
    "seo"
  ],
  "maxOpportunities": 5,
  "compareWithPreviousRun": false,
  "regressionThreshold": 10,
  "locale": "en",
  "maxConcurrency": 3
}
```

# Actor output Schema

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

No description

## `runSummary` (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://www.wikipedia.org/",
        "https://web.dev/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("latamdatatools/pagespeed-bulk-checker").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.wikipedia.org/",
        "https://web.dev/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("latamdatatools/pagespeed-bulk-checker").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.wikipedia.org/",
    "https://web.dev/"
  ]
}' |
apify call latamdatatools/pagespeed-bulk-checker --silent --output-dataset

```

## MCP server setup

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

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/wsenQrelgbR11LaHe/builds/DtCY7vx2zVf2yJIOf/openapi.json
