WriteMySEO / Blog / Measuring Time-to-Index With the URL Inspection API
SEO data

Measuring Time-to-Index With the URL Inspection API

How to measure your site's real indexing latency instead of quoting vendor averages — the API fields that matter, polling design, and how to tell discovery from selection delays.

Ask how long Google takes to index a new page and you'll get an average from somebody's sample of somebody else's sites. That number is useless to you. Indexing latency is site-specific, section-specific, and often bimodal — a chunk of URLs picked up within hours and a long tail that sits for weeks. The average sits in the empty space between the two humps.

The good news: this is one of the few SEO metrics you can measure directly, on your own URLs, with a documented API. The bad news is that most implementations measure the wrong event and quietly bias the result.

What "time to index" actually means

There are four distinct events, and they can be days apart:

  1. Discovery. Google learns the URL exists — from a sitemap, an internal link, an external link, a redirect target.
  2. Crawl. Googlebot fetches it. Your logs see this.
  3. Indexing. The page is processed and stored in the index, possibly after a rendering pass.
  4. Serving. The URL becomes eligible to appear for queries, which is when it shows up in Search Console Performance data.

People say "indexed" and measure serving, or measure crawl and call it indexing. Decide which event your team cares about before you build anything. For publishing workflows, discovery→serving is usually the number that matters. For diagnosing a crawl problem, you want discovery→crawl isolated, because that's the interval a faster server or a better internal link actually moves.

The instrument: Search Console's URL Inspection API

The URL Inspection API returns, per URL, the same index status data the Search Console UI shows. Google documents a quota of 2,000 queries per day and 600 per minute per property. The fields that matter for latency work live under indexStatusResult:

Two cautions. First, lastCrawlTime is the last crawl, not the first, so it stops being a proxy for "when did Google first fetch this" the moment a second crawl happens. Poll often enough that the first non-null value is close to the first crawl. Second, when Google selects a different canonical, the returned status describes the indexed canonical, not necessarily the URL you asked about — always compare googleCanonical to the inspected URL before recording a result.

The Indexing API is not an alternative here. Google restricts it to JobPosting and livestream BroadcastEvent pages; using it for general content submission is out of policy and gives you no measurement value.

Mapping states to stages

coverageStateStage reachedWhat it tells you
URL is unknown to GooglenoneNot discovered. Check sitemap inclusion and internal links.
Discovered - currently not indexeddiscoveryKnown, crawl deferred. Usually scheduling or host-load related.
Crawled - currently not indexedcrawlFetched, not selected. A quality/selection judgment, not a crawl problem.
Duplicate, Google chose different canonicalcrawlConsolidated onto another URL. Compare the two canonical fields.
Submitted and indexed / Indexed, not submittedindexDone. Record the timestamp.

That distinction between Discovered and Crawled - currently not indexed is the single most useful output of the whole exercise. They look similar in a coverage report and have opposite fixes. Discovery-stage stalls respond to crawl-side work: sitemap hygiene, internal linking from frequently crawled pages, faster responses. Crawl-stage stalls do not — Google fetched the page and declined to index it, and no amount of resubmitting changes that judgment.

Designing the poll so it doesn't lie to you

You cannot measure this retroactively. Latency data only exists if you were watching.

# one cohort row per URL, appended daily until terminal state
for url in cohort_urls_not_yet_indexed():
    r = inspect(url)            # URL Inspection API
    s = r["inspectionResult"]["indexStatusResult"]
    record(url,
           checked_at=now_utc(),
           verdict=s["verdict"],
           state=s.get("coverageState"),
           last_crawl=s.get("lastCrawlTime"),
           g_canonical=s.get("googleCanonical"))

Design notes that change the answer:

Report the median and the 90th percentile, plus the share still unindexed at day 14 and day 30. Never report the mean.

Cross-checking with serving data

The Performance API gives you a second, independent instrument: the first date a URL received an impression. That measures serving, not indexing, and it's noisy — a page can be indexed for a week before any query surfaces it, and Search Console's anonymized-query filtering can suppress low-volume rows. But when inspection says a URL is indexed and Performance shows no impressions for weeks afterward, you've learned something different and more interesting: the page is in the index and losing every retrieval it enters. That's a relevance problem, not a crawl problem.

Log files give you the third instrument and the most reliable crawl timestamps — verified Googlebot fetches with exact times. Use logs for discovery→crawl, use the API for crawl→index, use Performance for index→serving.

What to do with the number

Search Consoleindexingcrawlingmeasurement

WriteMySEO produces marketing content, not legal, medical, financial, or compliance advice. Figures cited reflect publicly reported industry data at time of writing and shift over time.

Get started

We write this well about your industry, every month.

AI-drafted, human-reviewed SEO content on a flat subscription. Blog posts, metadata, schema, and internal links, shipped on a monthly rhythm.

See plans

More from the blog