How to Monitor Website Performance With a Load Time API
Build a reliable load-time monitor with PageSpeed Insights, field data, alerts, troubleshooting, and ScreenshotNeo visual captures.
Direct answer: schedule API checks for the URLs and device strategies that matter, save every response with a timestamp, extract stable metrics into a time series, and alert only when a regression persists or breaches your target. Start with the PageSpeed Insights API for Lighthouse diagnostics and available CrUX field data. Add WebPageTest when you need real browsers, geographic tests, waterfalls, filmstrips, video, or visual evidence.
What a load-time monitor should measure
A monitor is a scheduled experiment, not a single score. Store the URL, timestamp, API version, strategy, test location when available, raw JSON, headline score, audit identifiers, and normalized metrics including LCP, INP, CLS, FCP, TTFB, Speed Index, and Total Blocking Time.
Keep lab and field data in separate dashboard series. Lighthouse lab runs use a controlled, emulated environment and are useful for debugging. CrUX field data represents real users, devices, and networks and has different coverage and timing.
Choose the right API
| Service | Strengths | Use it when |
|---|---|---|
| PageSpeed Insights API | Lighthouse audits and scores plus available CrUX field data; simple REST requests with mobile or desktop strategy. | You need a low-friction baseline and diagnostics. |
| WebPageTest API | Real browsers, locations, connection speeds, waterfalls, filmstrips, video, history, and CI/CD integration. | You need geographic or visual evidence. |
| Lighthouse Metrics API | Hosted authenticated Lighthouse checks from multiple regions. | You prefer a managed multi-region service; confirm current plans and terms. |
Compare lab versus field data, browser realism, geographic and network controls, metric depth, repeatability, authentication and quotas, retention, CI/CD integration, alerting, and total operating cost. Do not compare vendors only by one score.
Build a PageSpeed Insights monitor
1. Select pages and variants
Include the homepage, conversion pages, representative templates, and authenticated or scripted journeys that materially affect users. Run mobile and desktop separately when both audiences matter. Establish a fixed cadence and repeat samples.
2. Make the API request
Google documents runPagespeed with a required url and optional category, locale, and strategy. An API key is recommended for frequent automated queries.
curl -G 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed' \
--data-urlencode 'url=https://example.com/' \
--data-urlencode 'strategy=mobile' \
--data-urlencode 'category=performance' \
--data-urlencode 'key=YOUR_PSI_API_KEY'
Repeat with strategy=desktop for a separate series. Save the complete response.
3. Runnable Python collector
import json, os, time
from datetime import datetime, timezone
from pathlib import Path
import requests
API = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed'
URLS = ['https://example.com/', 'https://example.com/pricing']
STRATEGIES = ['mobile', 'desktop']
OUT = Path('psi-samples'); OUT.mkdir(exist_ok=True)
def value(audits, key):
v = audits.get(key, {}).get('numericValue')
return v if isinstance(v, (int, float)) else None
for url in URLS:
for strategy in STRATEGIES:
params = {'url': url, 'strategy': strategy, 'category': 'performance', 'key': os.environ['PSI_API_KEY']}
stamp = datetime.now(timezone.utc)
r = requests.get(API, params=params, timeout=120)
r.raise_for_status(); payload = r.json()
audits = payload.get('lighthouseResult', {}).get('audits', {})
categories = payload.get('lighthouseResult', {}).get('categories', {})
names = ['largest-contentful-paint','interaction-to-next-paint','cumulative-layout-shift','first-contentful-paint','server-response-time','speed-index','total-blocking-time']
record = {'timestamp': stamp.isoformat(), 'url': url, 'strategy': strategy,
'score': categories.get('performance', {}).get('score'),
'metrics': {n: value(audits, n) for n in names},
'audit_refs': sorted(audits), 'raw': payload}
safe = url.replace('https://','').replace('/','_')
(OUT / f'{safe}-{strategy}-{int(time.time())}.json').write_text(json.dumps(record, indent=2))
Run it with PSI_API_KEY=… python monitor.py. Missing audits become null, never zero.
4. Runnable Node.js collector
const fs = require('node:fs/promises');
const endpoint = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed';
const url = process.env.TARGET_URL || 'https://example.com/';
const strategy = process.env.STRATEGY || 'mobile';
const q = new URLSearchParams({url, strategy, category:'performance', key:process.env.PSI_API_KEY});
const res = await fetch(`${endpoint}?${q}`);
if (!res.ok) throw new Error(`PageSpeed Insights returned ${res.status}`);
const payload = await res.json();
const audits = payload.lighthouseResult?.audits || {};
const names = ['largest-contentful-paint','interaction-to-next-paint','cumulative-layout-shift','first-contentful-paint','server-response-time','speed-index','total-blocking-time'];
const metrics = Object.fromEntries(names.map(n => [n, audits[n]?.numericValue ?? null]));
await fs.mkdir('psi-samples', {recursive:true});
await fs.writeFile(`psi-samples/sample-${Date.now()}.json`, JSON.stringify({timestamp:new Date().toISOString(), url, strategy, metrics, raw:payload}, null, 2));
5. Store and alert
Keep raw JSON in durable storage and write one normalized row per URL, strategy, and timestamp to your metrics store. Alert on sustained regression or a service-level breach, with separate thresholds for lab and field series. Annotate deploys, CDN changes, third-party script changes, and content releases.
Field data and Core Web Vitals
PSI can include CrUX field experiences alongside Lighthouse results. Google lists FCP, LCP, INP, CLS, and experimental TTFB among field metrics. Lab output includes FCP, LCP, Speed Index, CLS, Time to Interactive, and Total Blocking Time. Google describes CrUX as a previous 28-day collection period and documents score bands of 90 or above as good, 50–89 as needing improvement, and below 50 as poor. Recheck current documentation before hard-coding policy.
When to add WebPageTest
Use WebPageTest when PSI cannot explain a regression. Its API provides real-browser tests across locations and connection speeds, request-level metrics, waterfalls, filmstrips, video, test history, and CI/CD integration. Save the test identifier and artifact links with each sample. Hold browser, location, connection profile, and repeat count constant when comparing runs.
Reliability, performance, and cost controls
- Run often enough to catch regressions before release and increase frequency around deploys.
- Retry transient failures with bounded backoff, while recording every attempt.
- Set client timeouts longer than the provider’s normal test duration.
- Cache unchanged checks, stagger URLs, and monitor quota responses.
- Keep API keys in a secret store and redact them from logs.
- Estimate usage as URLs × strategies × runs per day × retention period.
- Pin strategy and test settings so comparisons remain valid.
Common errors and fixes
| Symptom | Cause | Fix |
|---|---|---|
| 400 response | Malformed URL, invalid strategy, or unsupported parameter. | URL-encode the value, use mobile or desktop, and remove optional parameters. |
| 401, 403, or quota error | Missing, invalid, restricted, or exhausted API key. | Check key restrictions and project quota. |
| Timeout or empty result | Slow page or transient provider failure. | Retry with bounded backoff, record the attempt, and inspect the URL independently. |
| Score changes without a code change | Lab variance, third-party changes, network conditions, or field-population changes. | Use repeated samples and alert on sustained movement. |
| Metric missing | The audit is unavailable for that response. | Store null and retain raw JSON. |
| Field data unavailable | Insufficient CrUX coverage. | Use lab data for diagnostics and label field data unavailable. |
Or skip the browser setup
ScreenshotNeo adds a visual record to a performance workflow with one GET request returning PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture and removes 60+ known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo documentation for options. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
How often should checks run?
Use a consistent cadence and increase frequency around releases.
Should I alert on the score?
Use the score as context; alert on sustained changes in target metrics.
Can one API represent real-user performance?
No. Combine controlled lab data with CrUX field data and geographic real-browser tests when needed.
Why save raw responses?
Audit names and available metrics change; raw responses let you explain and reprocess historical alerts.


