Lightscore

PageSpeed Insights API rate limits and quota exceeded errors

You sent one request too many and got this back:

Quota exceeded for quota metric 'Queries' and limit 'Queries per day' of
service 'pagespeedonline.googleapis.com' for consumer 'project_number:...'

That's HTTP 429. The quota belongs to a Google Cloud project — the one behind your API key. It is not per key and not per IP address. Google publishes no number for it in the PageSpeed Insights documentation. Every figure you find in a blog post is somebody's snapshot of their own console. The number that applies to you is on your project's quota page, and nowhere else.

Which limit did you hit?

The 429 body carries more than the message. Here is the part that matters, from a keyless request I sent on 2 September 2026:

{
  "error": {
    "code": 429,
    "status": "RESOURCE_EXHAUSTED",
    "details": [{
      "@type": "type.googleapis.com/google.rpc.ErrorInfo",
      "reason": "RATE_LIMIT_EXCEEDED",
      "metadata": {
        "quota_metric": "pagespeedonline.googleapis.com/default",
        "quota_unit": "1/d/{project}",
        "quota_limit": "defaultPerDayPerProject",
        "quota_limit_value": "0",
        "consumer": "projects/583797351490"
      }
    }, {
      "@type": "type.googleapis.com/google.rpc.Help",
      "links": [{
        "description": "Request a higher quota limit.",
        "url": "https://cloud.google.com/docs/quotas/help/request_increase"
      }]
    }]
  }
}

Read quota_unit first. It tells you the window.

Field What it tells you
quota_unit the window — 1/d/{project} is a daily limit
quota_limit the limit ID to search for in the Cloud console
quota_metric the metric row on the quota page
consumer which project the request counted against

The pages I found for this error quote the message and stop there. That block is the difference between a wait of a minute and a wait until tomorrow.

Do I still need an API key?

Yes. Google's getting-started page says only that a key "is recommended for frequent, automated queries", which reads like a suggestion. Google's own discovery document disagrees with it: there, key is "Required unless you provide an OAuth 2.0 token."

Trust the discovery document. Keyless requests all count against one anonymous project, 583797351490, and look again at quota_limit_value above. It is 0. The shared quota isn't drained — it's set to zero, so a keyless request fails on the first try rather than the thousandth. The same project number turns up in a bubblewrap bug report from May 2026 and in OpenPanel's help page. Get a key.

So what is the actual quota?

I could not verify a single figure against a Google source, and I looked hard. Five numbers circulate instead.

Figure Where it comes from
25,000/day, 1 request/second a KNIME node's docs, marked "as of September, 2015"
25,000/day, 100 per 100 seconds a 2016 Google Groups post
25,000/day, 1,500 per minute a different poster in the same 2016 thread
25,000/day, 240 per 4 minutes a developer log from October 2022
25,000/day, 240 per minute DebugBear, updated December 2025

Normalise the second column and most of that disagreement goes away. One a second, 100 per 100 seconds and 240 per 4 minutes are the same rate written three ways: 60 a minute. DebugBear's 240 a minute is four times it. The 2016 figure of 1,500 a minute is twenty-five times it, and its author called the limit bursty and averaged.

The daily number is the only thing all five agree on. A 2021 bug report caught the other half of the error string: limit 'Queries per minute'. So the short window is named per minute somewhere in the quota system, whatever its value is.

Read yours instead. In the Google Cloud console, open APIs & Services → PageSpeed Insights API → Quotas, and find the metric named in your error payload. That page shows the limit and your usage against it. It is the only figure that is both current and yours.

Retries: what works, and what wastes your afternoon

Google's own API design guidance covers this case. AIP-194 lists RESOURCE_EXHAUSTED as generally non-retryable: "Retries therefore may not be expected to work for several hours." Exponential backoff against a daily quota is a loop that burns your CI minutes and changes nothing.

So branch on the window, not on the status code.

per day

per minute

HTTP 429
RESOURCE_EXHAUSTED

read quota_unit

Stop the run
resume after reset

Back off
retry with jitter

A 429 from this API has two meanings, and only one of them is worth retrying.

There's one more failure that no quota page explains. One developer ran roughly one request a second and hit 500 errors in valleys. They arrived every 450 to 500 requests and lasted about five minutes. Requests for a different origin succeeded while the stalled job kept failing. But those went out on a second key, so the experiment doesn't separate the origin from the key. It's a single report from October 2022 and I have found no Google documentation of it. Treat it as a reason to spread one origin's requests over time, not as a rule.

When PSI stops being the right tool

Be honest about the shape of your problem before you reach for a replacement. Three of these aren't us.

You need Reach for The catch
Real-user field data, at scale CrUX API — 150 queries/minute per project, documented and free Origin and page level only, no lab report, 28-day window
Historical field data for many origins CrUX on BigQuery, back to 2017 SQL, a Cloud account and a card, free only to the BigQuery free tier
Lighthouse on every commit, no quota at all Lighthouse CI — Google's own, Apache-2.0 You run the machines, and your numbers move when the runner does
Lab runs from a location you choose A hosted Lighthouse API, ours included Somebody's fleet, somebody's limits

If you audit one site on every push, Lighthouse CI is the tool for it and the software costs nothing. You hit the limit when you test many URLs. You also hit it when you need the run to come from a place Google won't let you pick. The v5 runPagespeed method takes seven parameters of its own, and not one of them is a location.

That last case is what we built. Lightscore runs the same Lighthouse engine from a region you name, on a pinned Lighthouse version, and hands back the full JSON and HTML report. It measures Core Web VitalsGoogle’s three headline UX metrics: LCP (load), INP (responsiveness), CLS (stability). in the lab, so it is not a substitute for CrUX field data — different question, different tool. And we have limits too: the free one-URL tool limits new audits per IP address per hour, and its page says so.

Run a test · Get an API key

Common questions