Page Speed¶
Issue a single GET against a URL from a server-side probe and return the wire-level speed metrics: time-to-first-byte, total response size, whether compression is in play, the HTTP version that was negotiated, and a count of response headers.
It's not a synthetic Lighthouse run — there's no rendering, no Core Web Vitals, no JavaScript execution. What you get is a fast, cheap signal you can call thousands of times per day to spot regressions, flag uncompressed responses, or confirm that HTTP/2/3 actually rolled out on your CDN.
For on-page SEO signals see /v1/seo/audit. For broken-link checks see /v1/seo/broken-links.
Endpoint¶
GET /v1/seo/pagespeed
Base URL: https://seo.toolkitapi.io
Query Parameters¶
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Absolute URL to test. Must be http:// or https://. |
Response Fields¶
| Field | Type | Description |
|---|---|---|
url |
string | Final URL after redirects. |
status_code |
integer | HTTP status from the final response. |
ttfb_ms |
number | End-to-end time from start of request to fully-received body, in milliseconds, rounded to 2 dp. |
total_size_bytes |
integer | Length of the response body as received (after decompression by the HTTP client). |
content_encoding |
string | null | Raw Content-Encoding response header, or null if absent. |
is_compressed |
boolean | true if content_encoding is one of gzip, br, deflate, zstd. |
http_version |
string | null | Negotiated HTTP version (e.g. "HTTP/1.1", "HTTP/2"). |
headers_count |
integer | Number of headers on the final response. |
About
ttfb_ms: despite the name, this is wall-clock time for the whole GET (connect + TLS + first byte + body). It's labelled TTFB for backwards compatibility with the schema; treat it as "total fetch time". The probe follows redirects with a 15 s timeout.
Examples¶
curl¶
curl -G "https://seo.toolkitapi.io/v1/seo/pagespeed" \
-H "x-api-key: $TOOLKIT_API_KEY" \
--data-urlencode "url=https://example.com"
Python¶
import requests
resp = requests.get(
"https://seo.toolkitapi.io/v1/seo/pagespeed",
params={"url": "https://example.com"},
headers={"x-api-key": API_KEY},
timeout=30,
)
data = resp.json()
print(f"{data['url']} → {data['status_code']} "
f"in {data['ttfb_ms']} ms over {data['http_version']}")
print(f" size: {data['total_size_bytes']/1024:.1f} KB, "
f"compressed: {data['is_compressed']} ({data['content_encoding']})")
if not data["is_compressed"]:
print(" ⚠️ No compression on the wire — check your CDN config")
if data["http_version"] == "HTTP/1.1":
print(" ⚠️ Still on HTTP/1.1 — consider HTTP/2 or HTTP/3")
JavaScript¶
const url = new URL("https://seo.toolkitapi.io/v1/seo/pagespeed");
url.searchParams.set("url", "https://example.com");
const resp = await fetch(url, {
headers: { "x-api-key": process.env.TOOLKIT_API_KEY },
});
const data = await resp.json();
console.log(
`${data.url} → ${data.status_code} in ${data.ttfb_ms} ms over ${data.http_version}`,
);
if (data.ttfb_ms > 800) console.warn("Slow response");
if (!data.is_compressed) console.warn("Uncompressed response");
Example Response¶
{
"url": "https://example.com/",
"status_code": 200,
"ttfb_ms": 184.42,
"total_size_bytes": 14823,
"content_encoding": "gzip",
"is_compressed": true,
"http_version": "HTTP/2",
"headers_count": 18
}
Error Responses¶
| Status | Reason |
|---|---|
400 |
URL failed validation (not absolute, bad scheme, or SSRF-blocked host). |
502 |
Generic network/TLS/connection error reaching the target. |
504 |
The target took longer than the 15-second probe timeout. |
Notes¶
- Single sample, not a benchmark. TTFB varies; for stable numbers run multiple samples and take a median yourself.
total_size_bytesis post-decompression. If the server returned 4 KB gzip that decoded to 14 KB, you'll see14823, not4096. Use it as a "rendered HTML weight" signal, not a "bytes on the wire" metric.- No assets are fetched. Only the document at
urlitself is requested — no images, JS, CSS, or fonts. This is intentional, to keep the probe light. - HTTP/3 / QUIC support depends on the upstream client; you'll typically see
HTTP/2even when curl reportsHTTP/3against the same origin. - The probe sends a fixed
User-Agent: WebScrapingToolkit/1.0 (+https://devtoolkit.api)— some origins serve different responses based on UA. - Each call goes through the same SSRF guard as
/v1/seo/audit; private/loopback hosts are rejected with400.