# Bulk PageSpeed & Website Health Audit: Lighthouse, Web Vitals (`southern-data/website-speed-audit`) Actor

Test hundreds of websites with Google Lighthouse: performance, SEO, accessibility scores, Core Web Vitals and top fixes, plus a health check (HTTPS, SSL expiry, mobile-ready, title, platform, copyright year) and a ready-to-pitch problem summary per site. No API key needed.

- **URL**: https://apify.com/southern-data/website-speed-audit.md
- **Developed by:** [Southern Data](https://apify.com/southern-data) (community)
- **Categories:** SEO tools, Lead generation, Developer tools
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $27.00 / 1,000 page speed test (lighthouse)s

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 & Website Health Audit: Lighthouse, Web Vitals

Give it a list of websites (or a lead list from another Actor). For each one it runs a **real Google Lighthouse test in Chrome** and a quick **website health check**, and returns **one spreadsheet-ready row**: performance / accessibility / best-practices / SEO scores, Core Web Vitals (LCP, CLS, TBT, FCP, Speed Index), page weight, the top fixes Lighthouse suggests, HTTPS and certificate expiry, mobile viewport, title / meta description / H1, noindex, platform (WordPress, Wix, Squarespace, Shopify…), copyright year, analytics, plus a plain-English **problem list and pitch summary** you can paste into an outreach email.

Built for **web-design, SEO and marketing agencies** prospecting local businesses, and for anyone who needs to check many sites at once.

### Read this first: limits

- **Lab test, not Google's own servers.** Without an API key the test runs Lighthouse 13 in Chrome on Apify's machines with Google's default mobile throttling. Scores are in the same family as [pagespeed.web.dev](https://pagespeed.web.dev) but **not identical**. Lighthouse scores also change a little run to run: in our cloud tests the same page usually came back **within ±3 points** (15 of 21 repeated sites), but a few varied by up to 24 points. Use the scores to sort and spot slow sites, not as exact numbers.
- **We could not compare against PageSpeed Insights.** Google's keyless PSI API answered "429 quota exceeded" every time we tried (1 Nov 2026), so there is no accuracy figure against PSI. If you need the exact pagespeed.web.dev numbers and real-visitor data, add your own free **Google API key** (see below). The own-key path is checked with bad and invalid keys, but **we have not tested it with a real key yet**.
- **Memory matters.** Apify gives 1 CPU core per 4 GB. Lighthouse on 1 core reads blocking time 2–5× higher, so the default is **8192 MB** (2 cores, one test at a time). Runs below 8 GB still work (min 2048 MB for speed tests) but each row says the numbers read high. `health-only` runs at 512 MB.
- **Very heavy sites can time out.** Each test has a 150-second hard limit. A test that times out, fails or is blocked is **free** and the row says why.
- **No bypassing.** Sites behind bot protection (Cloudflare challenge, 403) may refuse the test browser or the health check. We don't bypass bot checks, logins or cookie walls. When the health fetch is refused but the browser loads the page, the scores are given and the health columns are left blank, with a note.
- **Homepage only** unless you give a full page link. One page = one test per device.
- **Real-visitor Core Web Vitals (field data)** come only with your own Google API key.

### Pricing

| Event | Price | When |
|---|---|---|
| `page-speed-tested` | **$0.03** | One page tested on one device with Lighthouse (includes the health check) |
| `page-speed-tested-own-key` | **$0.005** | One page tested on one device via PageSpeed Insights with **your own** Google API key |
| `site-health-checked` | **$0.0015** | One page in `health-only` mode |

**Store discount tiers:** free-plan and Bronze users pay the prices above. Silver gets 5% off and Gold (and higher) gets 10% off.

**100 websites, mobile only, cost at most $3.00.** "Both" devices = 2 tests per page. **Free:** failed, timed-out, blocked, dead, parked and broken-certificate sites (speed mode), invalid input (Facebook / Instagram links, typos), duplicates, anything not processed, bad-input runs and the self-test. Set **Maximum cost per run** and the Actor stops cleanly there; untested sites go to the `REMAINING` record and are listed as `not_processed` (free).

### Quick start

1. Click **Try for free**.
2. Paste websites, one per line (`acmeplumbing.com.au`, `https://acme.com/services`, or an email like `info@acme.com.au`), **or** a link to a CSV / TXT file or Google Sheet ("Anyone with the link can view") in **websitesFileUrl**, **or** pick a dataset from another run in **datasetId**, e.g. [Local Business Leads from OpenStreetMap](https://apify.com/southern-data/osm-business-leads) or [Website Contact Finder](https://apify.com/southern-data/website-contact-finder). Name, phone and email columns are carried through to the output.
3. Click **Start**. Open the **Output** tab: *Overview*, *Speed & Core Web Vitals*, *Website health*, *Leads to pitch*, *Not tested / failed* or *All fields (CSV export)*. Download as CSV, Excel or JSON.

Via API:

```bash
curl -X POST "https://api.apify.com/v2/acts/southern-data~website-speed-audit/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"websites":["goodthingscafe.com.au","example.com"],"devices":"mobile"}'
```

Speed tests take about 20–40 s per page on the default 8 GB, one at a time: 100 pages ≈ 45–60 minutes. `health-only` checks 10 pages at a time from 1024 MB (3 at a time below that); 22 sites took 27 s at 512 MB.

### Example row

A real row (input `goodthingscafe.com.au`, 1 Nov 2026), trimmed:

```json
{
  "domain": "goodthingscafe.com.au", "siteStatus": "ok", "platform": "Squarespace",
  "pitchSummary": "Slow on mobile: Google Lighthouse speed score 39/100 (poor). Main content takes 12.8 s to appear on mobile (Google's target: 2.5 s or less). Heavy JavaScript blocks the page for 782 ms while loading (total blocking time; target 200 ms or less).",
  "issueCount": 5,
  "mobilePerformance": 39, "mobileAccessibility": 94, "mobileBestPractices": 73, "mobileSeo": 85,
  "mobileLcpSec": 12.8, "mobileLcpRating": "poor", "mobileCls": 0.003, "mobileTbtMs": 782,
  "mobilePageWeightKb": 2247, "mobileRequests": 84,
  "mobileTopFixes": "Reduce unused JavaScript (Est savings of 572 KiB); Render-blocking requests (Est savings of 5,820 ms); …",
  "httpsOk": true, "sslDaysLeft": 77, "httpRedirectsToHttps": true, "mobileViewport": true,
  "pageTitle": "Good Things", "metaDescriptionLength": 0, "h1Count": 0, "analytics": "Meta Pixel",
  "engine": "lighthouse", "lighthouseVersion": "13.6.0",
  "notes": "No copyright year in the page HTML; Real-visitor Core Web Vitals (field data) need your own Google API key; lab scores only",
  "chargedEvents": "page-speed-tested x1"
}
```

### Input

| Field | Meaning |
|---|---|
| `websites` | Domains, page links or email addresses. Duplicates are merged. |
| `websitesFileUrl` | Public CSV / TXT / Google Sheet. Uses the website / url / domain column; name / phone / email columns are carried over. |
| `datasetId` | A dataset from another Apify run (pick it in the dataset picker). Rows without a website are skipped, free. |
| `mode` | `full` (Lighthouse + health check, default) or `health-only` (no browser, about 17× cheaper). |
| `devices` | `mobile` (default), `desktop` or `both` (2 tests per page). |
| `googleApiKey` | Optional, secret. Your own PageSpeed Insights key: Google's exact numbers plus real-visitor data, $0.005 per test. |
| `maxWebsites` | Test only the first N pages (cheap trial). |
| `selfTest` | Run the built-in checks instead (no rows, no charge; the run fails if anything is broken). |

### What each field means

| Field | Meaning |
|---|---|
| `rowStatus` | `checked`, `invalid_input` or `not_processed` (spend cap, time limit or maxWebsites) |
| `siteStatus` | `ok`, `dead` (domain doesn't resolve), `unreachable`, `http_error`, `tls_error` (broken / expired certificate), `parked`, `blocked` (bot check), `not_html` |
| `issues`, `issueCount`, `pitchSummary` | Plain-English problems sorted by importance; the summary is the top 3 |
| `mobile…` / `desktop…` | `Status` (`tested` / `failed` / `skipped` / `not_requested`), the 4 Lighthouse scores (0–100), `LcpSec`, `Cls`, `TbtMs`, `FcpSec`, `SpeedIndexSec` with good / needs-improvement / poor ratings, `PageWeightKb`, `Requests`, `TopFixes`, `SeoIssues`, `AccessibilityIssues`, `Error` (why a test failed) |
| `field…` | Real-visitor Core Web Vitals from Chrome UX Report (own API key only) |
| `httpsOk`, `sslDaysLeft`, `sslIssuer`, `httpRedirectsToHttps` | HTTPS and certificate health |
| `mobileViewport`, `pageTitle`, `titleLength`, `metaDescriptionLength`, `h1Count`, `noindex`, `structuredData`, `openGraph`, `favicon` | On-page basics from the homepage HTML |
| `platform`, `copyrightYear`, `analytics`, `serverResponseMs`, `htmlKb`, `language` | Site build details. An old copyright year often means a neglected site |
| `businessName`, `businessPhone`, `businessEmail` | Carried over from your file or dataset |
| `engine`, `lighthouseVersion`, `cpuBenchmark`, `testedAt` | How and when it was measured |
| `notes`, `reason` | Why anything is blank or unusual |
| `chargedEvents` | What this row cost |

Blank always means "not measured", and `notes` / `…Error` / `reason` say why.

### Tested (1 Nov 2026)

- 34 cloud runs on the final builds (1.0.6–1.0.11): 33 succeeded; 1 ran out of memory in `health-only` at 256 MB, so the minimum is now 512 MB. 13 of 13 runs since that fix succeeded.
- 94 Lighthouse tests at 8 GB on 33 real sites (small-business WordPress / Squarespace / Wix / Shopify sites, a council page, big brands, heavy JS): **94 of 94 tested**, 0 timeouts. Dead, broken-certificate and error pages were skipped free (15). In a separate 4 GB run one site blocked the test browser (free, reported).
- 223 output rows were checked against the dataset schema with 0 errors. Bad input (empty, wrong key, 404 file, unknown dataset, too little memory) always ended cleanly with a message and $0 charged.

### Pairs with

[Local Business Leads from OpenStreetMap](https://apify.com/southern-data/osm-business-leads) → **this Actor** (find the slow, broken or outdated sites) → [Website Contact Finder](https://apify.com/southern-data/website-contact-finder) (get the email to pitch).

### Responsible use

Each site gets a single homepage load per device plus one quick health fetch. Follow anti-spam law (Spam Act 2003 in Australia, CAN-SPAM, GDPR) when contacting businesses.

# Actor input Schema

## `websites` (type: `array`):

One per line: "acmeplumbing.com.au" (its homepage is tested), a full page link ("https://acme.com/services", tested as given) or an email address (its website is tested). Duplicates are removed. Facebook / Instagram / directory links are not websites and are listed as rowStatus=invalid_input (free).

## `websitesFileUrl` (type: `string`):

Public URL of a .txt or .csv file, or a Google Sheets link shared as "Anyone with the link can view". Uses the column headed website / url / domain / homepage (or the column with the most websites). Columns named name / phone / email are carried into the output so the results are ready to pitch. Up to 100,000 rows.

## `datasetId` (type: `string`):

Pick (or paste the ID of) a dataset from another run, e.g. Local Business Leads from OpenStreetMap or Website Contact Finder. Each row's website is tested and its name / phone / email are carried over. Rows without a website are skipped (free). This Actor only gets read access to the dataset you choose.

## `mode` (type: `string`):

"full": Google Lighthouse speed test (scores, Core Web Vitals, top fixes) plus the website health check. "health-only": no browser, just the quick health check (HTTPS, certificate, mobile viewport, title, meta description, H1, noindex, platform, copyright year, analytics, response time) — about 17x cheaper and works at 512 MB memory, good for filtering a big lead list first.

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

Mobile is what Google uses for ranking and what most small-business visitors use. "both" runs two tests per page (charged as two).

## `googleApiKey` (type: `string`):

Optional. With your own free Google API key (Google Cloud → enable "PageSpeed Insights API" → Credentials → API key), tests run on Google's PageSpeed Insights servers — the same test pagespeed.web.dev runs — and add real-visitor Core Web Vitals from the Chrome UX Report. Cheaper per test ($0.005), because Google does the work. Not yet tested by us with a real key. Google's free quota is 25,000 tests a day per key. Without a key, Lighthouse runs in Chrome on the Apify cloud (lab data only).

## `maxWebsites` (type: `integer`):

Test only the first N pages — handy for a cheap trial on a big file. The rest are skipped (not charged) and listed in the REMAINING record.

## `selfTest` (type: `boolean`):

Ignore the inputs above and run the built-in check instead: scoring rules, a health check and a real Lighthouse test of a built-in page, and two live sites. Writes a SELF_TEST record, adds no dataset rows, charges nothing, and FAILS the run if anything is broken.

## Actor input object example

```json
{
  "websites": [
    "example.com",
    "newcastle.nsw.gov.au"
  ],
  "mode": "full",
  "devices": "mobile",
  "maxWebsites": 0,
  "selfTest": false
}
```

# Actor output Schema

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

One row per page: speed scores, Core Web Vitals, health checks and a pitch summary. Views: Overview, Speed, Health, Leads to pitch, Not tested, All fields.

## `leads` (type: `string`):

Business name / phone / email (when your input had them) with the problems found.

## `runSummary` (type: `string`):

RUN_SUMMARY record: pages tested, speed tests OK / failed, status counts, charged events, seconds per page and any stop reason.

## `remaining` (type: `string`):

REMAINING record: inputs not tested in this run (spend cap, run timeout or maxWebsites), ready to paste into the next run. Only present when some were left.

## `selfTest` (type: `string`):

SELF_TEST record: results of the health check. Only present when the Actor runs with selfTest: true.

# 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 = {
    "websites": [
        "example.com",
        "newcastle.nsw.gov.au"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("southern-data/website-speed-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 = { "websites": [
        "example.com",
        "newcastle.nsw.gov.au",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("southern-data/website-speed-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 '{
  "websites": [
    "example.com",
    "newcastle.nsw.gov.au"
  ]
}' |
apify call southern-data/website-speed-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,southern-data/website-speed-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/Ou8GzB708EN13lInc/builds/9Vu3k634F3dYW4OwD/openapi.json
