Bulk PageSpeed & Core Web Vitals Auditor (Lab + CrUX)
Pricing
from $6.00 / 1,000 url auditeds
Bulk PageSpeed & Core Web Vitals Auditor (Lab + CrUX)
Audit many URLs with Google PageSpeed Insights in one run. Get Lighthouse scores, lab metrics (LCP, CLS, TBT, FCP, Speed Index, TTI) and real-user Chrome UX Report data (LCP, INP, CLS, FCP, TTFB) with pass/fail against Google's thresholds. One flat row per URL and device.
Pricing
from $6.00 / 1,000 url auditeds
Rating
0.0
(0)
Developer
Birchwood Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Security
Limited permissionsShare
Run Google PageSpeed Insights on a whole list of URLs in one go. For each URL and device you get one flat row with:
- Lighthouse scores for Performance, Accessibility, Best Practices and SEO (0–100)
- Lab metrics: LCP, CLS, TBT, FCP, Speed Index, TTI
- Real-user field data from the Chrome UX Report (CrUX): p75 LCP, INP, CLS, FCP and TTFB, for the URL and for the whole origin, each rated good / needs-improvement / poor against Google's thresholds, plus an overall Core Web Vitals passed/failed result
- The top opportunities (sorted by estimated savings) and failing diagnostics
fetchedAtand a clear error message for any URL that failed
It uses Google's official PageSpeed Insights v5 API and, if you turn it on, the Chrome UX Report API. It doesn't scrape anything and doesn't need a browser or proxies.
Why field data matters
Lab tests (Lighthouse) simulate a single page load, so they can't measure INP (Interaction to Next Paint). INP needs real user interactions. Google's Core Web Vitals assessment is based on real-user CrUX data at the 75th percentile, and this Actor puts that data next to the lab scores so you can see both in the same row.
Use cases
- SEO or agency audits of a client's key pages, mobile and desktop
- Comparing your Core Web Vitals with competitors' sites
- Regression checks after a deploy (schedule the Actor and compare runs)
- Finding which pages in a list fail CWV and the biggest fixes for each
- Feeding scores into Google Sheets, Looker Studio, Make, Zapier or n8n through Apify integrations
Input
| Field | Description |
|---|---|
urls (required) | Pages to audit. https:// is added if it's missing. |
strategy | mobile (default), desktop, or both. both returns two rows per URL. |
categories | Any of performance, accessibility, best-practices, seo. All four are on by default. |
includeFieldData | Real-user CrUX data from PSI (default on). |
useCruxApi | Also queries the CrUX API directly. Adds cruxUrl* / cruxOrigin* columns and the collection period. Needs an API key with the Chrome UX Report API enabled. |
googleApiKey | Optional secret field for your own Google API key. If you leave it empty, the Actor uses its built-in key, if one is configured. |
maxConcurrency | Number of parallel audits, 1–10 (default 4). |
requestTimeoutSecs | Timeout per request, 30–180 s (default 90). |
maxOpportunities | How many opportunities and diagnostics to keep per row (default 5). |
{"urls": ["https://web.dev/", "https://www.wikipedia.org/"],"strategy": "both","categories": ["performance", "seo"],"includeFieldData": true}
Output
One dataset item per URL × strategy. The example below is illustrative only and uses synthetic values, not a real measurement:
{"url": "https://fixture.example/","finalUrl": "https://fixture.example/","strategy": "mobile","status": "ok","performanceScore": 87, "accessibilityScore": 95, "bestPracticesScore": 100, "seoScore": 90,"labLcpMs": 2812, "labCls": 0.0421, "labTbtMs": 151, "labFcpMs": 1204, "labSpeedIndexMs": 2100, "labTtiMs": 3901,"fieldUrlLcpP75": 2300, "fieldUrlLcpRating": "good","fieldUrlInpP75": 250, "fieldUrlInpRating": "needs-improvement","fieldUrlClsP75": 0.05, "fieldUrlClsRating": "good","fieldUrlFcpP75": 1900, "fieldUrlTtfbP75": 700,"fieldUrlCwvAssessment": "failed","fieldOriginLcpP75": 2000, "fieldOriginCwvAssessment": "failed","hasFieldData": true,"topOpportunities": [{"id": "unused-javascript", "title": "Reduce unused JavaScript", "savingsMs": 900, "savingsBytes": 120000}],"topDiagnostics": [{"id": "mainthread-work-breakdown", "title": "Minimize main-thread work", "score": 0.2, "displayValue": "4.1 s"}],"opportunitiesSummary": "Reduce unused JavaScript (~900 ms); Eliminate render-blocking resources (~450 ms)","lighthouseVersion": "12.0.0","fetchedAt": "2026-11-04T23:55:00+00:00","errorCode": null, "errorMessage": null}
Every field-data metric comes with ...P75 and ...Rating columns, for fieldUrl, fieldOrigin, and (if you turn on the CrUX API) cruxUrl and cruxOrigin. Times are in milliseconds, and CLS has no unit.
Thresholds used (Google, p75):
| Metric | Good | Poor |
|---|---|---|
| LCP | ≤ 2500 ms | > 4000 ms |
| INP | ≤ 200 ms | > 500 ms |
| CLS | ≤ 0.1 | > 0.25 |
| FCP | ≤ 1800 ms | > 3000 ms |
| TTFB | ≤ 800 ms | > 1800 ms |
CwvAssessment is passed when LCP, INP and CLS are all good. If INP is missing, LCP and CLS decide on their own. When there's no data the field is null.
Values are never made up. When Google returns nothing for a metric, its column is null. If PSI quietly swaps in origin data for a URL (origin_fallback), the URL-level columns stay null and the origin data appears only in the fieldOrigin* columns.
Failed URLs get a row with status: "error", plus errorCode (the HTTP status) and errorMessage (Google's message). The key-value store has a RUN_SUMMARY record with the counts.
Pricing (pay per event)
| Event | Price | When it's charged |
|---|---|---|
audit | $0.006 | Each successful URL × strategy row |
field-data | $0.002 | Added only when that row actually contains real-user CrUX data |
- Failed URLs aren't charged.
- A mobile + desktop audit of one URL with field data costs 2 × ($0.006 + $0.002) = $0.016.
- The Actor respects your maximum cost per run: it stops cleanly before going over the limit, and everything saved up to that point stays in the dataset.
Limits and quota notes
- Each PageSpeed audit runs on Google's servers and takes roughly 10–30 s per URL (observed in our Nov 2026 tests). A test run with the default 2 URLs on mobile finished in about 32 s on Apify. Concurrency is kept modest on purpose.
- Opportunities include both classic Lighthouse audits and the newer Lighthouse 13 "insights" (e.g. Render-blocking requests). Google currently returns Lighthouse 13.x.
- Google may return a cached result if the same URL and device were audited in the last few minutes, so back-to-back runs can show identical numbers.
- Google enforces PSI and CrUX API quotas per Google Cloud project. Without a key, Google often rejects requests with
429 Quota exceeded, and those rows show up as errors and aren't charged. For large or frequent runs, add your own free API key ingoogleApiKeyso you use your own quota. You can see your quota in the Google Cloud console. - The Actor retries 429/5xx errors and network errors with capped backoff. It saves each result as soon as it's ready, and it stops gracefully before the run timeout.
- CrUX only has field data for pages and origins with enough Chrome traffic. Small sites often have none.
- Lighthouse scores vary from run to run, so compare trends rather than single numbers.
FAQ
Do I need a Google API key? Not if the Actor has a built-in key configured. Adding your own makes sure you don't run into shared quota limits. To get one, go to Google Cloud console → APIs & Services, enable the "PageSpeed Insights API" (and "Chrome UX Report API" if you use useCruxApi), then create an API key.
Is my key stored? It's a secret input field: Apify encrypts it, and the Actor never writes it to the dataset or the logs.
Why is field data empty? Google has no CrUX data for that URL or origin, usually because there isn't enough traffic. In that case you only pay for the audit event.
Why do my scores differ from pagespeed.web.dev? It's the same API, but Lighthouse results naturally vary between runs.
Can it audit pages behind a login? No. PageSpeed Insights can only load public URLs.