How to Measure Page Load Time with Selenium
Measure Selenium navigation time, browser load milestones, and application readiness with runnable examples and guidance for reliable results.
To measure Selenium navigation time, take a monotonic-clock reading immediately before driver.get(url) and another immediately after it returns. The result is the duration of that WebDriver navigation call under the session’s configured page-load strategy. For a modern application, measure the time until a meaningful application condition separately: document loading and app readiness are different events.
Use time.perf_counter() in Python, System.nanoTime() in Java, or performance.now() in JavaScript. State the browser, browser version, Selenium version, page-load strategy, test conditions, and exact event measured whenever you report a result.
1. Choose what “page load time” means
There is no single duration that answers every performance question. Choose the event that matches the question and label it in your output.
| Measurement | What it tells you | Useful for | Limitation |
|---|---|---|---|
Clock around driver.get() |
Elapsed time for the WebDriver navigation call under the selected page-load strategy | A simple end-to-end navigation measure | Includes browser and WebDriver coordination; changes meaning with the strategy |
| Navigation Timing API | Browser timestamps for the current document navigation | Document-level milestones and intervals | Does not include later app readiness by default |
| Explicit wait for an app condition | Elapsed time until a selected element or state is usable | Workflow readiness on dynamic pages | The condition must reflect the user outcome you care about |
| ChromeDriver performance logs or tracing | Browser Timeline, Network, Page events, and trace data | Diagnosing what contributed to a slow load | Must be enabled; tracing adds overhead and buffers can fill |
Selenium’s default normal page-load strategy waits for the document’s complete state. The eager strategy waits for interactive (the DOMContentLoaded milestone), while none does not block on page loading. A completed document does not prove that a single-page application has finished later JavaScript work. See Selenium browser options and Selenium waiting strategies.
2. Measure WebDriver navigation duration
This is the simplest method. The following Python program starts Chrome, measures one navigation with a monotonic clock, prints milliseconds, and closes the browser even if navigation fails. It uses Selenium Manager to locate or manage the driver as supported by the installed Selenium version.
from time import perf_counter
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
url = "https://example.com"
options = Options()
# Optional: uncomment for a headless run.
# options.add_argument("--headless=new")
# Default page-load strategy is "normal". Set it explicitly for clarity:
options.page_load_strategy = "normal" # "normal", "eager", or "none"
driver = webdriver.Chrome(options=options)
try:
start = perf_counter()
driver.get(url)
navigation_ms = (perf_counter() - start) * 1000
print(f"strategy={options.page_load_strategy} navigation_ms={navigation_ms:.1f}")
finally:
driver.quit()
Install the Python package with python -m pip install selenium. Run the script with python measure.py. The measured interval begins just before the WebDriver call, so browser startup is excluded; it includes the time for driver.get() to return.
Java example
Use System.nanoTime(), which is monotonic, rather than wall-clock time. This example assumes Selenium Java is available on the project’s classpath.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class MeasureNavigation {
public static void main(String[] args) {
ChromeOptions options = new ChromeOptions();
options.setPageLoadStrategy(org.openqa.selenium.PageLoadStrategy.NORMAL);
// Optional headless run:
// options.addArguments("--headless=new");
WebDriver driver = new ChromeDriver(options);
try {
long start = System.nanoTime();
driver.get("https://example.com");
double navigationMs = (System.nanoTime() - start) / 1_000_000.0;
System.out.printf("strategy=NORMAL navigation_ms=%.1f%n", navigationMs);
} finally {
driver.quit();
}
}
}
Use the equivalent dependency management and driver setup for your project. Keep browser startup outside the timed interval when you intend to report navigation time.
3. Read browser navigation milestones
The Navigation Timing API exposes timestamps for the current document. Read its navigation entry after navigation and calculate an explicitly named interval. For example, loadEventEnd - startTime is the browser-reported interval from navigation start to the end of the load event; it is not the same measurement as wall-clock time around driver.get().
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
driver = webdriver.Chrome(options=options)
try:
driver.get("https://example.com")
timing = driver.execute_script("""
const entry = performance.getEntriesByType('navigation')[0];
if (!entry) return null;
return {
type: entry.type,
domContentLoadedMs: entry.domContentLoadedEventEnd - entry.startTime,
loadEventMs: entry.loadEventEnd - entry.startTime,
responseStartMs: entry.responseStart - entry.startTime,
responseEndMs: entry.responseEnd - entry.startTime
};
""")
print(timing)
finally:
driver.quit()
These are browser timestamps for the current document navigation. The MDN Navigation Timing guide describes how navigation timing entries expose fetch and document construction timestamps. Choose and label milestones precisely; do not call every interval “page load time.”
4. Measure when a dynamic application is ready
For a single-page app or a page that fills content after the load event, wait for the condition a user needs. Examples include a result table becoming visible, a success message appearing, or a loading indicator disappearing. Record this as app readiness, separately from navigation duration.
from time import perf_counter
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
url = "https://example.com/search"
driver = webdriver.Chrome()
try:
navigation_start = perf_counter()
driver.get(url)
navigation_ms = (perf_counter() - navigation_start) * 1000
app_start = perf_counter()
WebDriverWait(driver, 15).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='results']"))
)
app_ready_ms = (perf_counter() - app_start) * 1000
print(f"navigation_ms={navigation_ms:.1f}")
print(f"app_ready_after_navigation_ms={app_ready_ms:.1f}")
finally:
driver.quit()
Here, the second number is time spent waiting after navigation returned, not total time from the beginning of navigation. If you need total time until readiness, take the start timestamp before driver.get() and subtract it when the condition succeeds. Use an explicit wait tied to a meaningful state instead of a fixed sleep: sleeps waste time on fast runs and may still be too short on slow ones.
5. Configure the measurement fairly
- Record the page-load strategy. Report
normal,eager, ornonewith each result. These strategies return at different document milestones. - Separate setup from navigation. Create the WebDriver before starting the timer unless browser startup is part of the scenario you intend to measure.
- Use a monotonic clock. Wall clocks can jump due to time synchronization or manual changes. Use a monotonic elapsed-time source.
- Keep the environment comparable. Use the same browser and version, machine or worker size, viewport, network conditions, browser options, and target URL for runs you compare.
- Decide cache behavior. A warm browser cache and a fresh profile can produce different results. Document which scenario you measured.
- Repeat runs. A single run is sensitive to network and machine variation. Report the number of runs and a useful summary such as median and range; do not invent a universal “good” threshold.
- Avoid mixing diagnostic overhead into the baseline. Performance logs and tracing can affect timing. Run a lightweight measurement separately from detailed diagnosis.
- Handle navigation failures. Put
driver.quit()in afinallyblock. A timeout or browser error is a failed observation, not a valid fast page load.
6. Diagnose slow results with browser data
When a measured run is unexpectedly slow, use ChromeDriver performance logging or a Chrome DevTools Performance recording to investigate. Performance logging can collect Timeline, Network, and Page events, but it is not enabled by default. Chrome tracing can add overhead and its buffer can fill; keep tracing results separate from lightweight benchmark figures and check for warnings or missing events. See ChromeDriver performance logging.
The Chrome DevTools Performance panel records CPU profiles that help locate runtime bottlenecks. Use a profile to investigate a slow result; it is diagnostic evidence, not the Selenium navigation duration itself.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The measurement is much shorter after switching settings | The page-load strategy changed what driver.get() waits for |
Log the strategy and compare equivalent milestones. With eager or none, add an explicit app condition if the workflow needs content to be ready. |
driver.get() returned, but the content is missing |
The document reached the configured ready state, but JavaScript-driven content has not appeared | Wait for the relevant element or state with WebDriverWait. |
| The explicit wait times out | The selector is incorrect, the element never appears, the state differs, or the timeout is too short for the environment | Confirm the locator and expected state in the browser; inspect the page and console; set a timeout appropriate to the workflow and environment. |
| Results vary widely across runs | Network, cache, machine load, browser version, or page content varies | Control and record the environment, repeat runs, and report a distribution rather than a single best result. |
| A navigation timeout is raised | The page did not reach the configured navigation milestone before the timeout | Treat the run as a timeout, inspect network and browser behavior, and choose a timeout consistent with the test. If using a different strategy, label the changed measurement. |
| Navigation Timing values are missing or zero | The entry was read in the wrong context, no navigation entry was available, or an event timestamp had not been set | Read from the current top-level document after navigation, check that an entry exists, and select a milestone that applies to the navigation. |
| Performance logs are empty or incomplete | Logging was not enabled at session creation, events were not collected, or trace buffers filled | Enable the supported logging capability when creating the session; check ChromeDriver warnings and buffer limitations. Consult the ChromeDriver logging documentation. |
| Driver startup fails before measurement | Browser installation, driver discovery, or version compatibility problem | Resolve browser and driver setup first; do not include setup failure in a navigation timing result. |
8. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a screenshot or PDF. This captures an image; it does not replace Selenium timing when you need a browser navigation duration or application readiness measurement.
Cookie banners are accepted and removed before the shot, along with 60+ known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for options. Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up free for ScreenshotNeo.
9. Frequently asked questions
Should I use time.time() to measure the call?
Prefer a monotonic elapsed-time clock such as Python’s perf_counter(). It is intended for duration measurements and is not affected by wall-clock adjustments.
Does Selenium measure Core Web Vitals?
The clock around driver.get() measures a WebDriver call duration. It is not a Core Web Vitals measurement. Use the browser metrics and tooling appropriate to the specific metric you need.
Can I compare results from different page-load strategies?
Only if you clearly label the different completion conditions and understand that they measure different endpoints. For a direct comparison, hold the strategy and other test conditions constant.
What is the best readiness condition for an application?
Use the state that marks the user’s actual task as possible, such as visible search results or an enabled submit control. There is no universal selector or readiness rule.
Is a screenshot enough to prove the page finished loading?
No. A screenshot shows rendered pixels at a particular point. It does not establish which browser milestone completed or whether later app work is finished.


