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

Run Google PageSpeed Insights on hundreds of URLs: Lighthouse performance, accessibility, best-practices and SEO scores, Core Web Vitals (lab + real-user CrUX incl. INP, LCP, CLS) and top fixes per page. Mobile, desktop or both. No API key needed to start. $3.00 per 1,000 audits.

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

## Pricing

from $2.00 / 1,000 page auditeds

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

**Who it's for:** SEO agencies, web developers, site owners and performance teams who check page speed for many URLs.

**The problem it solves:** pagespeed.web.dev checks one URL at a time, and the raw API returns huge JSON. This Actor audits a whole URL list and gives you one row per page and device with scores, Core Web Vitals and the top fixes.

**Use cases:**

- Core Web Vitals audits for a whole site (pair with Sitemap URL Extractor)
- Weekly performance monitoring on a schedule
- Agency client reports and competitor speed comparisons
- Before/after checks for releases and migrations

**Quick example:** `{"urls":[{"url":"https://example.com"}],"strategy":"mobile"}` → one row per URL and device: `performanceScore`, `accessibilityScore`, `seoScore`, `coreWebVitalsAssessment`, `lab` and `field` metrics (LCP, INP, CLS…), `opportunities`.

**Price:** **$3.00 per 1,000 audits**. Comparable PageSpeed/Lighthouse Actors in the Store charge about $2 to $80 per 1,000 URLs. Failed, skipped and retried audits are free.

Run **Google PageSpeed Insights on hundreds of URLs at once** and get every result in one table: Lighthouse **Performance, Accessibility, Best Practices and SEO scores**, **Core Web Vitals** (lab data plus real-user Chrome UX Report field data), and the **top fixes** for each page, on mobile, desktop or both.

It uses Google's **official PageSpeed Insights API v5**, so you get the same numbers as [pagespeed.web.dev](https://pagespeed.web.dev). There's no browser to run and no proxies, which makes it cheap and reliable.

### What you get for each URL and device

- **Scores (0–100):** performance, accessibility, best practices, SEO
- **Core Web Vitals, field data:** 28-day p75 from real Chrome users (CrUX) for LCP, **INP**, CLS, FCP and TTFB, each rated FAST / AVERAGE / SLOW, for both the page and the whole origin
- **Core Web Vitals verdict:** PASSED / FAILED / NO_FIELD_DATA, the same pass/fail result PageSpeed shows
- **Lab metrics (Lighthouse):** LCP, CLS, **TBT** (the lab stand-in for INP), FCP, Speed Index, TTI, server response time, total page weight
- **Top opportunities:** ranked by estimated time saved, e.g. "Reduce unused JavaScript: 3,000 ms / 907 KiB"
- **Failed audits:** accessibility, SEO and best-practices checks that failed
- A link to the full report on pagespeed.web.dev

### Use cases

- SEO and Core Web Vitals audits of a whole site. Pair it with **Sitemap URL Extractor** to get every URL first.
- Weekly performance monitoring: schedule runs and track scores over time.
- Agency client reports and comparisons with competitors
- Checks before and after a release or migration

### API key: optional for small runs, recommended for big ones

- **No key needed for quick checks.** Runs without a key use the Actor's built-in Google API key, up to **30 audits per run** (one audit = one URL on one device). Extra URLs are listed as `"status": "skipped"` rows and are **not charged**. The limit keeps the shared quota fair for everyone.
- **Big jobs: bring your own free key.** Paste your own Google API key into **Google API key** for **unlimited audits per run**, at up to 25,000 audits a day on Google's free quota. It takes about 2 minutes and needs no billing: [get a key](https://developers.google.com/speed/docs/insights/v5/get-started), click "Get a Key" and copy it. Your key is stored as a secret input.
- If Google's quota is ever used up, the run still finishes cleanly with a clear message and an error row, and **nothing is charged**.

### Input

| Field | Default | Description |
|---|---|---|
| `urls` | `https://crawlee.dev` | Pages to audit (max 30 audits per run without your own key) |
| `apiKey` | – | Your free Google API key (stored as a secret) |
| `strategy` | `mobile` | `mobile`, `desktop` or `both` (both = 2 audits per URL) |
| `categories` | all four | `performance`, `accessibility`, `best-practices`, `seo` |
| `maxConcurrency` | 4 | Parallel audits (capped at 4 when using the built-in key) |
| `maxOpportunities` | 5 | Top opportunities per audit |
| `locale` | `en` | Language of audit titles |
| `maxRetries` | 4 | Retries on 429, 5xx and timeouts, with exponential backoff |
| `requestTimeoutSecs` | 120 | Timeout per audit |
| `includeRawResponse` | false | Attach the full PSI JSON (large) |

```json
{
  "urls": [{ "url": "https://example.com" }, { "url": "https://example.com/pricing" }],
  "strategy": "both",
  "apiKey": "AIza...",
  "maxConcurrency": 8
}
```

### Output example

This is a real result from a cloud run on 10 Oct 2026 (crawlee.dev, mobile; `originField` is omitted here for brevity):

```json
{
  "url": "https://crawlee.dev/",
  "finalUrl": "https://crawlee.dev/",
  "strategy": "mobile",
  "status": "success",
  "performanceScore": 47,
  "accessibilityScore": 95,
  "bestPracticesScore": 96,
  "seoScore": 92,
  "coreWebVitalsAssessment": "FAILED",
  "lab": {"lcpMs": 11938, "cls": 0, "tbtMs": 823, "fcpMs": 1812, "speedIndexMs": 7838, "ttiMs": 22354, "serverResponseMs": 2, "totalByteWeight": 2674545},
  "field": {"overallCategory": "AVERAGE", "isOriginFallback": false, "lcpMs": 2894, "lcpCategory": "AVERAGE", "inpMs": 305, "inpCategory": "AVERAGE", "cls": 0, "clsCategory": "FAST", "fcpMs": 2363, "fcpCategory": "AVERAGE", "ttfbMs": 880, "ttfbCategory": "AVERAGE"},
  "opportunities": [
    {"id": "cache-insight", "title": "Use efficient cache lifetimes", "displayValue": "Est savings of 782 KiB", "savingsMs": 2550, "savingsBytes": 0},
    {"id": "unused-javascript", "title": "Reduce unused JavaScript", "displayValue": "Est savings of 866 KiB", "savingsMs": 1950, "savingsBytes": 886455},
    {"id": "render-blocking-insight", "title": "Render-blocking requests", "displayValue": "Est savings of 1,200 ms", "savingsMs": 1200, "savingsBytes": 0}
  ],
  "failedAudits": [
    {"category": "accessibility", "id": "color-contrast", "title": "Background and foreground colors do not have a sufficient contrast ratio."},
    {"category": "accessibility", "id": "landmark-one-main", "title": "Document does not have a main landmark."}
  ],
  "lighthouseVersion": "13.5.0",
  "fetchTime": "2026-10-10T17:14:35.441Z",
  "reportUrl": "https://pagespeed.web.dev/analysis?url=https%3A%2F%2Fcrawlee.dev%2F&form_factor=mobile"
}
```

Failed URLs appear as `"status": "error"` rows that say why (for example, Lighthouse couldn't load the page), and they are **never charged**.

### Pricing

**Pay per event: $3.00 per 1,000 audits** ($0.003 per audit). One audit = one URL on one device, so `"strategy": "both"` counts as 2 audits per URL. Platform usage is included. **Failed audits, retries, skipped URLs and error rows are free.** Set a **maximum cost per run** and the Actor stops starting new audits when it reaches that limit.

Examples: 100 pages on mobile cost $0.30. A 1,000-page site on mobile and desktop costs $6.00.

### Related Actors

More no-login, pay-per-result tools from the same developer:

- [Sitemap URL Extractor](https://apify.com/aaronallday89/sitemap-url-extractor): every URL from a website's sitemaps (indexes, gzip, RSS), with lastmod and priority.
- [Whole-Site PageSpeed Audit (Sitemap → Lighthouse)](https://apify.com/aaronallday89/whole-site-pagespeed-audit): enter a homepage and get PageSpeed audits of its pages plus a site summary.
- [SEO Meta Tags & Structured Data Auditor](https://apify.com/aaronallday89/seo-meta-structured-data-auditor): titles, descriptions, canonical, noindex, Open Graph and Schema.org rich-result checks per URL.
- [Wayback Machine Snapshot Finder](https://apify.com/aaronallday89/wayback-snapshot-finder): archived snapshots, first/last capture and every archived URL of a domain.
- [Domain Health Checker: WHOIS, DNS & SSL](https://apify.com/aaronallday89/domain-health-checker): domain age and expiry, DNS/SPF/DMARC, SSL certificate and HTTPS redirects.
- [ATS Jobs Scraper](https://apify.com/aaronallday89/ats-job-board-scraper): open jobs from Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee and Personio.

#### Use together

Extract a site's URLs with **Sitemap URL Extractor** (e.g. `"includeRegex": "/products/"`, `"maxUrls": 30`), then pass the `url` column of its dataset as `urls` here. You can do this by hand, with an Apify integration ("Run another Actor when this one succeeds"), or in code:

```js
const { defaultDatasetId } = await client.actor('aaronallday89/sitemap-url-extractor').call({ startUrls: [{ url: 'https://example.com' }], includeRegex: '/products/', maxUrls: 30 });
const { items } = await client.dataset(defaultDatasetId).listItems({ fields: ['url'] });
await client.actor('aaronallday89/pagespeed-bulk-audit').call({ urls: items.map((i) => ({ url: i.url })), strategy: 'both' });
```

### FAQ

**Why are the field data / Core Web Vitals empty?**
Google only has real-user (CrUX) data for pages and origins with enough Chrome traffic. Smaller sites show `NO_FIELD_DATA`, but you still get the lab metrics.

**Why is my score different from the last run?**
Lighthouse lab scores naturally vary by a few points from run to run. Field data is a 28-day rolling average and changes slowly.

**Where's lab INP?**
Lighthouse can't measure INP in a lab test, so it reports Total Blocking Time (TBT) instead. Real-user INP is in `field.inpMs`.

**How fast is it?**
Each audit takes about 10–40 seconds on Google's side (the cloud test took 30 seconds). With your own key and concurrency 8, that's roughly 15–40 audits a minute.

**Does it run Lighthouse itself?**
No. Google runs Lighthouse on its own servers through the official API, which is why it's cheap and matches pagespeed.web.dev.

**Why is there a 30-audit limit?**
It applies only when you don't provide a key, because the built-in key's daily quota is shared by all users. Add your own free key and the limit goes away.

**I got "Invalid API key" or "Access denied".**
Make sure the key is copied correctly and that the PageSpeed Insights API is enabled for its Google Cloud project.

### More tools from the same developer

- [Sitemap URL Extractor: Get All URLs From Any Website](https://apify.com/aaronallday89/sitemap-url-extractor): SEO specialists, agencies, developers and data/AI teams
- [Whole-Site PageSpeed & Core Web Vitals Audit (via Sitemap)](https://apify.com/aaronallday89/whole-site-pagespeed-audit): SEO consultants, agencies and site owners
- [Greenhouse, Lever, Ashby & Workable Jobs Scraper (7 ATS)](https://apify.com/aaronallday89/ats-job-board-scraper): Job boards and aggregators, recruiters, sales teams prospecting on hiring signals, and market researchers
- [Bulk WHOIS, DNS & SSL Expiry Checker: Domain Health](https://apify.com/aaronallday89/domain-health-checker): IT admins, agencies managing client domains, email deliverability teams, domain investors and lead-enrichment workflows

# Actor input Schema

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

Pages to audit. Each URL x strategy is one audit. Tip: feed the output of Sitemap URL Extractor here.

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

Your free Google PageSpeed Insights API key (25,000 audits/day, no billing needed). Without a key the Actor uses Google's tiny shared keyless quota, which is often exhausted. Get one: https://developers.google.com/speed/docs/insights/v5/get-started

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

Emulated device. 'both' runs two audits per URL.

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

Which Lighthouse categories to score. Performance is needed for lab Core Web Vitals and opportunities.

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

Parallel PSI requests (each takes ~10-40 s). Forced to 1 without an API key.

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

How many improvement opportunities (sorted by estimated savings) to include.

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

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

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

Retries per audit on 429 (rate limit), 5xx and timeouts, with exponential backoff.

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

Timeout per PSI API call.

## `includeRawResponse` (type: `boolean`):

Attach the full PageSpeed Insights JSON (large, ~0.5-1 MB per audit).

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://crawlee.dev"
    }
  ],
  "strategy": "mobile",
  "categories": [
    "performance",
    "accessibility",
    "best-practices",
    "seo"
  ],
  "maxConcurrency": 4,
  "maxOpportunities": 5,
  "locale": "en",
  "maxRetries": 4,
  "requestTimeoutSecs": 120,
  "includeRawResponse": false
}
```

# Actor output Schema

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

Scores and Core Web Vitals per URL and device (default dataset, 'Scores & Core Web Vitals' view).

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

JSON summary: audits requested, succeeded, failed, skipped, API key source.

# 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": [
        {
            "url": "https://crawlee.dev"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("aaronallday89/pagespeed-bulk-audit").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": [{ "url": "https://crawlee.dev" }] }

# Run the Actor and wait for it to finish
run = client.actor("aaronallday89/pagespeed-bulk-audit").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": [
    {
      "url": "https://crawlee.dev"
    }
  ]
}' |
apify call aaronallday89/pagespeed-bulk-audit --silent --output-dataset

```

## MCP server setup

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

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/eubFlNARkAhz8ZPjx/builds/fd2ziOt8vnsyuv8XD/openapi.json
