How to bulk-audit Core Web Vitals for hundreds of URLs without a PageSpeed API key
Run real Lighthouse on a URL list or a whole sitemap, export lab Core Web Vitals to CSV, and skip the PageSpeed Insights quota.
How to bulk-audit Core Web Vitals for hundreds of URLs without a PageSpeed API key
PageSpeed Insights and the Lighthouse panel in Chrome DevTools are the right tools when you are debugging one URL. They are the wrong shape of tool when you need a spreadsheet of lab Core Web Vitals for a sitemap, every product template after a release, or a monthly report across a client's sites.
The usual next step is Google's PageSpeed Insights API. It works, and it is free up to a quota, but that shared quota is easy to burn through once you start auditing hundreds of URLs, on mobile and desktop, more than once. Local Lighthouse on your laptop also works, if you are happy to install Chrome, walk a sitemap, keep median runs and stitch the JSON together yourself.
This tutorial shows a practical way to run real Google Lighthouse on a list of URLs or a whole sitemap, with no PageSpeed Insights API key and no shared quota, using Bulk Lighthouse Audit on Apify. You can do the same job in the browser in a couple of minutes, or from Python or curl if you want it on a schedule or in CI. Only audit sites you own or are allowed to test.
What you will get
Each row in the dataset is one page on one device. Fields include:
url,finalUrl,strategy(mobileordesktop) andenginescores: Performance, Accessibility, Best Practices and SEO (0–100)- Lab metrics:
largestContentfulPaintMs,cumulativeLayoutShift,totalBlockingTimeMs, plus FCP, Speed Index, TTI, TTFB (serverResponseTimeMs),totalByteWeight,domSizeandnetworkRequests labCwv(LCP, CLS and TBT each ratedgood,needs-improvementorpoor) and an overalllabCwvAssessment- The
lcpElementandlcpBreakdownMs(time to first byte vs render delay) opportunities: the top fixes, with estimated time and bytes savedfailedAudits: failing accessibility, SEO and best-practice checks, listed by namepassedThreshold, if you set a performance budget- Optional
reportUrlfor a shareable HTML Lighthouse report (local engine only) - Optional
fieldData: real-user CrUX p75 LCP, INP, CLS, FCP and TTFB, if you add a free Google API key lighthouseVersion,runs,errorandauditedAt
The dataset has two views: Scores & Core Web Vitals, and Top fixes (one row per recommendation). A SUMMARY record in the key-value store holds site-level averages, the slowest pages and the lab Core Web Vitals pass rate (labCwvPassRate).
Lab Core Web Vitals here are LCP, CLS and TBT. TBT is Lighthouse's lab stand-in for INP. Real-user INP only appears in fieldData, and that needs a Google API key.
Option 1: run it from the Apify Console (no code)
- Create a free Apify account if you do not have one. The free plan includes $5 of monthly credit, which covers hundreds of local audits.
- Open Bulk Lighthouse Audit and click Try for free.
- Paste one or more URLs, or a sitemap URL. The form is prefilled with
https://example.com. Sitemap indexes and.gzfiles are fine. - Keep Devices on
mobilefor a first run (a simulated mid-range phone on slow 4G, the same method PageSpeed Insights uses). Adddesktopwhen you need both. - Leave Save full HTML Lighthouse reports off unless you need the shareable report. Leave the engine on Auto: with no key, that runs Lighthouse 13 inside the Actor's own Chrome.
- Optionally set Max URLs (default 50, counted per device), a Performance budget (for example 80) and Runs per URL. Google recommends 3–5 runs and taking the median when you need stable numbers; each run is billed.
- Click Start. A single mobile audit usually finishes in well under a minute.
- Open the Dataset tab for scores and Core Web Vitals, and the key-value store for the
SUMMARYrecord. Export CSV or JSON, or pull the results through the API.
That is the whole one-off workflow. Schedule the same input weekly or after each release when you want a history.
Example input
A sitemap, mobile only, capped at 50 pages, with a performance budget:
{
"sitemapUrls": ["https://crawlee.dev/sitemap.xml"],
"maxUrls": 50,
"strategies": ["mobile"],
"performanceThreshold": 80,
"runsPerUrl": 1,
"saveHtmlReports": false
}
A short URL list on mobile and desktop:
{
"urls": [
"https://www.python.org",
"https://www.wikipedia.org",
"https://example.com"
],
"strategies": ["mobile", "desktop"],
"performanceThreshold": 80,
"maxConcurrency": 1
}
You can combine urls and sitemapUrls in one run. The Actor collects the sitemap loc entries, de-duplicates them, and audits each page up to maxUrls per device.
Option 2: run it from Python or curl
Install the official client and call the Actor. Replace the token with your Apify API token (Console → Settings → Integrations). Do not commit the token.
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("refillflows/bulk-lighthouse-audit").call(
run_input={
"sitemapUrls": ["https://example.com/sitemap.xml"],
"maxUrls": 50,
"strategies": ["mobile"],
"performanceThreshold": 80,
"saveHtmlReports": False,
}
)
dataset_id = run["defaultDatasetId"]
items = list(client.dataset(dataset_id).iterate_items())
print(f"Got {len(items)} audits")
for row in items[:5]:
print(row["url"], row["strategy"], row.get("scores"), row.get("labCwv"))
# Fail a CI job if any page missed the budget
failed = [r for r in items if r.get("passedThreshold") is False]
if failed:
raise SystemExit(f"{len(failed)} page(s) below the performance budget")
The same call with curl, waiting for the dataset and returning CSV:
curl -X POST \
"https://api.apify.com/v2/acts/refillflows~bulk-lighthouse-audit/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN&format=csv" \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://example.com"],
"strategies": ["mobile"],
"saveHtmlReports": false
}'
If you already have a DATASET_ID from a finished run, you can also download it with:
https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=csv&token=YOUR_APIFY_TOKEN
The site-level SUMMARY record lives in the run's default key-value store:
https://api.apify.com/v2/key-value-stores/<VALUE_STORE_ID>/records/SUMMARY?token=YOUR_APIFY_TOKEN
Schedule the Actor daily or weekly, set a performanceThreshold, and post rows where passedThreshold is false to Slack, Sheets or a webhook from a CI pipeline.
Pricing, in practice
The Actor is pay-per-event. You are charged for audits that complete, and platform usage is included in that price. Pages that cannot load are reported with an error field and are not charged.
| What | Price |
|---|---|
| Actor start | $0.005 per run (charged per GB of memory; this Actor defaults to 2 GB, so $0.01) |
| Lighthouse audit (local engine, no key) | $0.01 per page per device |
| Audit with your PageSpeed API key | $0.003 per page per device |
A 100-page site on mobile is about $1.00, or $2.00 for mobile plus desktop, plus the $0.01 start charge. With your own Google key it is $0.30 per 100 pages. If you set Runs per URL to 3, you pay for three audits per page. The $5 free-plan credit covers hundreds of local audits, or more if you switch to the PSI engine. Set Max URLs or a max cost per run if you are exploring.
Tips that save time
- No key is required. The default Auto engine runs local Lighthouse when no Google API key is set, so there is no PSI quota to run out of. Add a key only if you want the cheaper PSI engine and real-user CrUX field data (
includeFieldDatadefaults to on once a key is present; it falls back from URL-level to origin-level data). - To get a free Google API key, open Google Cloud Console, enable the PageSpeed Insights API and the Chrome UX Report API, and create an API key. Paste it into Google API key.
- Lab scores vary slightly between machines. They come from a simulated Moto G Power on slow 4G. Use
runsPerUrlof 3 or more when you need precise numbers, and remember that each run is billed. - Keep
maxConcurrencyat 1 for the most accurate local scores (CPU contention skews metrics). Use 2–4 with 4 GB or more of memory when you need speed. The PSI engine can go higher, up to 10. - Fewer Lighthouse categories is faster. The default is all four (
performance,accessibility,best-practices,seo). You can drop the ones you do not need. - Leave HTML reports off for routine monitoring. Turn Save full HTML Lighthouse reports on when you want a shareable
reportUrlfor a specific page (local engine only). maxUrlsis per device. 50 URLs on mobile and desktop is 100 audits.- Failed loads are free. DNS errors, interstitials and pages that never paint are stored with an
errorand are not billed. The default timeout is 120 seconds per audit (auditTimeoutSecs).
May I audit any website?
The Actor loads only the pages you give it, the same way a normal browser visit does. Only audit sites you own or are allowed to test.
Further reading
- Bulk Lighthouse Audit on Apify Store
- PageSpeed Insights, which is still the fastest way to inspect a single URL
- Apify API docs
- Apify MCP server, if you want an agent to ask "audit this site's speed" against fresh data
If you also work with Shopify App Store listings or public Luma events, the same publisher has Shopify App Store Scraper and Luma Events Scraper.
Disclosure: I built the Bulk Lighthouse Audit tool this tutorial uses.
Comments
Join the discussionNo comments yet. Be the first to comment.