Bulk PageSpeed Auditor: Lighthouse and Core Web Vitals
Pricing
$8.00 / 1,000 url checkeds
Bulk PageSpeed Auditor: Lighthouse and Core Web Vitals
Audit a list of URLs with Google's official PageSpeed Insights API. Get Lighthouse scores, lab and real-user Core Web Vitals, and the top issues to fix, one row per URL and device. Failed audits are not charged.
Pricing
$8.00 / 1,000 url checkeds
Rating
0.0
(0)
Developer
Open Data Tools
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Bulk PageSpeed Auditor: Lighthouse and Core Web Vitals
This tool checks how fast and how well built a list of web pages is, using Google's official PageSpeed Insights service. For each page you get the four Lighthouse scores (Performance, Accessibility, Best practices and SEO, each out of 100), the main speed measurements, real-visitor Core Web Vitals where Google has them, and a short list of the biggest problems to fix. Paste in your URLs, choose mobile, desktop or both, and you get one row per page that you can sort, compare or export. You pay $0.008 for each page audited successfully on each device type, and failed audits are free.
Who it's for
- Agencies and freelancers auditing client sites, or a prospect's site before a pitch.
- Developers and site owners checking many pages at once instead of testing them one by one on the PageSpeed website.
- SEO teams tracking Core Web Vitals across key landing pages, product pages or templates.
- E-commerce and content teams comparing their pages with competitors' pages.
Why use it
- Official Google data: results come from Google's PageSpeed Insights API, the same engine as the PageSpeed Insights website. Your pages are not scraped.
- Many URLs in one run: add a list of pages and get every result in one table instead of testing them one at a time.
- Four scores per page: Performance, Accessibility, Best practices and SEO, each from 0 to 100. Leave out the categories you don't need.
- Lab and real-visitor numbers: lab measurements from a fresh test (such as Largest Contentful Paint and Total Blocking Time), plus Core Web Vitals from real Chrome users when Google has enough data for the page.
- What to fix first: up to 10 failed checks per page, sorted by the estimated time they would save.
- Mobile, desktop or both: choose one device type, or run both and get one row for each.
- No charge for failures: a page that can't be audited gets a row with the error message, and you are not charged for it. You pay $0.008 only for each successful audit. One bad URL never stops the run.
- Stays within your budget: set a maximum total charge for the run, and it stops auditing new URLs once that limit is reached.
How to use it
- Open the Actor in Apify Console and add the pages you want to check under URLs to audit. Each entry must start with
http://orhttps://. - Choose a Strategy: Mobile (the default), Desktop, or Both. Both gives you two rows per page and costs twice as much.
- Optionally untick Categories you don't need. All four are selected by default.
- Click Start. Each page takes Google a few seconds to test, so larger lists take a few minutes.
- Open the Output tab. The overview shows the URL, device, status, the four scores and the key speed numbers for each page. Rows with status
errorinclude the reason. Export everything as JSON, CSV or Excel. - To track scores over time, create a schedule under Schedules (weekly, for example) with the same input, and add an integration such as Google Sheets, Slack, Zapier or Make, or a webhook, to send the results on.
Pricing
You pay $0.008 for each page audited successfully on each device type. Rows with status error are not charged. You can set a maximum total charge for a run, and the run stops auditing new URLs once it is reached.
Example: auditing 100 pages on mobile costs $0.80. Auditing the same 100 pages on both mobile and desktop gives 200 rows and costs $1.60. Running that mobile audit once a week costs about $3.20 over four weeks.
API and reference
This section is for developers and AI agents. It lists every input and output field, an example record and code examples.
Summary
Sends each input URL to the Google PageSpeed Insights API v5 (https://www.googleapis.com/pagespeedonline/v5/runPagespeed) and writes one dataset row per URL and strategy. Each row contains the Lighthouse category scores (0 to 100), lab metrics, Chrome UX Report (CrUX) p75 field metrics when available, and up to 10 failed audits sorted by estimated savings. The Actor does not fetch the pages itself. Failed audits become status: "error" rows and are not charged. They do not fail the run unless no audit succeeded at all (for example when the shared daily quota is used up), in which case the run ends as Failed with the reason in the status message. SUMMARY is still saved.
Input
| Field | Type | Default | Description |
|---|---|---|---|
startUrls | array | required | URLs to audit, as {"url": "..."} objects (plain strings are also accepted). Only http and https URLs on a public domain or public IP address are valid. Exact duplicates are removed. An invalid URL produces an error row without calling the API. An empty list fails the run. |
strategy | mobile, desktop, both | mobile | Lighthouse device profile. both produces two rows per URL, each charged. |
categories | string[] | all four | Any of performance, accessibility, best-practices, seo. Scores for unselected categories are null. |
maxConcurrency | integer | 5 | 1 to 20. Number of PageSpeed API requests in parallel. Values outside the range are clamped. |
apiKey | string (secret) | none | Optional. A Google API key with the PageSpeed Insights API enabled. Leave empty to use the key configured on the Actor, which is shared and limited to 1,000 audits per run; if Google's shared daily quota runs out, the run stops and asks you to add your own key. |
Output
One dataset row per URL and strategy.
| Field | Type | Description |
|---|---|---|
url | string or null | URL as given in the input. null if an entry had no url value |
finalUrl | string or null | URL after redirects, as reported by Lighthouse. null on error rows |
strategy | string | mobile or desktop |
fetchedAt | string | ISO 8601 UTC time of the audit (time of the attempt on error rows) |
performanceScore | integer or null | 0 to 100 |
accessibilityScore | integer or null | 0 to 100 |
bestPracticesScore | integer or null | 0 to 100 |
seoScore | integer or null | 0 to 100 |
lcpMs | number or null | Lab Largest Contentful Paint, ms |
fcpMs | number or null | Lab First Contentful Paint, ms |
clsScore | number or null | Lab Cumulative Layout Shift |
tbtMs | number or null | Lab Total Blocking Time, ms |
speedIndexMs | number or null | Lab Speed Index, ms |
ttiMs | number or null | Lab Time to Interactive, ms |
fieldLcpMs | number or null | CrUX p75 Largest Contentful Paint, ms. null when Google has no field data |
fieldInpMs | number or null | CrUX p75 Interaction to Next Paint, ms |
fieldClsScore | number or null | CrUX p75 Cumulative Layout Shift |
fieldOverallCategory | string or null | CrUX overall rating: FAST, AVERAGE or SLOW |
topIssues | object[] | Up to 10 failed Lighthouse audits (score below 0.9), sorted by savingsMs descending (a missing saving counts as 0), then by score ascending, then by id. The metric audits behind the lab fields (LCP, TBT and so on) are left out because they are measurements, not fixes. Empty on error rows |
topIssues[].id | string | Lighthouse audit ID, for example render-blocking-insight or unused-css-rules |
topIssues[].title | string | Audit title |
topIssues[].score | number | Audit score, 0 to 1 |
topIssues[].displayValue | string or null | Lighthouse summary, for example Est savings of 128 KiB |
topIssues[].savingsMs | number or null | Estimated time saving, ms. Taken from the audit's overall savings, or else the largest time-based metric saving (CLS savings are ignored). null if Lighthouse gives none |
status | string | ok or error |
errorMessage | string or null | Reason when status is error, otherwise null |
Example record from a real run (Apify run on 21 October 2026, mobile). Values are as returned, except that topIssues is trimmed to the first 3 of its 10 entries. Lighthouse puts a non-breaking space before units in displayValue, shown here as \u00a0:
{"url": "https://www.python.org","finalUrl": "https://www.python.org/","strategy": "mobile","fetchedAt": "2026-10-21T06:17:41.491Z","performanceScore": 83,"accessibilityScore": 75,"bestPracticesScore": 100,"seoScore": 92,"lcpMs": 3953,"fcpMs": 2721,"clsScore": 0.003,"tbtMs": 9,"speedIndexMs": 2721,"ttiMs": 3953,"fieldLcpMs": 1270,"fieldInpMs": 88,"fieldClsScore": 0,"fieldOverallCategory": "FAST","topIssues": [{"id": "render-blocking-insight","title": "Render-blocking requests","score": 0,"displayValue": "Est savings of 2,590\u00a0ms","savingsMs": 2600},{"id": "unused-css-rules","title": "Reduce unused CSS","score": 0,"displayValue": "Est savings of 128\u00a0KiB","savingsMs": 620},{"id": "unused-javascript","title": "Reduce unused JavaScript","score": 0,"displayValue": "Est savings of 59\u00a0KiB","savingsMs": 450}],"status": "ok","errorMessage": null}
The run's default key-value store gets a SUMMARY record with ok (integer), error (integer), keyless (boolean, true if the run had no API key at all) and stoppedEarly (string or null, "maximum total charge reached" when the charge limit stopped the run).
Charge limit: when a charge reports that the run's maximum total charge has been reached, no new audits start. URLs not yet started are skipped and get no row. Audits already in progress at that moment finish and their rows are saved.
Error handling: invalid URLs fail without an API request. This includes missing URLs, schemes other than http and https, localhost, hosts without a dot, reserved names (.invalid, .test, .localhost, .example, .local, .internal) and private or reserved IP addresses. HTTP 429, 500, 502, 503 and 504 responses are retried, up to 4 attempts in total. The wait follows the API's Retry-After header (capped at 60 seconds), or else 2, 4 and 8 seconds. Two cases are not retried: a 429 for the daily quota, and a "Lighthouse returned error" message, which means the page itself failed to load. Other API errors also give an error row straight away.
Pricing details
Pay per event: url-checked at $0.008, charged once for each row with status: "ok", so once per URL per strategy. Rows with status: "error" are not charged. The run stops starting new audits once the user's maximum total charge is reached.
Usage
Apify API (synchronous run, returns dataset items):
curl -X POST "https://api.apify.com/v2/acts/opendata-tools~bulk-pagespeed-auditor/run-sync-get-dataset-items" \-H "Authorization: Bearer $APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"startUrls": [{"url": "https://example.com"},{"url": "https://www.python.org"}],"strategy": "mobile","categories": ["performance", "seo"]}'
Python (pip install apify-client):
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("opendata-tools/bulk-pagespeed-auditor").call(run_input={"startUrls": [{"url": "https://example.com"},{"url": "https://www.python.org"},],"strategy": "both",})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["status"] == "ok":print(item["url"], item["strategy"], item["performanceScore"], item["lcpMs"])else:print(item["url"], item["strategy"], "error:", item["errorMessage"])
JavaScript (npm install apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('opendata-tools/bulk-pagespeed-auditor').call({startUrls: [{ url: 'https://example.com' },{ url: 'https://www.python.org' },],strategy: 'desktop',});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const item of items) console.log(item.url, item.status, item.performanceScore, item.topIssues.length);
For recurring audits, create an Apify schedule with a fixed input and attach a webhook on ACTOR.RUN.SUCCEEDED to receive results.
Limits
- Results come from Google's API, so Lighthouse scores vary a little from run to run, as they do on the PageSpeed Insights website.
- Google can only test pages it can reach from the public internet. Pages behind a login, on a private network or blocking Google's tester return an error row.
- Each PageSpeed API request times out after 150 seconds (retries can add more). The default run timeout is 30 minutes, so very long lists may need a longer timeout or to be split into several runs.
- Field data (
field*fields) comes from the Chrome UX Report and isnullfor pages without enough real-visitor traffic.
FAQ
Do I need my own Google API key?
No. Leave PageSpeed API key empty and the Actor uses its own key. If you have a Google API key with the PageSpeed Insights API enabled, you can enter it, and requests then count against your own Google quota.
Why do some rows have status error?
The row's errorMessage gives the reason, for example an invalid or private URL, a page Google could not load, Google's daily quota being used up, or Google's API being busy after all retries. Error rows are not charged.
Why are the field data columns empty?
Real-visitor data comes from Google's Chrome UX Report, which only covers pages with enough Chrome traffic. Newer or low-traffic pages have no field data, so those fields are null. The lab scores and measurements are still filled in.
Does it crawl my site to find pages?
No. It audits exactly the URLs you give it. Add every page you want checked to URLs to audit.
What happens when my maximum total charge is reached?
The run stops starting new audits. Pages already being tested finish and are saved, the rest are skipped with no row, and the run's SUMMARY record shows stoppedEarly. Run again with the skipped URLs to finish the list.
Why is Both twice the price?
Mobile and desktop are separate Lighthouse tests with separate results, so each device type gives its own row and its own $0.008 charge. A page audited on both costs $0.016.
Issues and feature requests: use the Issues tab.