Crawl budget is not a quota Google hands out. Half of it is a feedback loop: Googlebot pushes request volume up while the host stays healthy and pulls it back when the host looks strained. That loop runs on signals your server produces — response time and error rates — which means it leaves a measurable trace in your access logs.
So the question "do I have a crawl budget problem?" has a concrete answer. Either your response times and Googlebot's request volume move against each other, in which case you are capacity-limited and infrastructure work will raise the ceiling, or they don't, in which case no amount of server tuning will change how much Google crawls.
What actually sets the ceiling
Google's crawl budget documentation splits crawling into two constraints. The crawl capacity limit is how much Googlebot thinks it can fetch without degrading your site. The crawl demand is how much it wants to fetch, driven by perceived URL value, freshness, popularity, and inventory size. Actual crawling is the lower of the two.
Google has documented the capacity side as adaptive: if a site responds quickly for a sustained period, the limit rises; if it slows down or returns server errors, the limit falls. Google has also documented the hard brakes — sustained 500, 503, and 429 responses cause rapid crawl reduction, and if they persist for more than a day or two, URLs can start dropping out of the index.
Two mechanical details matter for diagnosis:
- The limit is per host, not per URL pattern. One slow, expensive route family — internal search, unbounded facets, an uncached API-backed page type — taxes crawling of everything else on the same host.
- The relevant latency is time to first byte on the HTML document as served to the crawler, measured server-side. Not Largest Contentful Paint, not a Lighthouse score, not full page load. Googlebot's fetch loop cares when bytes start arriving.
Note also that Google deprecated the Search Console crawl rate limiter tool at the start of 2024. Google's stated reasoning was that the automatic adjustment already responds well to server signals — which is another way of saying the feedback loop is the only lever you have, and you pull it with response times.
What to pull from your logs
Bucket by hour, and keep verified crawler traffic separate from everything else. Reverse-DNS verification matters here: a meaningful share of self-declared Googlebot user agents are scrapers, and counting them inflates your crawl volume series with noise that has nothing to do with Google's rate controller.
SELECT date_trunc('hour', ts) AS hour,
count(*) FILTER (WHERE verified_googlebot) AS gbot_requests,
quantile_cont(ttfb_ms, 0.5) FILTER (WHERE verified_googlebot) AS gbot_p50_ttfb,
quantile_cont(ttfb_ms, 0.9) AS all_p90_ttfb,
count(*) FILTER (WHERE verified_googlebot AND status >= 500) AS gbot_5xx,
count(*) FILTER (WHERE NOT is_bot) AS human_requests
FROM logs
GROUP BY 1 ORDER BY 1;
Four series, hourly, over at least 60 days: Googlebot request count, Googlebot p50 TTFB, overall p90 TTFB, and human request count. Then compute the correlation between crawl volume and latency at several lags — 0, 1, 6, and 24 hours, plus a daily-aggregated version. The lag structure is what carries the diagnostic information, because Google's adjustment is a control loop, not an instantaneous reaction.
Search Console's Crawl Stats report shows the same two curves — total crawl requests and average response time — over 90 days, and it's a fine first look. It just won't let you segment by URL pattern or align to your own deploys, which is where the actual answer lives.
Reading the correlation
| Pattern in the data | Crawl vs. latency correlation | Response to a demand push | Read |
|---|---|---|---|
| Latency rises, crawl volume falls a few hours to a day later | Negative, latency leads | Nothing happens; volume returns to the same ceiling | Capacity-limited |
| Latency rises at the same time crawl volume rises | Positive, no lead | Volume climbs, latency climbs with it | You are the load; not yet limited |
| Latency flat and low, crawl volume flat | Near zero | Volume jumps on new URLs or sitemap changes | Demand-limited |
Crawl volume collapses, 5xx/429 share spikes | Negative and abrupt | Recovery lags the fix by days | Hard-braked, urgent |
The signature of a capacity ceiling is latency leading crawl volume downward, plus a visible plateau. If you can plot Googlebot requests per day against your slowest weeks and see a stable upper bound that never breaks regardless of how many new URLs you publish, that bound is the capacity limit.
The positive-correlation case trips people up. If crawl volume and latency rise together with no lag, Googlebot is causing the slowness rather than reacting to it. You are consuming headroom, not hitting a wall — though you're on the path to one.
The confounders that create false positives
Human traffic seasonality. If your peak human hours are also your slowest hours, and Googlebot happens to crawl less then for unrelated reasons, you get a spurious negative correlation. Control for it: partial out human request volume, or compare the correlation within traffic-volume strata.
CDN cache hits. If your edge serves Googlebot from cache, the latency Google observes is edge latency and your origin p90 is irrelevant to the crawl loop. Check the cache-status header distribution for verified crawler requests specifically. Crawlers hit long-tail URLs that humans rarely request, so their cache hit ratio is often far worse than your sitewide number — and sometimes far better, if your edge pre-warms.
Deploy and infrastructure events. A capacity story and a "we shipped a bad release" story look identical in an hourly series. Overlay your deploy timestamps before you conclude anything about steady-state capacity.
Mixed-latency route families. Sitewide p50 can look healthy while one route family sits at multiple seconds. Break latency down by URL pattern and weight by verified crawler request share. That weighted number is much closer to what Google's controller sees.
When it isn't capacity
If latency is flat and low and crawl volume still feels inadequate, the problem is demand or allocation, and those have different fixes. Two tells:
- A high share of crawl on URLs you don't care about. Parameterized duplicates, filter combinations, paginated tails. You have crawl capacity; it's being spent badly. That's an internal linking, canonicalization, and URL-hygiene problem.
- A low
304 Not Modifiedshare on stable pages. Correct conditional-request handling lets Googlebot confirm freshness cheaply, freeing capacity for URLs that changed. Many stacks never implementETagorLast-Modifiedcorrectly and pay full body cost on every revisit.
What to do
- Build the hourly series described above with verified crawler filtering, and keep it running. This is a monitoring artifact, not a one-off audit.
- Compute lagged correlations between crawl volume and crawler-weighted TTFB. Confirm the sign, the lead, and whether a plateau exists.
- Segment latency by URL pattern weighted by crawler request share, and fix the worst pattern first. Because the limit is per host, one bad route family is often the entire problem.
- Alert on
5xxand429share of verified crawler requests, separately from your general error alerting. Crawler-visible errors carry indexing consequences that human-visible errors do not. - If you're demand-limited, stop the performance project. Redirect effort to allocation: kill low-value crawl paths, fix conditional requests, and improve internal links to the URLs you want refreshed.
One caveat worth stating plainly: raising the capacity limit raises the ceiling, not the demand. A faster server lets Google crawl more if it wants to. Whether it wants to is a different measurement.