Core Web Vitals and Lighthouse scores for up to 300 URLs per run, from Google's own PageSpeed Insights API. Every row pairs the field data real Chrome users produced over 28 days (LCP, INP, CLS) with the lab scores and the heaviest fixes Lighthouse found. Bring your own free Google key. Pay per URL.
One audited URL for one strategy, with its field data, lab scores and top fixes. Pages Lighthouse cannot load, invalid URLs and quota errors are never charged.
ACTOR=steadydata~pagespeed-insights-bulk URL="https://api.apify.com/v2/acts/$ACTOR/run-sync-get-dataset-items" curl -X POST "$URL?token=$APIFY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"urls": ["https://www.wikipedia.org", "https://www.gov.uk"], "apiKey": "<your apiKey>", "strategy": "mobile"}'
import os, requests ACTOR = "steadydata~pagespeed-insights-bulk" URL = f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items" rows = requests.post( URL, params={"token": os.environ["APIFY_TOKEN"]}, json={'urls': ['https://www.wikipedia.org', 'https://www.gov.uk'], 'apiKey': '<your apiKey>', 'strategy': 'mobile'}, timeout=900, ).json() # every row carries a status; failures are records, not exceptions ok = [r for r in rows if r.get("status") == "ok"] print(len(ok), "rows delivered")
const ACTOR = "steadydata~pagespeed-insights-bulk"; const url = `https://api.apify.com/v2/acts/${ACTOR}/run-sync-get-dataset-items` + `?token=${process.env.APIFY_TOKEN}`; const rows = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({"urls": ["https://www.wikipedia.org", "https://www.gov.uk"], "apiKey": "<your apiKey>", "strategy": "mobile"}), }).then((r) => r.json()); // one object per delivered row, same shape every time console.log(rows.filter((r) => r.status === "ok").length, "rows");
Point an MCP client at Apify's hosted server with this dataset in the tools list, or run our own server locally.
https://mcp.apify.com?tools=steadydata/pagespeed-insights-bulk # the tool arrives in your agent as steadydata--pagespeed-insights-bulk
| Field | Type | Name | What it does |
|---|---|---|---|
urlsrequired | array of string | URLs | One page URL per row, up to 300. A missing https:// is added for you. |
apiKeyrequired | string | Your Google API key | Required, and free. In Google Cloud: enable the PageSpeed Insights API for a project, then create an API key and restrict it to that one API. The key is yours, the quota is yours, and it is only used for these requests. |
strategy | string | Device | Which device Lighthouse simulates. 'Both' returns two rows per URL and is charged as two. |
allCategories | boolean | Also score accessibility and best practices | Off by default: performance and SEO are scored, and only those two columns are returned. On adds two more score columns, but Lighthouse then needs about two and a half times longer per URL, so keep runs under 250 URLs. |
locale | string | Report language | Optional BCP-47 code for the wording of the fixes, for example nl or de. Leave empty for English. |
{
"url": string | null,
"finalUrl": string | null,
"strategy": string | null,
"fetchedAt": string | null,
"lighthouseVersion": string | null,
"performanceScore": integer | null,
"seoScore": integer | null,
"accessibilityScore": integer | null,
"bestPracticesScore": integer | null,
"fieldOverall": string | null,
"fieldLcpMs": integer | null,
"fieldLcpRating": string | null,
"fieldInpMs": integer | null,
"fieldInpRating": string | null,
"fieldCls": number | null,
"fieldClsRating": string | null,
"fieldFcpMs": integer | null,
"fieldFcpRating": string | null,
"fieldTtfbMs": integer | null,
"fieldTtfbRating": string | null,
"originOverall": string | null,
"labLcpMs": integer | null,
"labFcpMs": integer | null,
"labCls": number | null,
"labTbtMs": integer | null,
"labSpeedIndexMs": integer | null,
"labTimeToInteractiveMs": integer | null,
"labServerResponseMs": integer | null,
"totalBytesKb": integer | null,
"topOpportunities": array | null,
"opportunitySavingsMs": integer | null,
"status": string | null,
"input": string | null,
"errorCode": string | null,
"error": string | null
}| Field | Type | When it is filled |
|---|---|---|
url | string, null | |
finalUrl | string, null | Where the page ended up after redirects; compare it with url to spot a redirect. |
strategy | string, null | Which device Lighthouse simulated. With strategy 'both' a URL returns two rows. |
fetchedAt | string, null | When Google ran the analysis, so a row can be dated. |
lighthouseVersion | string, null | The Lighthouse version behind the scores, so results stay comparable over time. |
performanceScore | integer, null | Always scored. Runs from 0 to 100, as on Google's own page. |
seoScore | integer, null | Always scored. Runs from 0 to 100, as on Google's own page. |
accessibilityScore | integer, null | Filled when allCategories is on. Scoring all four categories makes a run about two and a half times slower and dearer, so it is off by default. |
bestPracticesScore | integer, null | Filled when allCategories is on. Scoring all four categories makes a run about two and a half times slower and dearer, so it is off by default. |
fieldOverall | string, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldLcpMs | integer, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldLcpRating | string, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldInpMs | integer, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldInpRating | string, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldCls | number, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldClsRating | string, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldFcpMs | integer, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldFcpRating | string, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldTtfbMs | integer, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
fieldTtfbRating | string, null | Filled when Google has enough real-user data for this URL over the last 28 days; empty for pages with little traffic. |
originOverall | string, null | The verdict for the whole domain, not this page. Filled when Google has enough real-user data for the domain; the per-metric columns stay about this URL. |
labLcpMs | integer, null | Measured by Lighthouse in the lab; empty when that audit did not run for the page. |
labFcpMs | integer, null | Measured by Lighthouse in the lab; empty when that audit did not run for the page. |
labCls | number, null | Measured by Lighthouse in the lab; empty when that audit did not run for the page. |
labTbtMs | integer, null | Measured by Lighthouse in the lab; empty when that audit did not run for the page. |
labSpeedIndexMs | integer, null | Measured by Lighthouse in the lab; empty when that audit did not run for the page. |
labTimeToInteractiveMs | integer, null | Measured by Lighthouse in the lab; empty when that audit did not run for the page. |
labServerResponseMs | integer, null | Measured by Lighthouse in the lab; empty when that audit did not run for the page. |
totalBytesKb | integer, null | Measured by Lighthouse in the lab; empty when that audit did not run for the page. |
topOpportunities | array, null | The fixes Lighthouse found, largest first, each worth at least 0.1 second; empty when it found nothing that size. |
opportunitySavingsMs | integer, null | The total of those fixes in milliseconds; empty when Lighthouse found nothing worth at least 0.1 second. |
status | string, null | Either 'ok' or 'error'. An error row carries errorCode and error, and leaves the data fields empty; it is never charged. |
input | string, null | The input this row was built from, so a row can always be traced back. |
errorCode | string, null | Filled on an error row only; a delivered row leaves it empty. |
error | string, null | Filled on an error row only; a delivered row leaves it empty. |
One health report per domain, up to 500 per run: registration and expiry from the official RDAP registry, DNS records with the mail and nameserver provider, SPF and DMARC policy, the TLS certificate with days remaining, and a ranked list of issues. Official protocols only.
$20.00 per 1,000 · Domain reportedOne deliverability report per domain, up to 500 per run: MX and mail provider, the SPF record with its DNS lookup count against the limit of ten, DKIM selectors that really exist, the DMARC policy and reporting, MTA-STS mode, TLS-RPT, BIMI and DNSSEC, plus ranked issues and a score.
$3.00 per 1,000 · Domain checkedPlain text extraction from PDF files by URL, up to 200 files per run: one row per page (or one per file) with the text, page count, title, author, dates, producer and file size, plus whether the page has a text layer at all. Pay per page delivered.
$1.00 per 1,000 · Page extractedAlready using this one? Ratings are the first thing other buyers look at, and this dataset has none yet. If it does a job for you, a rating on its Apify page is the one thing that helps. It takes a minute and it is the only thing we ask for.