ScreenshotNeo

BlogHow-to

How to Get the Current URL in Puppeteer

Use Puppeteer’s synchronous `page.url()` method for the main frame. Learn when to wait for navigation, how to read an iframe URL, and how to handle single-page apps.

By the ScreenshotNeo team4 October 20266 min read

Call page.url() to get the current URL of the page’s main frame. It returns a string synchronously, so you do not need await just to read it:

const currentUrl = page.url();
console.log(currentUrl);

page.url() is a shortcut for page.mainFrame().url(). It does not return the URL of an embedded iframe; use that frame’s url() method instead. See the Puppeteer Page API.

Get the current page URL

Here is a complete Node.js example using Puppeteer. Install the package with npm install puppeteer, save this as current-url.js, and run node current-url.js. Puppeteer’s package manages a compatible browser installation for its standard setup.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', {
      waitUntil: 'domcontentloaded',
    });

    // page.url() is synchronous and returns the main frame URL.
    const currentUrl = page.url();
    console.log(currentUrl);
  } finally {
    await browser.close();
  }
})();

Use page.url() when you need the browser’s current address after navigation, a redirect, or an application-driven URL update. The value is the browser’s URL, including its path, query string, and fragment where present.

Read the URL after navigation

If an action can navigate the page, wait for the navigation and action together. Starting the navigation wait concurrently avoids missing a fast navigation:

const [response] = await Promise.all([
  page.waitForNavigation(),
  page.click('a.my-link'),
]);

console.log('Navigation response:', response);
console.log('Current URL:', page.url());

waitForNavigation() resolves to the main resource’s response, or null when there is no such response. A null response does not by itself mean the URL failed to change; read page.url() after the wait. For example, navigation to about:blank or a change to only the URL hash can have no main-resource response. See the Page.waitForNavigation API and Page.goto API.

Choose the appropriate wait condition for the page. Puppeteer’s navigation wait options include load, domcontentloaded, networkidle0, and networkidle2. The default is load. Pages with long-running requests may not reach a network-idle condition, so use a condition that matches what your next step needs.

await Promise.all([
  page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
  page.click('a.my-link'),
]);

const currentUrl = page.url();

For a direct navigation, wait for page.goto() to finish, then read the address:

const response = await page.goto('https://example.com/next', {
  waitUntil: 'domcontentloaded',
});

console.log('Response:', response);
console.log('Current URL:', page.url());

Handle single-page app URL changes

Puppeteer treats anchor navigation and History API URL changes as navigation. This includes common single-page app changes made with history.pushState() or related history operations. After the action that triggers the URL change, wait for that navigation and then call page.url(). See the Puppeteer FAQ on navigation.

await Promise.all([
  page.waitForNavigation(),
  page.click('[data-testid="open-account"]'),
]);

console.log(page.url());

If the application changes its URL without triggering a navigation event in the way your flow expects, wait for an application-specific condition, then read the URL. For example, wait for a known element that appears on the destination view:

await page.click('[data-testid="open-account"]');
await page.waitForSelector('[data-testid="account-view"]');

console.log(page.url());

Get an iframe’s URL

The main page URL is not the URL of every embedded frame. Find the relevant frame and call frame.url():

const frame = page.frames().find(frame =>
  frame.url().includes('/embedded/')
);

if (!frame) {
  throw new Error('Embedded frame was not found');
}

console.log(frame.url());

Choose a frame using a stable identifier for your application, such as a known URL path or the frame associated with a known element. Avoid assuming the first child frame is always the one you need. The Frame.url API returns the URL for that specific frame.

Common mistakes and troubleshooting

Symptom Cause Fix
The value is the old URL immediately after a click The code reads the URL before navigation or the app’s URL update finishes. Use Promise.all([page.waitForNavigation(), page.click(...)]), or wait for an application-specific destination condition before calling page.url().
waitForNavigation() returns null Some URL changes have no main-resource response, such as a hash change or navigation to about:blank. Check page.url() after the wait. Treat the response and the current URL as separate values.
The URL does not match the embedded content page.url() reports the main frame, not a child frame. Find the relevant Frame in page.frames() and read frame.url().
waitForNavigation() times out The action may not navigate, the selector may target the wrong element, or the page may not reach the selected wait condition. Confirm the action works and choose a suitable waitUntil condition. For app-driven changes, wait for a stable destination element if appropriate.
You used page.goto() to read the address page.goto(url) is a navigation method, not a getter. Await it when you intend to navigate, then call page.url() to read the resulting URL.
page.url is not a function The variable may not be a Puppeteer Page, or a different object was assigned to page. Check how the page was created, for example with const page = await browser.newPage(), and inspect the installed Puppeteer version if using older code.

Performance, reliability, and cost

Reading page.url() is a synchronous getter. The meaningful time cost in a typical automation flow is usually navigation or waiting for the page state, not retrieving the string. Avoid adding an arbitrary delay when you can wait for navigation or a specific destination condition.

For reliable automation, register navigation waits before or concurrently with actions that may navigate, handle the possibility of a null navigation response, and distinguish the main frame from child frames. Choose a navigation condition that fits the page; network-idle waits can be unsuitable for applications that keep requests open.

With Puppeteer, you run and maintain the browser automation environment. Browser startup, page loading, and repeated captures use your runtime and infrastructure. If your task is to obtain a screenshot rather than inspect the live browser URL, a screenshot API can avoid managing that browser setup.

Or skip the browser setup

If you need a screenshot of a URL rather than its value inside an active Puppeteer session, ScreenshotNeo returns an image or PDF from one GET request. The request and parameter options are documented at ScreenshotNeo’s API docs.

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} ${res.statusText}`);
}

const image = Buffer.from(await res.arrayBuffer());
await require('node:fs/promises').writeFile('shot.webp', image);
  • Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
  • Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed. Response headers report the page verdict and billing status.
  • An MCP server lets Claude, Cursor, and other MCP clients take screenshots, inspect page information, and capture PDFs.
  • The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; all features are available on every plan.

Sign up free for 1,000 screenshots a month, with no card.

FAQ

Does page.url() need await?

No. It returns the current URL string synchronously. Await the operation that changes the page first, when needed.

Does page.url() include query parameters and a hash?

It returns the current main frame URL, including the URL components present in the browser’s current address.

Can I get a URL without navigating?

Yes. Call page.url() whenever you need to read the current main frame address.

Which Puppeteer version should I use?

Check the API documentation and the version installed in your project, especially if you are maintaining an older Puppeteer setup. The method is documented in the current Page API.