# PageSpeed & Lighthouse Audit: Bulk Core Web Vitals, No API Key (`charlesmarkette/pagespeed-lighthouse-audit-core-web-vitals`) Actor

Bulk Lighthouse audits without a Google API key: performance, accessibility, best practices and SEO scores plus LCP, CLS, TBT, FCP and Speed Index for many URLs, mobile and desktop, run in headless Chrome. Optional: your own PageSpeed Insights key for real-user CrUX data.

- **URL**: https://apify.com/charlesmarkette/pagespeed-lighthouse-audit-core-web-vitals.md
- **Developed by:** [Charles Markette](https://apify.com/charlesmarkette) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $14.00 / 1,000 audited urls

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 Audit: Bulk Core Web Vitals, No API Key

**PageSpeed & Lighthouse Audit** runs Google's open-source **Lighthouse 13** in headless Chrome on Apify's servers and returns, for every URL, the four **Lighthouse scores** (performance, accessibility, best practices, SEO, each 0–100) and the lab **Core Web Vitals and speed metrics**: Largest Contentful Paint, Cumulative Layout Shift, Total Blocking Time, First Contentful Paint, Speed Index and Time to Interactive. Audit **mobile, desktop or both**, and optionally get the **top failing audits** with their estimated savings.

**No API key is needed.** Many PageSpeed tools call Google's PageSpeed Insights API, whose shared keyless quota often runs out ("Queries per day" errors). This Actor runs Lighthouse itself, so there is no Google quota. If you also want **real-user field data** from the Chrome UX Report (CrUX), paste your own free PageSpeed Insights API key and each URL goes to Google's API instead.

### Why this Actor

- **No key, no quota errors.** Lighthouse runs here, in a fresh headless Chrome for every audit.
- **Bulk.** Paste hundreds of URLs; each URL × device is one row, ready for CSV, Excel or an integration.
- **The same engine as PageSpeed Insights.** Lighthouse with its standard mobile settings (phone screen, simulated slow 4G, 4× CPU slowdown) or desktop settings.
- **Field data when you want it.** With your own key you get CrUX p75 LCP, INP and CLS for the URL (or its whole origin when the URL has too little traffic), next to Google's lab scores.
- **No audit charge for failures.** Invalid URLs, DNS failures, timeouts and pages Lighthouse can't load get a row with a plain-English `error` and no audit charge (the run start still applies, see pricing).
- **Guards against risky URL lists.** Every page and every request it makes go through a filter that refuses private, loopback, link-local and cloud-metadata addresses.

### Use cases

- **SEO and site audits:** score every template of a site (home, category, product, blog) on mobile and desktop in one run.
- **Client reporting:** a scheduled run over your clients' key pages, sent to Google Sheets.
- **Lead generation for agencies:** find the companies in a scraped list whose sites score badly on mobile.
- **Before and after a release:** run the same URLs before and after a change and compare LCP, CLS and TBT.
- **Accessibility triage:** turn on *Include top failing audits* to see which accessibility checks fail on each page.

### How to use it

1. Add **URLs** (full URLs or bare domains such as `example.com`, which become `https://example.com/`).
2. Pick the **Device**: mobile (the default, like PageSpeed Insights' mobile tab), desktop, or both.
3. Optionally pick fewer **Categories**, turn on **Include top failing audits**, or paste **your own PageSpeed Insights API key** for field data.
4. Run it. Each URL × device becomes one row. The run summary (counts and average scores) is saved as `OUTPUT` in the key-value store.

**Input example**

```json
{
  "urls": ["https://www.python.org", "https://apify.com", "example.com/pricing"],
  "strategy": "both",
  "categories": ["performance", "accessibility", "best-practices", "seo"],
  "includeAudits": true,
  "maxConcurrency": 1,
  "timeoutSecsPerUrl": 90
}
```

**Output example** (shortened; values are illustrative)

```json
{
  "url": "https://www.example.com/",
  "inputUrl": "example.com",
  "finalUrl": "https://www.example.com/",
  "strategy": "mobile",
  "engine": "lighthouse-local",
  "lighthouseVersion": "13.5.0",
  "scores": {"performance": 75, "accessibility": 100, "bestPractices": 96, "seo": 100},
  "metrics": {"lcpMs": 2374, "fcpMs": 943, "tbtMs": 1151, "cls": 0, "speedIndexMs": 943, "ttiMs": 2374},
  "fieldData": null,
  "topOpportunities": [
    {"id": "render-blocking-insight", "title": "Render blocking requests", "category": "performance", "score": 0,
     "displayValue": "Est savings of 250 ms", "savingsMs": 250, "savingsBytes": null},
    {"id": "errors-in-console", "title": "Browser errors were logged to the console", "category": "bestPractices", "score": 0,
     "displayValue": null, "savingsMs": null, "savingsBytes": null}
  ],
  "runWarnings": [],
  "benchmarkIndex": 2373,
  "durationMs": 7337,
  "fetchedAt": "2026-10-19T12:00:00.000Z",
  "error": null
}
```

With your own API key, `engine` is `"pagespeed-api"` and `fieldData` holds the real-user numbers, for example:

```json
"fieldData": {"scope": "url", "lcpP75Ms": 2710, "inpP75Ms": 180, "clsP75": 0.05, "fcpP75Ms": 1450, "ttfbP75Ms": 640, "category": "AVERAGE"}
```

**All columns**

- `url` (normalised), `inputUrl` (as you typed it), `finalUrl` (after redirects), `strategy` (`mobile` or `desktop`), `engine` (`lighthouse-local` or `pagespeed-api`), `lighthouseVersion`.
- `scores`: `performance`, `accessibility`, `bestPractices`, `seo`, as 0–100 integers (null for a category you didn't pick).
- `metrics` (lab, with Performance): `lcpMs`, `fcpMs`, `tbtMs`, `cls`, `speedIndexMs`, `ttiMs`.
- `fieldData` (only with your API key, otherwise null): `scope` (`url`, or `origin` when Google only has data for the whole site), `lcpP75Ms`, `inpP75Ms`, `clsP75`, `fcpP75Ms`, `ttfbP75Ms`, `category` (`FAST`, `AVERAGE` or `SLOW`). These are the 75th percentile of real Chrome users over the last 28 days.
- `topOpportunities` (only with *Include top failing audits*, otherwise null): up to 10 failing audits with `id`, `title`, `category`, `score`, `displayValue`, `savingsMs`, `savingsBytes`, biggest savings first.
- `runWarnings` (Lighthouse's own warnings, such as a slow CPU or a page that redirected), `benchmarkIndex` (Lighthouse's measure of the CPU it ran on), `durationMs`, `fetchedAt`, `error` (null on success).

The dataset has three views: **Scores**, **Real-user data** and **Top failing audits**.

`error` values include `Invalid URL: …`, `Blocked: private or internal address …`, `DNS lookup failed: …`, `Timed out after 90 seconds …`, `Lighthouse couldn't audit the page: …` (for example the page returned an error status, never painted, or has a bad certificate) and `PageSpeed Insights API error …`.

### How much does it cost?

Pay per event: **$0.02 per audited URL** (one URL on one device, one output row), plus the run start. Apify platform usage is included.

**Apify Store discounts:** the price above is for Apify's Free plan. Paid plans pay less per audit: $0.018 on Starter, $0.016 on Scale, $0.014 on Business. The "from" price at the top of the Store page is the Business price.

The run start is charged by the platform per GB of Actor memory: **$0.02 at the default 4 GB**. Rows with an `error` (invalid or blocked URLs, DNS failures, timeouts, pages Lighthouse couldn't load, API errors) **have no audit charge**. The run start applies to every run, even one where every audit fails. *Both* devices means two audits, so two charges, per URL.

**Worked examples:**

*Examples use the Free-plan rate. Where an example doesn't add it, each run also has its run-start charge, counted per GB of Actor memory (minimum one; four at the default 4 GB, which is $0.02).*

- 100 URLs on mobile: 100 × $0.02 = **$2.00** + start.
- 100 URLs on mobile and desktop: 200 × $0.02 = **$4.00** + start.

If you set a maximum cost per run, the Actor stops cleanly at that limit and doesn't start audits it can't charge for. If a run is restarted (for example after a migration), it skips the URL × device pairs already in its dataset.

### FAQ and limits

#### Why are my scores different from PageSpeed Insights?

This is **lab data** measured on Apify's servers, not on Google's machines: the CPU, network and location differ, and PageSpeed Insights may run a different Lighthouse version. Performance scores also vary from run to run on any machine, often by several points. Accessibility, best-practices and SEO scores are much steadier. For important pages, run the audit more than once and compare the median. `benchmarkIndex` and `runWarnings` show when the CPU was slower than Lighthouse expects.

#### How much memory should I give it?

The default is 4 GB because on Apify a run's CPU share grows with its memory, and Lighthouse needs about one full CPU core: in our tests a heavy page (apify.com) timed out after 90 s at 1 GB but finished in 36 s at 4 GB. Each parallel audit needs about 900 MB, and the Actor lowers *Parallel audits* to what fits. Running with less memory costs less to start but makes timeouts and lower performance scores more likely. Parallel audits share the CPU and lower performance scores, so keep *Parallel audits* at 1 when you compare performance; for accessibility, best practices and SEO only, parallel audits are fine (up to 3).

#### How long does it take?

Each audit loads the page in a fresh Chrome and waits for it to settle, so expect tens of seconds per URL per device on real pages; heavy pages take longer. For long lists, raise the run timeout (default 1 hour).

#### How do I get real-user (CrUX) data?

Create an API key in Google Cloud Console, enable the **PageSpeed Insights API** for its project, and paste the key into *Your PageSpeed Insights API key*. Each URL is then audited by Google's own PageSpeed Insights servers (their lab scores plus CrUX field data), and Google's quota for your key applies. Without a key, `fieldData` is always null: real-user data only comes from Google. Sites with little Chrome traffic have no CrUX data at all; the row then says so in `runWarnings`. If Google refuses your key, the run stops with a clear message instead of filling the dataset with errors. The key is stored encrypted by Apify and is never written to the output or logs.

#### Why is there no INP in the lab metrics?

Interaction to Next Paint needs a real user clicking and typing, so Lighthouse can't measure it in a page load. Total Blocking Time (`tbtMs`) is the lab metric that tracks it best. Real INP comes with `fieldData` when you use your own key.

#### Can it audit pages behind a login?

No. Each audit starts a fresh browser with no cookies, and there are no options for logins, custom headers or cookies. Pages that block bots or data-centre IP addresses may score oddly or fail with `NO_FCP` or an error status.

#### What does it load, and is it safe?

Each page is loaded in a **real headless browser**: it runs the page's JavaScript and loads its images, scripts and third-party tags, as a visitor's browser would. Requests to private, loopback, link-local and cloud-metadata addresses are refused, including redirects and subresources, so internal or localhost URLs can't be audited (they get a free `Blocked` row). Every audit uses a new, empty browser profile that is deleted afterwards.

#### Other limits

- Only http and https URLs. URLs with a user name or password aren't supported.
- Each audit gets *Timeout per URL* (default 90 seconds); slower audits are stopped and get a free `Timed out` row.
- Lighthouse 12 removed the PWA category, so it isn't offered.
- The same URL listed twice is audited once.

### Related Actors

| Actor | Use it to |
|---|---|
| [Broken Link Checker](https://apify.com/charlesmarkette/broken-link-checker-crawl-website-404) | Crawl a whole site to find 404s and redirects, then audit its key pages here. |
| [Bulk URL Status Checker](https://apify.com/charlesmarkette/bulk-url-status-checker-redirects-ssl) | Check status codes, redirect chains and SSL expiry for thousands of URLs, fast and cheap. |
| [Sitemap URL Extractor & New Page Monitor](https://apify.com/charlesmarkette/sitemap-url-extractor-new-page-monitor) | Get every page URL of a site from its sitemaps to audit here. |
| [Website Tech Stack Detector](https://apify.com/charlesmarkette/website-tech-stack-detector-shopify-cms-analytics) | See which CMS, shop platform and tags are behind a slow site. |
| [Dataset Filter & Transform](https://apify.com/charlesmarkette/dataset-filter-transform-rename-flatten-fields) | Keep only pages under a score, flatten `scores` and `metrics` into columns, or rename fields before export. |

# Actor input Schema

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

Pages to audit, one per line. Full URLs or bare domains (example.com becomes https://example.com/). Each URL is audited once per strategy.

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

Mobile uses Lighthouse's default phone emulation with simulated slow 4G and a 4x slower CPU (like PageSpeed Insights' mobile tab). Desktop uses Lighthouse's desktop settings. Both gives two rows (and two charges) per URL.

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

Which Lighthouse categories to score. Core Web Vitals metrics (LCP, CLS, TBT…) come with Performance.

## `includeAudits` (type: `boolean`):

Adds topOpportunities: up to 10 failing audits per row (title, displayed value and estimated savings in ms or bytes), biggest savings first.

## `psiApiKey` (type: `string`):

Leave empty to run Lighthouse here in headless Chrome (no key needed). If you paste your own Google API key, each URL is sent to Google's PageSpeed Insights API instead: you also get real-user Chrome UX Report (CrUX) field data, and Google's daily quota for your key applies. The key is stored encrypted and never written to the output.

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

How many audits run at the same time. Local Lighthouse: at most 3, and about 900 MB of memory each (it is lowered to fit the run's memory). Parallel audits share the CPU, which lowers performance scores, so keep 1 when you compare performance numbers. With your own API key: up to 10.

## `timeoutSecsPerUrl` (type: `integer`):

An audit that takes longer is stopped and gets a free error row. Heavy pages on mobile settings can take 60 seconds or more.

## `maxResults` (type: `integer`):

Optional cap on output rows (one row per URL per device), errors included.

## Actor input object example

```json
{
  "urls": [
    "https://www.python.org",
    "https://apify.com"
  ],
  "strategy": "mobile",
  "categories": [
    "performance",
    "accessibility",
    "best-practices",
    "seo"
  ],
  "includeAudits": false,
  "maxConcurrency": 1,
  "timeoutSecsPerUrl": 90
}
```

# Actor output Schema

## `items` (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://www.python.org",
        "https://apify.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("charlesmarkette/pagespeed-lighthouse-audit-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",
        "https://apify.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("charlesmarkette/pagespeed-lighthouse-audit-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",
    "https://apify.com"
  ]
}' |
apify call charlesmarkette/pagespeed-lighthouse-audit-core-web-vitals --silent --output-dataset

```

## MCP server setup

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