Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

In Python Selenium, driver.get() waits according to the browser’s page-load strategy. With the default normal strategy, it returns when the document reaches readyState complete. That does not guarantee a JavaScript application has finished rendering or that the content your test needs is ready. After navigation or an in-page action, use a bounded WebDriverWait for the specific element, text, or state that signals readiness.

Wait for the condition your test needs

For a typical page, let Selenium perform its normal navigation wait, then explicitly wait for the dashboard, result, or control your test will use. This example waits for visible dashboard content and then for a button to become clickable:

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

options = webdriver.ChromeOptions()
options.page_load_strategy = "normal"  # Selenium's default

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.test/dashboard")
    wait = WebDriverWait(driver, 20)

    dashboard = wait.until(
        EC.visibility_of_element_located(
            (By.CSS_SELECTOR, "[data-testid='dashboard']")
        )
    )
    submit = wait.until(
        EC.element_to_be_clickable((By.CSS_SELECTOR, "button.submit"))
    )
    submit.click()
finally:
    driver.quit()

Replace the example URL and selectors with ones from the application under test. The timeout is a maximum wait, not a fixed delay: when the condition succeeds, execution continues. If it does not succeed in time, Selenium raises TimeoutException.

What “page finished loading” means to Selenium

Selenium navigation commands wait for the readyState associated with the configured page-load strategy. The default is normal, which waits for complete. The browser may still be running application JavaScript that fetches data, updates a single-page-app route, or renders content after this point. As Selenium’s browser-options documentation cautions, complete does not necessarily mean a JavaScript application has finished loading: Selenium browser options.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Think of navigation readiness and application readiness as separate milestones. Use navigation to reach the document, then wait for the application state relevant to the next test step. A visible account summary may be a better signal than an assumption that every image, analytics request, or background task has ended.

Choose a page-load strategy deliberately

Set the strategy on the browser options before creating the WebDriver. The values documented by Selenium determine when navigation returns; they do not replace application-specific synchronization: page-load strategy options.

Strategy Navigation returns when When to use it
normal The document reaches complete. Use as the ordinary default when you want standard navigation behavior.
eager The document reaches interactive; some subresources may still be loading. Consider it when DOM access is enough and waiting for all resources would block longer than needed. Add explicit waits for the actual content or control.
none Navigation does not wait for a ready-state milestone. Use only when you intend to manage synchronization yourself with explicit waits.

Example configuration for a deliberate eager navigation:

options = webdriver.ChromeOptions()
options.page_load_strategy = "eager"
driver = webdriver.Chrome(options=options)

Changing to eager or none can make get() return sooner, but only the condition you wait for establishes that the page is ready for your test. With none, do not access page elements on the assumption that navigation has completed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Select an explicit wait condition

WebDriverWait(driver, timeout).until(condition) repeatedly evaluates a condition until it returns a truthy result or the timeout expires. Selenium’s Python API documents a default polling interval of 0.5 seconds; that is an API default, not a guarantee about page speed. Expected conditions are designed to be used with explicit waits: expected conditions and Python wait API.

Condition What it establishes Use it when
presence_of_element_located A matching node exists in the DOM. The next step needs the node, but it need not yet be visible.
visibility_of_element_located The matching node is present and visible. The test depends on content being shown to a user.
element_to_be_clickable The control is visible and enabled. The next action is a click.
text_to_be_present_in_element The expected text appears in the element. A status, result, or confirmation message marks completion.
staleness_of A previously located element is no longer attached to the DOM. A transition should replace an old page region or loading element.

For example, wait for a results message rather than guessing how long a search request takes:

wait.until(
    EC.text_to_be_present_in_element(
        (By.CSS_SELECTOR, "[data-testid='search-status']"),
        "Results loaded"
    )
)

Choose a signal that represents the application milestone you care about. Waiting for an element to exist does not prove it is visible; waiting for visibility does not prove a control is enabled; waiting for a control to be clickable does not prove that a later server-side operation has completed.

Wait after clicks and single-page-app updates

A click that changes content in place may not trigger a new navigation at all. The same is true of many AJAX updates and single-page-app route changes. After the action, wait for an observable change—new content, updated text, disappearance of a spinner, or replacement of an old element—instead of expecting driver.get() to synchronize work it did not start.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
old_results = driver.find_element(By.CSS_SELECTOR, "[data-testid='results']")
driver.find_element(By.CSS_SELECTOR, "button.refresh").click()

wait.until(EC.staleness_of(old_results))
new_results = wait.until(
    EC.visibility_of_element_located(
        (By.CSS_SELECTOR, "[data-testid='results']")
    )
)

If the application updates the existing results node instead of replacing it, wait for the new text or another changed property instead of staleness. The condition should match how that application signals completion.

Use explicit and implicit waits without obscuring timing

An implicit wait sets a driver-wide polling period for element-location calls. An explicit wait applies to a particular condition and has its own timeout. Selenium’s Python documentation describes both mechanisms: waiting strategies.

For tests that need precise synchronization, keep waits close to the action they protect and favor explicit waits with clear conditions. Avoid stacking large implicit and explicit waits: the interaction between a global element lookup delay and repeated explicit-condition polling can make elapsed time less intuitive and failures harder to diagnose. A single, bounded explicit wait makes the expected milestone and timeout visible in the test.

Handle timeouts and diagnose failed waits

A timeout is useful evidence: the condition did not become true within the configured interval. Catch TimeoutException when you need to add context or perform cleanup, but do not silently treat the missing condition as success.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium.common.exceptions import TimeoutException

try:
    dashboard = WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located(
            (By.CSS_SELECTOR, "[data-testid='dashboard']")
        )
    )
except TimeoutException as exc:
    raise AssertionError("Dashboard did not become visible") from exc

Use the following checks to find the cause rather than increasing the timeout reflexively:

  • Selector does not match: Confirm the selector against the current page and verify that the element is in the top-level document, not an iframe. Switch into the relevant frame before locating its contents.
  • Wrong readiness signal: If the node exists but is hidden, wait for visibility; if it is visible but disabled, wait for clickability. If a result is updated in place, wait for its new text or state rather than staleness.
  • Action did not trigger the expected change: Check that the click or navigation occurred and that the test is waiting for the resulting application state, not an unrelated page-load event.
  • Page is slower or unavailable: Check whether the site loaded successfully and whether the test environment is experiencing a delay. Set a bounded timeout appropriate to the test environment, then report a real timeout as a failure.
  • Navigation blocks too long: Consider whether eager is suitable for the test, but add an explicit wait for the needed content. Do not switch to none without taking responsibility for synchronization.

Do not replace a condition with a fixed sleep

time.sleep(seconds) pauses for the full duration whether the page becomes ready immediately or remains unready when the pause ends. That wastes time on fast runs and remains unreliable on slow ones. A bounded explicit wait proceeds as soon as its condition succeeds and raises a timeout if the condition never does.

Use a fixed delay only when the test specifically needs to observe a timed behavior and no application state can represent it. It is not a general substitute for waiting for page or application readiness.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to capture a site rather than exercise it through browser automation, ScreenshotNeo offers a website screenshot API and MCP server. A single GET request returns an image or PDF; its documented options include full-page capture, element capture, viewport and device settings, and waiting for a selector, delay, or network idle. See the ScreenshotNeo API documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

The free plan includes 1,000 shots per month with no card required; paid plans start at $5 for 3,000 shots. Every feature is available on every plan. Learn more at ScreenshotNeo, or sign up for 1,000 free screenshots a month with no card.

Frequently asked questions

Does Selenium have a command that waits until every part of a website is finished?

No single ready-state value establishes that every asynchronous application task is done. Wait for the particular content or state your test needs.

Why does driver.get() return before my AJAX results appear?

Navigation readiness and a later AJAX update are different events. Wait after navigation or the triggering action for an observable result condition.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Is a 20-second explicit wait required?

No. Choose a bounded timeout that fits the application and test environment; the example uses 20 seconds as an illustration.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.