--- title: "Blog" description: "Read about the background of our monitoring solution, new features and case studies" canonical: "https://www.rumvision.com/blog/speculative-load-measurement-api-are-speculation-rules-efficient/" --- Breadcrumbs: [Home](https://www.rumvision.com/?format=md) > Blog # Speculative Load Measurement API: are your Speculation Rules efficient? Speculation Rules can make page navigations feel extremely fast. Prefetch the right page before someone clicks and much of the network work is already done. Prerender the right page and the next navigation can become almost instant. But speculation comes with an obvious challenge: you are spending resources on something that may or may not happen. [New to speculation rules? See how you can get instant page navigations with it.](https://www.rumvision.com/blog/speculation-rules-near-instant-page-loading-times/?format=md) That raises questions that have historically been surprisingly difficult to answer across real users: - How many speculative navigations were actually started? - Which Speculation Rules were responsible? - Which speculation matched the navigation the visitor eventually took? - How many speculative preloads went unused? - And how much speculative work was needed to produce one useful result? That is what the experimental [Speculative Load Measurement API](https://github.com/WICG/speculative_load_measurement/) is trying to address. We implemented the API in RUMvision and enabled it on production websites during its Origin Trial. That means we are now starting to see what speculation looks like across real pageviews rather than individual local tests. And the first data already shows why this kind of browser visibility is useful. ## Why Speculation Rules need measurement Speculative loading is fundamentally a prediction problem. The website and browser are spending resources now in exchange for a potentially faster experience later. Sometimes that prediction is correct. Sometimes it is not. ### Faster navigation has a cost A prefetch can download a future HTML document before it is needed. A prerender can go considerably further by preparing an entire future page in the background. When the user eventually chooses that destination, this can produce substantial performance gains. But speculation that is never used may still consume: - user bandwidth; - server or CDN requests; - browser cache space; - memory; - CPU; - and potentially battery on mobile devices. That does not automatically make aggressive speculation bad. The browser itself can decide not to perform speculative work based on resource constraints, user settings and other conditions. Spending some extra resources can also be a perfectly reasonable trade-off when it consistently makes important navigations faster. The challenge is understanding where that trade-off remains useful. ### Success != efficiency Imagine two pageviews. On the first: ``` 2 speculative navigations started1 matched the eventual destination ``` On the second: ``` 200 speculative navigations started1 matched the eventual destination ``` Both had a matching speculation. But they clearly represent very different amounts of speculative work. That leads to a more useful question than simply asking whether speculation worked: > **How much speculative work did it take to produce that winner?** This is also where field data matters. A single local test only shows one version of a page and one user journey. Real websites can expose different links based on template, viewport, recommendations, personalization and client-side DOM changes. Real visitors also trigger different interaction-based rules. So the useful question quickly becomes: > How does speculation behave across thousands of real pageviews, and where does that behaviour suddenly change? ## What the API exposes The Speculative Load Measurement proposal adds `performance.getSpeculations()` to the browser. Rather than trying to infer speculative activity through network requests, developers can ask the browser what speculative work took place during the document lifecycle. ### Measuring speculative navigations The API exposes speculative navigations that were actually started by the browser. Entries include information such as: ``` typetagseagernessurl ``` That means a RUM implementation can distinguish strategies such as: ``` prefetch:moderateprefetch:immediateprerender:eagerprerender_until_script:eager ``` Developer-defined Speculation Rules tags add another useful layer. Instead of only seeing that a `prefetch:moderate` occurred, a site could use tags such as: ``` product-cardmain-navigationsearch-resultrecommendation ``` This creates a bridge between browser behaviour and the rule that caused it. ### Measuring speculative preloads The API also exposes `` activity and whether a preload was eventually consumed during the lifetime of the page. That gives us both: ``` speculative preloadsunused preloads ``` A website may preload many resources without a problem if those resources are almost always consumed. A similar number of preloads with a much higher unused count tells a very different story. ### Knowing the outgoing destination The API can expose `navigationDestinationURL` for eligible outgoing navigations. This lets us compare the destination the visitor actually chose with the speculative navigations that were started. In RUMvision we use this information to determine which speculation matched the outgoing navigation. We then carry a small piece of that attribution into the next pageview, allowing us to report which speculation strategy was associated with the page that was eventually loaded. This was something I had wanted in our RUM data for quite a while. Server-side instrumentation can provide similar information, but requiring customers to add custom server logic would make adoption considerably harder. Having the browser expose the outgoing destination makes a client-side implementation possible. ## An experimental API in RUMvision Speculative Load Measurement is still experimental. Meaning the [API is available through a Chrome Origin Trial](https://www.rumvision.com/blog/a-step-in-to-the-future-of-the-web-what-are-origin-trials/?format=md). ### Why we adopted it early At RUMvision, we participate in quite a few experimental web platform APIs. We previously did the same with APIs such as [Soft Navigations](https://www.rumvision.com/blog/soft-navigations-better-core-web-vitals-attribution-for-spas/?format=md), [Container Timing](https://www.rumvision.com/blog/container-timing-measuring-when-components-actually-appear/?format=md) and [`fetchLater()`](https://www.rumvision.com/blog/introducing-fetchlater-api-a-new-way-to-collect-core-web-vitals/?format=md). There is a practical reason for doing this. A measurement API becomes considerably more interesting when it moves from: ``` this API returns these properties ``` to: ``` this is what those properties look like across real production traffic ``` Speculative Load Measurement is a particularly good RUM candidate because effectiveness depends heavily on visitor behaviour. If nobody clicks the links you are predicting, local testing will not tell you much about whether the strategy is useful in production. > Already using RUMvision? Enable Speculative Load Measurement [in your tracking configuration](https://www.rumvision.com/help-center/settings/domain/features/?format=md) and start exploring the [Speculative Load metrics and filters](https://www.rumvision.com/help-center/filters/traffic/speculative-load/?format=md) that will automatically become available. > You can also combine this data with the [RUMvision MCP](https://www.rumvision.com/features/web-performance-mcp/?format=md) to investigate patterns across templates, rules and speculation outcomes using natural-language questions. ### From OT to real-user feedback The timing is also interesting because Speculative Load Measurement is being [discussed by the Web Performance Working Group](https://docs.google.com/document/d/1etdM-hCu-zpQvG-ovA-qQF3xvGk_SyfVzgX5jamVYHI/edit?tab=t.0#heading=h.zhhlw1g6rtnl) during [TPAC 2026 this week](https://www.w3.org/news-events/tpac/2026/venue/). We now have an experimental API, implementation experience inside a RUM product and actual production data showing both what can already be learned and what information is still missing. That creates a useful feedback loop: ``` browser API→ production implementation→ real-user data→ new questions→ feedback into the API ``` And as the production data shows, those questions become considerably more concrete once you stop looking at hypothetical examples. ## RUMvision collecting speculation data The browser can expose more information than we actually need for monitoring. As the API is still experimental, we deliberately chose not to collect everything. ### Keeping the RUM payload useful Sending every speculative URL into RUM would create obvious disadvantages: - larger analytics payloads; - high-cardinality dimensions; - more storage; - potential privacy considerations; - and a lot of data that is difficult to turn into actionable reporting. Instead, we summarize the information into a small number of metrics and dimensions. For navigations: ``` Speculative navigationsSpeculation navigation tagsSpeculation navigation used ``` And for resource speculation: ``` Speculative preloadsUnused preloadsSpeculative preconnects ``` This gives us enough information to answer useful questions without turning every speculative URL into analytics data. The final speculation state becomes available near page dismissal, so this processing also needs to stay lightweight. Performing expensive synchronous work in `pagehide` could delay the next navigation, while asynchronous work is not guaranteed to finish after the document disappears. ### Triggered versus used This distinction is central to interpreting the data. **Speculation navigation tags** tells us which speculative navigations were started and which developer tags and `:` strategies were involved. For example: ``` product-cardproduct-card:count=4prefetch:moderateprefetch:moderate:count=4 ``` **Speculation navigation used** tells us which of those matched the outgoing destination. So one field describes **what speculative work happened**, while the other tells us **which speculation corresponded with the navigation the visitor eventually took**. That combination becomes particularly useful once we add templates and percentiles. ## What production data taught us Implementing an API is one thing. Seeing it across production traffic is where it becomes considerably more interesting. The following examples come from one production ecommerce website in our dataset. They are not universal benchmarks. They simply illustrate the kinds of questions that become possible once speculation is available as RUM data. ### Unused preloads per template Unused preloads become actionable per template. Across several important templates we saw broadly similar speculative preload counts. At the 75th percentile: ``` collection 15 preloadsproduct 15 preloadsindex 17 preloadspage 15 preloads ``` Looking only at that metric, there is not much reason to investigate one template before another. But the unused preload metric changes the picture: ``` collection 0 unused preloadsproduct 5 unused preloadsindex 0 unused preloadspage 1 unused preload ``` Now there is a clear place to start. The product template is doing roughly the same amount of preload work as other templates, but a larger part of that work is not being consumed. That does not immediately tell us which preload should be removed. But it tells us **where to investigate first**. And that is exactly the kind of result I want from RUM: not just another number, but a way to narrow down the investigation. ### One winner, lots of speculation Collection pages were starting considerably more speculative navigations than most other templates. Across one recent period: ``` collection speculative navigationsp50 ≈ 7p75 ≈ 19p90 ≈ 71 ``` A p90 of more than 70 speculative navigations immediately deserves some attention. The more interesting part appears when combining that count with **Speculation navigation used**. For collection pageviews where a `prefetch:immediate` navigation eventually matched: ``` p75 speculative navigations ≈ 12 ``` For collection pageviews where `prefetch:moderate` eventually matched: ``` p75 speculative navigations ≈ 48 ``` In both cases there was a matching speculation. Yet the amount of speculative work surrounding that winner was dramatically different. At first glance this might sound counterintuitive. `immediate` is the more aggressive eagerness level, so shouldn't it create more speculative work? Not necessarily. Eagerness describes **when** the browser may act on a rule. It does not tell us how broad the candidate set behind that rule is. A site can implement: ``` immediate→ a small, highly selected set of links ``` while using: ``` moderate→ a much broader set of links ``` On this particular website, `prefetch:immediate` appears to be used more selectively than `prefetch:moderate`. That is a site-specific observation, not a general property of the eagerness values. But it gives us an important lesson: > **Do not evaluate eagerness in isolation. Rule scope, candidate selection and actual browser behaviour need to be analysed together.** And it reinforces the earlier point: a matching speculation does not automatically mean the surrounding strategy was efficient. ### Percentiles and long-tail behaviour Percentiles reveal long-tail behaviour. Product pages showed perhaps the strangest distribution. Their normal speculation behaviour looked modest: ``` p50 ≈ 2p75 ≈ 4 ``` If we stopped there, product pages would not look concerning. Then p90 happened: ``` p90 ≈ 168 ``` That is not a template that consistently performs a huge amount of speculation. It is a template where **a subset of pageviews behaves completely differently from the majority**. Possible explanations include: - pages with much larger recommendation sections; - different product layouts; - dynamically inserted links; - a particular rule that only applies in some situations; - personalization; - or DOM changes that create an unusually large set of eligible links. The measurement does not tell us which explanation is correct. But it does tell us that applying a template-wide fix may be the wrong first response. The next step is segmentation. This is also why averages would be particularly unhelpful here. A long tail like this deserves p90 or p95 analysis rather than reducing everything to one mean value. ## What the API still cannot tell us Production data also exposes gaps in a measurement API very quickly. The biggest one we encountered is the difference between **available candidates** and **speculative navigations actually started**. ### Missing candidate context Started navigations are missing candidate context. Suppose the API tells us: ``` 5 speculative navigations started ``` That could mean: ``` 5 candidates available5 started ``` Or: ``` 100 candidates available5 started ``` Or even: ``` 5,000 candidates available5 started ``` Those can represent very different Speculation Rules and browser behaviour. But today they look identical if we only inspect the number of navigations that were actually started. This became interesting enough that I [opened a WICG issue requesting a bounded candidate-count signal](https://github.com/WICG/speculative_load_measurement/issues/17). I do not think RUM needs every candidate URL. In fact, I would rather not collect them. A count would already unlock measurements such as: ``` candidate countstarted navigation countstarted / candidate ratiocandidate count per Speculation Rules tag ``` For example: ``` product-card42 candidates4 started1 eventually matched ``` tells a richer story than: ``` 4 started1 matched ``` This would help identify unexpectedly broad rules, regressions where markup changes suddenly match far more links, and differences between templates. There is an important nuance though. A rule producing 500 candidates is not automatically bad if the browser only acts on a sensible subset. That is actually part of why both values would be useful. We could distinguish: ``` broad candidate poolbrowser remains selective ``` from: ``` broad candidate poolbrowser starts almost everything ``` Today we can observe the second half of that relationship, but not the first. ### Triggered != completed There is another limitation worth keeping in mind. A speculative navigation appearing in the API tells us that speculation was triggered, but not necessarily that the speculative load completed or delivered the full intended benefit. - A prefetch may have started but not completed before the click. - A prerender may have been started and later cancelled. The browser may also limit speculation because of memory, user settings or other resource constraints. So when we report that a speculation was "used", the safest interpretation is that the speculative navigation matched the destination the visitor ultimately chose. That is useful information. But it should not be mistaken for proof that the speculative operation completed successfully or saved a specific number of milliseconds. ## Turning it into useful RUM analysis The production data already points towards a fairly practical analysis workflow. The important part is combining the fields rather than interpreting any one metric in isolation. ### Templates 1st, outcome 2nd Site-wide speculation values can hide very different behaviour. So, start with templates, then combine work with outcome A homepage, collection page and product detail page expose very different navigation opportunities. So I would usually start with: ``` Template+Speculative navigations ``` or: ``` Template+Speculative preloads+Unused preloads ``` Once a template stands out, combine navigation volume with: ``` Speculation navigation tags+Speculation navigation used ``` Those signals answer different questions: ``` Speculative navigations→ how much work was started?Navigation Tags→ which rules and strategies caused it?Navigation Used→ which speculation matched the eventual destination? ``` This lets you distinguish a rule that merely triggers frequently from one that both triggers frequently and regularly corresponds with the next navigation. It also exposes rules that do produce winners, but only alongside a large amount of additional speculative work. ### Long tails and validations Do not stop at p50 or p75. Instead, watch the long tail and validate changes If: ``` p50 = 2p75 = 4p90 = 168 ``` then the useful question is probably not: > Why does this entire template speculate too much? It is: > What is different about the pageviews in that top 10%? That could lead towards a particular URL group, component, rule tag, device type or page state rather than changing behaviour everywhere. And after changing a Speculation Rule, collect more field data. For example: ``` beforemoderate rulep75 = 48 started navigations ``` and after narrowing the selector: ``` aftermoderate rulep75 = 12 started navigations ``` That sounds promising, but count alone still does not prove the change was better. You would also want to check: - did Navigation Used remain similar? - did navigation performance change? - did unused preload behaviour change? - did another template regress? - did the long tail disappear? That turns Speculation Rules optimisation into an iterative process based on actual visitor behaviour. ## What's next Speculative Load Measurement is still evolving, which is also why we expose it as an opt-in experiment in RUMvision today. Production usage has already made the next questions much clearer. ### From data back to the API Before implementation, candidate counts sounded useful because they would make the dataset more complete. After seeing real pageviews with tens or hundreds of started navigations, the use case becomes considerably more concrete. We can already observe: ``` how much work was startedwhich strategy caused itwhich speculation matched the destination ``` The missing candidate count would add: ``` how broad was the opportunity set the browser selected from? ``` That distinction is difficult to appreciate from an API design document alone. And this is why the timing around TPAC is interesting. Speculative Load Measurement is being discussed by the Web Performance Working Group while developers, browser engineers and early adopters are still exploring the API. Real-world RUM data can feed back into that process. The useful question for an experimental measurement API is not only whether it works technically. It is whether the information it exposes helps developers make better decisions. From the production data we have seen so far, Speculative Load Measurement clearly does. ### Closing thoughts Speculation Rules can produce some of the fastest navigations available on the web. But prediction without measurement can easily turn into guesswork. Our first production data has already changed how I think about speculation efficiency. A matching navigation is valuable information, but it is only part of the story. I also want to know: ``` How many navigations were started?Which rule caused them?How many preloads went unused?Does behaviour differ by template?Are percentiles hiding a long tail?How much speculative work surrounded the eventual winner? ``` A lab test can show that a speculation strategy works. Real User Monitoring can show **how often it works, where it behaves differently and how much work the strategy performs across actual visitors**. The Speculative Load Measurement API gives us browser-level building blocks that we previously did not have. There are still gaps, and the API will likely continue to change. But that is also why we wanted to implement it during the Origin Trial instead of waiting until every detail was finished. Because once you put measurement into the hands of real websites, the questions become much better. And in my opinion, the most useful one is no longer simply: **Did speculation work?** It is: **How efficiently did it work, for which pages, under which rules and for which real users?**