ScreenshotNeo

BlogGuides

Selenium Waits: Types, Examples, and How to Use Them

Learn when to use implicit, explicit, and fluent waits in Selenium, with runnable examples, troubleshooting, and guidance for reliable tests.

By the ScreenshotNeo team4 October 20268 min read

Selenium waits keep tests from racing ahead of a page that has not reached the state they need. Use an explicit wait for a specific condition such as visibility or text; use an implicit wait only when you intentionally want a session-wide delay for element lookups. Selenium warns not to mix the two because their combined timing can be unpredictable. In Java, a configurable FluentWait is the flexible form of explicit waiting; Python’s WebDriverWait provides comparable polling and ignored-exception options.

1. Choose the right wait

Wait Scope Best use Key limitation
Implicit Every element location call in the session A deliberately global allowance for elements to appear Does not wait for visibility, clickability, text, or other page states
Explicit A condition at a specific point in the test Waiting for a known element or state Requires identifying the actual condition the test needs
Fluent A configurable explicit wait Adjusting timeout, polling, and ignored exceptions Configuration must match the condition; ignored errors can conceal real failures

Selenium’s implicit wait defaults to zero. Element lookup proceeds immediately unless a global implicit timeout has been set. An explicit wait repeatedly evaluates a condition and continues when it succeeds; if it does not become truthy before the timeout, the wait fails. See Selenium’s WebDriver waiting strategies and Expected Conditions guide.

2. Explicit waits: the usual choice

Use an explicit wait when the next action depends on a particular dynamic state. For example, wait until a result is visible before reading or interacting with it. The timeout below is an example value, not a universal recommendation.

Python: wait for visibility

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

 driver = webdriver.Chrome()
try:
    driver.get("https://example.com")
    wait = WebDriverWait(driver, timeout=10)
    result = wait.until(EC.visibility_of_element_located((By.ID, "result")))
    print(result.text)
finally:
    driver.quit()

Remove the leading space before driver = if copying literally; it is shown here only as a code formatting artifact. The locator tuple pairs a Selenium By strategy with its value. The condition returns the visible element when ready, so until returns a useful value rather than only a pass/fail signal.

Python: custom predicate

from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait

wait = WebDriverWait(driver, timeout=10)
status = wait.until(
    lambda current_driver: (
        (element := current_driver.find_element(By.ID, "status"))
        if element.text == "Ready"
        else False
    )
)
print(status.text)

A condition supplied to until should return a truthy value when the desired state is reached. Python’s API documents that until returns the last successful call’s value and raises TimeoutException if no truthy result arrives before the timeout. For Python versions without assignment expressions, use a named function:

def status_is_ready(current_driver):
    element = current_driver.find_element(By.ID, "status")
    return element if element.text == "Ready" else False

status = wait.until(status_is_ready)

Useful expected conditions

  • presence_of_element_located(locator): element exists in the DOM; it may not be visible.
  • visibility_of_element_located(locator): element exists and is visible.
  • element_to_be_clickable(locator): element is considered visible and enabled for interaction.
  • text_to_be_present_in_element(locator, text): an element contains expected text.
  • staleness_of(element): a previously found element is detached from the DOM.
  • title_contains(text) or title_is(title): the document title reaches the expected value.

Choose the condition that matches the action. Presence alone is not enough when the test needs to click or read a visible element. Expected Condition availability and APIs vary by language binding; Selenium notes that .NET stopped supporting its Expected Conditions package in Selenium 4, while Ruby commonly uses blocks or lambdas.

3. Implicit waits

An implicit wait sets a global timeout used by element location operations. It is configured once on the driver, and applies to subsequent lookups in that session.

Python

from selenium import webdriver

driver = webdriver.Chrome()
driver.implicitly_wait(2)
try:
    driver.get("https://example.com")
    element = driver.find_element("id", "result")
    print(element.text)
finally:
    driver.quit()

Java

import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

WebDriver driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2));
try {
    driver.get("https://example.com");
    System.out.println(driver.findElement(By.id("result")).getText());
} finally {
    driver.quit();
}

Use an implicit wait only when a global element-lookup delay is what you intend. It does not wait for an element to become visible or for text to update. Selenium recommends against combining implicit and explicit waits: nested lookups can add their own delays, making observed timeouts longer than the explicit timeout suggests. Its guide illustrates a 10-second implicit wait combined with a 15-second explicit wait timing out after 20 seconds.

4. Fluent waits and polling

Fluent waits let you control how often a condition is checked and which exceptions are ignored while checking it. In Java, FluentWait is an explicit wait with these controls:

import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.ElementNotInteractableException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.FluentWait;

FluentWait<WebDriver> wait = new FluentWait<>(driver)
    .withTimeout(Duration.ofSeconds(10))
    .pollingEvery(Duration.ofMillis(300))
    .ignoring(ElementNotInteractableException.class);

WebElement result = wait.until(d -> {
    WebElement element = d.findElement(By.id("result"));
    return element.isDisplayed() ? element : null;
});

The timeout and polling values are illustrative. Ignore only exceptions that are expected while the condition is transient. Ignoring broad exception types can turn a useful immediate error into an opaque timeout.

Python’s WebDriverWait supports polling and ignored exceptions through constructor parameters. Selenium 4.50.0 documents a default poll frequency of 0.5 seconds and NoSuchElementException as the default ignored exception:

from selenium.common.exceptions import ElementNotInteractableException
from selenium.webdriver.support.wait import WebDriverWait

wait = WebDriverWait(
    driver,
    timeout=10,
    poll_frequency=0.3,
    ignored_exceptions=(ElementNotInteractableException,),
)
result = wait.until(lambda d: d.find_element("id", "result"))

Defaults are binding-specific. Check the current API documentation for the language and Selenium version in your project; Python’s documented defaults do not imply Java or other bindings use the same values.

5. A practical workflow for stable waits

  1. Name the state. Decide what must be true before the next step: present, visible, enabled, text updated, old element gone, or title changed.
  2. Use a specific locator. Prefer a stable ID or other robust selector over positional selectors that change with layout.
  3. Wait where needed. Put the wait immediately before the dependent action so the test expresses its dependency.
  4. Keep implicit wait at zero when using explicit waits. This avoids confusing nested timeout behavior.
  5. Set a bounded timeout. Choose a limit based on the application’s expected response and test environment; do not treat it as a precise wall-clock guarantee.
  6. On timeout, inspect the state. Record the URL, relevant DOM or screenshot, locator, and last observed condition so the failure explains what did not happen.

6. Common errors and fixes

Symptom Likely cause Fix
TimeoutException The condition never became true, the locator is wrong, or the page is in a different state. Check locator and page state; wait for the condition the next action actually needs; capture diagnostics at timeout.
NoSuchElementException before an explicit wait The code performed a direct lookup before waiting. Move lookup into an Expected Condition or the predicate passed to until.
Element found but not interactable Presence was mistaken for visibility or enabled state; an overlay may cover it. Wait for visibility or clickability as appropriate, then investigate overlays and page layout.
Wait lasts much longer than configured Implicit and explicit waits are mixed, or each poll performs a lookup that itself waits. Set implicit wait to zero when using explicit waits; account for work performed by each poll.
Stale element reference The page replaced the DOM node after the element was located. Wait for the old element to become stale when appropriate, then locate the replacement inside the wait.
Text condition never passes Text is in a different node, includes whitespace, or changes to a different value. Inspect the rendered text and use a predicate that matches the actual state, including deliberate normalization if needed.
Python constructor argument error Binding version or argument shape differs from the example. Check the installed Selenium API docs; verify keyword names and pass ignored exceptions in the form supported by that version.

7. Performance, reliability, and cost

Waits trade immediate failure for bounded polling. A short polling interval can notice a state sooner, but checks more often; a long interval reduces checks but can add detection delay. The total runtime of a suite also depends on browser startup, navigation, the application, and what each predicate does. Keep predicates inexpensive and avoid fixed sleeps when a condition can be observed directly.

Explicit waits improve reliability when they observe the actual state needed by the next action. They cannot make an application state deterministic, repair an incorrect locator, or guarantee a precise duration. Use enough timeout for normal environmental variation, while retaining a bound that lets genuine failures surface. For repeated failures, preserve useful diagnostics instead of repeatedly increasing every timeout.

Selenium itself is open source; operational cost depends on where browsers and test infrastructure run, which is outside the wait API. For a different task—capturing a website image or PDF rather than automating an interaction sequence—a hosted screenshot API can avoid managing browser setup. ScreenshotNeo offers a one-request screenshot API and MCP server; its clean-shot billing rules and plan limits are described below.

8. Or skip the browser setup

If the goal is a screenshot rather than an interactive Selenium test, call the ScreenshotNeo API directly:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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}`);
await Bun.write('shot.webp', res);

Cookie banners are accepted and removed before capture, along with known newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.

9. Frequently asked questions

Does an explicit wait pause for the full timeout?

No. It checks repeatedly and returns once the condition succeeds. It reaches the timeout only if the condition does not return a truthy result in time.

Can I wait for an element that disappears?

Yes. Use a condition such as staleness of a previously located element, or a custom predicate that checks absence when that is the intended state.

Is FluentWait a separate wait strategy from explicit waits?

It is a configurable form of explicit waiting. The name and exact API vary by binding.

Should I use a fixed sleep?

Use one only when a fixed pause is genuinely required and no observable condition represents readiness. A condition-based wait usually avoids unnecessary delay and gives clearer failure behavior.

Sources