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

Read the URL property on the browser object you already use: Selenium exposes driver.current_url, while Playwright exposes page.url. Both return a Python string, and neither accessor takes parentheses.

# Selenium
print(driver.current_url)

# Playwright
print(page.url)

The value belongs to the active browsing context (Selenium) or the specific Playwright Page object (tab or popup). After an action that navigates, synchronize with that navigation before reading the value your next step depends on.

The two Python accessors at a glance

Framework Accessor What it represents Async syntax
Selenium driver.current_url The current URL in WebDriver’s current browsing context The property read itself is ordinary Python access
Playwright page.url The URL of one Playwright Page, which is a tab or popup Await browser and navigation operations; read page.url directly

These are properties, not methods. Writing driver.current_url() or page.url() attempts to call the returned string and raises a TypeError.

Get the current URL with Selenium

Complete synchronous example

Create a WebDriver, navigate with get(), read current_url, and close the browser in a finally block so cleanup still runs if navigation or your assertion fails.

Free tools Windows power users keep installed

One-click scans. No signup required.

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


driver = webdriver.Chrome()
try:
    driver.get("https://example.com")
    current = driver.current_url
    print(current)
finally:
    driver.quit()

driver.current_url is a string snapshot. Store it when you need to compare the address before and after a click, submit it to a logger, or pass it to another Python function.

Use it in an assertion

from selenium import webdriver


driver = webdriver.Chrome()
try:
    driver.get("https://example.com/login")
    driver.find_element("css selector", "button[type='submit']").click()
    actual_url = driver.current_url
    if "/dashboard" not in actual_url:
        raise AssertionError(f"Unexpected destination: {actual_url}")
finally:
    driver.quit()

The click must have completed the navigation you are testing before you consume current_url. Use Selenium’s normal explicit wait strategy for the page state your test requires rather than adding an arbitrary sleep.

Get the current URL with Playwright

Synchronous Playwright

In the synchronous API, read page.url after creating a page and navigating it.

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.url)
    browser.close()

A Playwright browser context can contain several pages. The variable named page determines which tab’s URL you receive; reading another Page object reads another tab.

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

Asynchronous Playwright

In async code, await browser and page operations, but do not await the URL property itself.

import asyncio
from playwright.async_api import async_playwright


async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page()
        await page.goto("https://example.com")
        print(page.url)
        await browser.close()


asyncio.run(main())

await page.url is incorrect because page.url is already a string. Await the operation that changes the page, then read the property.

Synchronize after navigation before reading

A browser can update the address when navigation is committed while still loading resources and rendering the document. If the next line depends on the destination, coordinate the action and navigation instead of reading immediately after a click.

Playwright: wait for the expected URL

Use page.wait_for_url() with a URL or pattern that describes the destination. Then read page.url.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://example.com/start")
    page.get_by_role("link", name="Continue").click()
    page.wait_for_url("**/complete")
    print(page.url)
    browser.close()

For an async page, use await page.wait_for_url(...) and then read page.url without await. Choose a pattern that matches the redirect and query-string behavior of your application.

Selenium: wait for the condition your test needs

Selenium’s accessor has no separate URL-wait method requirement: perform an explicit wait for the expected URL or page condition, then read driver.current_url. This keeps the wait tied to an observable result instead of a fixed delay.

Reading URLs when tabs and popups are involved

Selenium browsing contexts

driver.current_url follows whichever window handle WebDriver has selected. If your code opens a new tab, switch to that window before reading the URL; otherwise you will read the original tab’s address.

handles_before = set(driver.window_handles)
# Trigger the link or action that opens a tab here.
new_handle = (set(driver.window_handles) - handles_before).pop()
driver.switch_to.window(new_handle)
print(driver.current_url)

The example assumes your code has already waited until the second handle exists. Keep the handle associated with each tab if later steps must return to the original page.

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

Playwright pages and popups

Each Playwright Page has its own URL. Capture the new page object when the popup opens, then read that object’s property.

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    context = browser.new_context()
    page = context.new_page()
    page.goto("https://example.com")
    with context.expect_page() as popup_info:
        page.get_by_role("link", name="Open report").click()
    popup = popup_info.value
    popup.wait_for_load_state()
    print(popup.url)
    browser.close()

Reading page.url after this action would still refer to the opener, not the popup.

Common mistakes and fixes

Calling the property like a function

Symptom: TypeError: 'str' object is not callable.

Cause: Parentheses were added to a property.

Fix: Use driver.current_url or page.url, never driver.current_url() or page.url().

Reading too early after a click

Symptom: The value is the old address, an intermediate redirect, or a URL that does not satisfy the assertion.

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

Cause: The navigation has not reached the state your test expects. URL commitment and complete loading are separate events.

Fix: In Playwright, wait with page.wait_for_url(). In Selenium, explicitly wait for the expected URL or a page condition, then read the property.

Inspecting the wrong tab

Symptom: The URL is valid but belongs to the opener or another tab.

Cause: Selenium is still on the old window handle, or your Playwright code is reading a different Page object.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Fix: Switch Selenium to the new handle, or retain and inspect the Playwright page returned for the popup.

Seeing about:blank or an initial URL

Symptom: A newly created page reports about:blank.

Cause: No navigation has been issued yet, or the popup object was captured before its navigation began.

Fix: Navigate the page and synchronize with the popup or destination before reading its URL.

Using the wrong object in async Playwright

Symptom: An error appears when awaiting page.url, or code treats it as a coroutine.

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

Cause: Only Playwright operations such as goto() and wait_for_url() are awaited; page.url is a regular string property.

Fix: Await the navigation operation, then assign current = page.url.

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

Reliability and performance considerations

  • The property lookup itself is inexpensive; browser startup, navigation, redirects, and waits account for nearly all elapsed time in a URL-checking script.
  • Use one browser session for multiple related checks when isolation is not required. Recreating a browser for every URL adds startup overhead.
  • Use explicit, condition-based waits so a fast page is not delayed by a long fixed sleep, while a slower redirect is not read prematurely.
  • Record the URL at the point where it matters. A later navigation can change the same tab’s value, so do not assume a previously read string updates automatically.
  • When a test fails, log the observed URL and the expected pattern. This distinguishes a genuine application redirect from a synchronization or tab-selection bug.

Or skip the browser setup

If your goal is a rendered image or PDF rather than a Python string containing the current URL, ScreenshotNeo provides a single-request screenshot API. It accepts consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.

The API also offers an MCP server for Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools. Features include full-page captures with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page controls, custom CSS or JavaScript, clicks before capture, selector hiding, selector/delay/network-idle waits, request and resource blocking, custom headers/cookies/user agents, Authorization, timezone and geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Common parameter names used by other screenshot APIs are accepted to ease migration.

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

One-call examples

See the ScreenshotNeo documentation for the complete option list. The basic Python request is:

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)

Equivalent cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Equivalent 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}`);

Plans and billing

Plan Monthly allowance Price
Free 1,000 shots $0, no card
Starter 3,000 shots $5
Growth 15,000 shots $15
Pro 60,000 shots $39
Scale 250,000 shots $99
Business 1,000,000 shots $249

Every feature is available on every plan, and yearly billing gives two months free. This service does not replace driver.current_url or page.url when your program needs the address string; it is an option when the deliverable is a clean screenshot, PDF, or agent-accessible page information.

Sign up for the free plan to get 1,000 screenshots a month with no card, or move to paid usage starting at $5 for 3,000 shots.

Frequently Asked Questions

What if I only have a URL string and no browser object?

A URL string has no current browsing context to inspect. Selenium’s and Playwright’s accessors read the address held by an active WebDriver or Page; if you only need to manipulate a known string, use normal Python string or URL-parsing code instead of a browser accessor.

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

Can ScreenshotNeo return the URL string that Selenium or Playwright exposes?

No. ScreenshotNeo is intended for captures, PDFs, and page-information workflows. Use driver.current_url or page.url when your program specifically needs the browser’s current address.

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.