What’s Deprecated in Selenium 4?
Selenium 4 changes vary by language and version. See the major deprecated and removed APIs, their replacements, and practical migration examples.
Short answer: there is no single, release-independent list of everything deprecated in Selenium 4. The status depends on the programming-language binding and the Selenium 4.x version. Some APIs were deprecated and later removed; others changed as Selenium adopted the W3C WebDriver standard. The examples below cover major migration points documented by Selenium, not every API in every binding. Check the API docs and release notes for the exact binding and version you use. Selenium documentation.
1. What “deprecated” means in Selenium 4
A deprecated API is still available in the version and binding that marks it deprecated, but has a replacement and may be removed later. A removed API is no longer callable. Do not treat “deprecated in Selenium 4” as a claim that every example became deprecated in version 4.0. Some removals arrived in later 4.x releases, and status can differ between Java, .NET, Python, Ruby, and JavaScript.
Selenium 4 adopted the W3C WebDriver standard and removed support for the legacy protocol. Selenium’s announcement said most users should not notice, though capabilities and Actions were notable exceptions. Code that depended on internals or APIs already marked deprecated could encounter upgrade issues. See the Selenium 4 announcement and upgrade guide.
2. Major deprecated and removed APIs by language
| Binding | Old API or behavior | Status documented | Replacement |
|---|---|---|---|
| Java | findElementBy… and findElementsBy… |
Removed | findElement(By…) and findElements(By…) |
| Java | Timeouts using (long, TimeUnit) |
Use the Duration-based APIs in the upgrade guidance | java.time.Duration |
| Java | BrowserType |
Deprecated | Browser |
| Java | Firefox setLegacy(true) |
Deprecated | Use GeckoDriver |
| .NET / C# | AddAdditionalCapability |
Deprecated | AddAdditionalOption |
| Python | find_element_by_* |
Removed in 4.3 | find_element(By.…) |
| Python | executable_path and desired_capabilities constructor keywords |
Removed in 4.10 | service= and options= |
These entries are the well-documented major examples, not a complete inventory. The project maintains separate guidance for bindings and releases. Start with the official upgrade guide and consult the current API docs for your binding.
3. Java migration examples
Replace the removed find-by helpers
Java’s old findElementBy… utility methods were removed. Pass a locator strategy to findElement or findElements instead.
// Old: removed in Selenium 4
// driver.findElementById("elementId");
// Replacement
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
WebElement element = driver.findElement(By.id("elementId"));
Choose the matching By method: By.id, By.className, By.cssSelector, By.linkText, By.name, By.partialLinkText, By.tagName, or By.xpath. For multiple matches, use driver.findElements(By.cssSelector(".result")). It returns an empty list when there are no matches, while findElement throws when none is found. The locator documentation describes locator strategies.
Use Duration for timeouts and waits
The Selenium 4 upgrade guidance uses java.time.Duration instead of the older (long, TimeUnit) signatures for timeout configuration and wait APIs.
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
// Assume driver is an initialized WebDriver.
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
wait.until(d -> d.findElement(By.id("ready")));
Apply the same Duration style to FluentWait.withTimeout and pollingEvery. Avoid using long implicit waits together with explicit waits without understanding their interaction; combined waits can make failure times less predictable. Prefer a short or zero implicit wait and explicit waits for conditions that matter to a test.
Update browser identity and Firefox setup
Where code uses the deprecated BrowserType.FIREFOX, use Browser.FIREFOX. The upgrade guidance marks Firefox’s legacy setLegacy(true) option deprecated and recommends GeckoDriver. Also, Selenium’s Java options merge returns a new options object: retain the return value rather than assuming the original object changed.
// Browser identity replacement
import org.openqa.selenium.Browser;
Browser browser = Browser.FIREFOX;
// Keep the merged options object returned by merge:
FirefoxOptions merged = optionsA.merge(optionsB);
Use imports and concrete options types appropriate to your Selenium Java version. Confirm the signatures in the Java API docs if your code targets an older 4.x release.
4. .NET / C# capability migration
Replace AddAdditionalCapability with AddAdditionalOption. Selenium 4 follows W3C capability conventions. Standard names include browserName, browserVersion, platformName, acceptInsecureCerts, pageLoadStrategy, proxy, timeouts, and unhandledPromptBehavior. Nonstandard capabilities need a vendor prefix; check the current format required by your browser or remote-grid provider.
// Old API (deprecated)
// options.AddAdditionalCapability("cloud:options", vendorOptions, true);
// Replacement
options.AddAdditionalOption("cloud:options", vendorOptions);
vendorOptions must have the shape expected by the provider. The vendor prefix belongs to the capability name; do not rename standard W3C capabilities into vendor-specific ones. Refer to Selenium’s driver options documentation and your provider’s current capability reference.
5. Python migration examples
Replace find_element_by_* calls
The find_element_by_* family was removed in Selenium 4.3. Import By and pass the locator strategy and value to find_element.
from selenium.webdriver.common.by import By
# Old: removed in Selenium 4.3
# driver.find_element_by_id("submit")
# Replacement
button = driver.find_element(By.ID, "submit")
links = driver.find_elements(By.CSS_SELECTOR, "a.navigation")
Pass Service and Options when creating a driver
In Selenium 4.10, the Python executable_path and desired_capabilities constructor keyword arguments were removed. Configure a Service and browser options, then pass them to the driver. Selenium Manager can manage the driver when that fits your environment, so a hard-coded executable path is not always necessary.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless")
# If managing chromedriver yourself, point Service at its executable.
service = Service("/path/to/chromedriver")
driver = webdriver.Chrome(service=service, options=options)
try:
driver.get("https://example.com")
print(driver.title)
finally:
driver.quit()
For Selenium Manager-managed drivers, use webdriver.Chrome(options=options) when your installed Selenium and environment support that workflow. Put browser configuration in the options object rather than passing the removed desired_capabilities argument. See the Selenium Manager documentation.
6. Capabilities and the W3C protocol
The shift to W3C WebDriver changes how sessions and capabilities are negotiated. Keep standard capability names compliant with W3C conventions, and use vendor-prefixed names for provider-specific settings. When a remote session fails during creation after an upgrade, inspect the returned error and compare the requested capability payload with the current browser or grid provider format.
The Selenium project’s historical discussion of the legacy protocol explains why compatibility issues could appear when older test suites ran against Selenium 4 Grid. It is historical context, not a promise that every old API behaves the same across all current bindings. See Selenium’s legacy protocol article.
7. A safe migration workflow
- Record the exact versions. Capture the language binding version, browser version, driver or Selenium Manager setup, and whether execution is local or through Grid.
- Read deprecation warnings before changing code. Search build output and test logs for deprecated symbols. Warnings are binding- and version-specific.
- Search for known removed names. Look for Java
findElementBy, Pythonfind_element_by_, and old Python driver constructor keywords. - Replace capabilities deliberately. Separate standard W3C capabilities from vendor-prefixed provider options.
- Update waits and locators. Use Duration-based Java waits and explicit locator strategies in Java and Python.
- Run a small representative test set. Include local and remote sessions, browser startup, element lookup, Actions if used, and failure paths.
- Check the target version’s docs. Do not infer the status in one binding or point release from another binding’s migration notes.
8. Troubleshooting migration errors
| Symptom | Likely cause | Fix |
|---|---|---|
Java compile error: findElementById cannot be resolved |
The old Java helper was removed. | Use findElement(By.id("…")). |
Python: AttributeError for find_element_by_* |
The method family was removed in 4.3. | Use find_element(By.ID, "…") or the appropriate locator. |
Python driver constructor rejects executable_path or desired_capabilities |
Those keywords were removed in 4.10. | Pass service= and options=; use Selenium Manager where appropriate. |
| Remote session creation fails with invalid capability | A nonstandard capability may lack a vendor prefix or use an outdated provider format. | Use current W3C names and consult the remote provider’s capability documentation. |
| Wait call no longer matches an overload | The code uses an older time and unit signature. | Use the Duration-based API supported by the binding version. |
| Browser options appear unchanged after merge | The returned merged options object was discarded. | Assign and use the object returned by merge. |
| Firefox starts with unexpected legacy behavior | Code may still enable the deprecated legacy option. | Remove setLegacy(true) and use GeckoDriver. |
| Failure appears only on Selenium Grid | Protocol or capability negotiation differs from local execution. | Check the Grid and binding versions, W3C capability payload, and provider-specific options. |
9. Performance, reliability, and maintenance
Replacing a deprecated call does not by itself make a test faster. Reliability usually improves when migration also removes fragile internals, uses explicit locators, sets realistic wait conditions, and keeps browser and driver management consistent. Avoid broad changes to selectors, waits, protocol settings, and browser versions in one step: smaller changes make regressions easier to isolate.
Pin or intentionally manage the Selenium binding version in your dependency configuration, and record browser and Grid versions in CI logs. Selenium’s downloads and documentation change over time; validate against the exact release you plan to ship. The downloads page is dynamic, so do not rely on a copied “latest version” value without checking it at upgrade time: Selenium downloads.
10. Capture browser failures without setting up another browser
When a migration failure is visible in a web page, a screenshot can help preserve the rendered state for debugging. For an automated test, use your existing WebDriver session and capture the page at the point of failure. If you need a screenshot of a public URL without adding browser setup to a small diagnostic script, ScreenshotNeo provides a website screenshot API and MCP server. Its API takes a URL and returns an image or PDF.
ScreenshotNeo API examples
Get an API key, then call the endpoint with the URL. See the ScreenshotNeo API documentation for request parameters and response details.
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}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
Or skip the browser setup
- Cookie banners are accepted and removed before capture; known consent platforms, newsletter popups, and chat widgets can be removed, with each step configurable.
- Bot checks and CAPTCHAs, blank pages, failed loads, timeouts, and cache hits cost nothing; response headers report the page verdict and billing status.
- An MCP server exposes
take_screenshot,get_page_info, andcapture_pdffor Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.
11. Frequently asked questions
Did Selenium 4 deprecate all Selenium 3 APIs?
No. Some APIs were removed or deprecated, but there is no single version-independent list covering every binding. Check the migration notes for your language and target release.
Is Selenium 4’s legacy protocol support the same as deprecated methods?
No. Protocol support concerns how WebDriver commands are exchanged; a binding method’s deprecation or removal is a separate API issue.
Do Java and Python have the same deprecated APIs?
No. Bindings have different APIs and release histories. For example, the Python locator helper removals do not imply the same method names existed in Java.
Where can I find the complete list for my project?
Use the official upgrade guide, binding API documentation, and release notes for the exact language and version. The examples here are major documented changes, not an exhaustive catalog.


