ScreenshotNeo

BlogHow-to

How to Get the URL of a Puppeteer Response

Use Puppeteer’s response.url() to get a response URL. Learn how it behaves with redirects, null navigation results, and HTTP errors.

By the ScreenshotNeo team4 October 20265 min read

Call response.url() on Puppeteer’s HTTPResponse object. It returns the response URL as a string. For a navigation, page.goto() returns the final response after redirects, so this gives you the final URL. Check that the navigation response is not null first.

const response = await page.goto('https://example.com');

if (response) {
  const finalUrl = response.url();
  console.log(finalUrl);
}

1. Get the URL from a navigation response

page.goto() resolves to the main document’s HTTPResponse, or null in documented cases such as navigating to about:blank or to the same URL with only a different hash. The url() method itself is synchronous; do not await it.

import puppeteer from 'puppeteer';

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

  if (!response) {
    console.log('Navigation returned no HTTP response.');
  } else {
    console.log('Response URL:', response.url());
    console.log('Status:', response.status());
  }
} finally {
  await browser.close();
}

Use the response URL when you need the address associated with the document response. If you only need the URL Puppeteer attempted to navigate to, keep that input separately; the two can differ after redirects.

2. Get the final URL and inspect redirects

For a redirecting navigation, Puppeteer resolves page.goto() with the response to the last redirect. Consequently, response.url() is the final response URL. To inspect prior request URLs, use the response’s request and its redirect chain.

const response = await page.goto('http://example.com');

if (response) {
  console.log('Final response URL:', response.url());
  const earlierRequests = response.request().redirectChain();
  console.log('Earlier redirect requests:', earlierRequests.map(request => request.url()));
}

The redirect chain contains the earlier requests; the final request is represented by the response’s own request. For a navigation without redirects, the chain is empty.

3. Request URL versus response URL

What you need Puppeteer value Meaning
URL for a request request.url() The URL associated with that HTTP request.
URL for a response response.url() The URL associated with that HTTP response.
Earlier URLs in a redirect sequence response.request().redirectChain() Prior requests; map them to request.url().

These APIs refer to different objects. Use the request URL when logging what was requested, and the response URL when you want the URL of the response you received.

4. Handle status codes and missing responses

An HTTP error status such as 404 or 500 can still have an HTTPResponse. A response existing does not mean the server returned a successful status. Check status() separately:

const response = await page.goto('https://example.com/missing');

if (response) {
  const status = response.status();
  console.log({ url: response.url(), status });
  if (status >= 400) {
    console.error('The server returned an HTTP error status.');
  }
} else {
  console.log('No navigation response was returned.');
}

This check is distinct from navigation failures that throw, such as a timeout or a network-level failure. Catch those around page.goto() if your script must continue after a failed navigation.

5. Capture the main document response explicitly

When a workflow is driven by navigation, use the response returned by page.goto(). If you need to observe response events instead, filter for the main document request so that an image, stylesheet, or API call is not mistaken for the page URL:

page.on('response', response => {
  if (response.request().isNavigationRequest()) {
    console.log('Navigation response URL:', response.url());
  }
});

await page.goto('https://example.com');

A page can load many subresource responses. Logging every response.url() may produce URLs for scripts, images, fonts, and fetch requests rather than the top-level document.

6. Troubleshooting

Symptom Likely cause Fix
Cannot read properties of null (reading 'url') page.goto() returned null, for example for about:blank or a same-URL hash navigation. Check the result before calling url().
The URL is different from the one passed to goto() The server redirected the navigation. Use response.url() for the final response and redirectChain() for earlier requests.
The script reports an error page but has a response HTTP status errors such as 404 and 500 can still produce a response. Inspect response.status(); handle network exceptions separately.
The logged URL belongs to an asset or API call The response event fired for a subresource. Filter with response.request().isNavigationRequest(), or use the result of page.goto().
response is undefined outside the navigation code The variable was not retained in that scope, or the code is reading a different event/API result. Assign the value returned by await page.goto() and pass it to the function that needs it.
Navigation throws before a response is assigned A timeout or network-level failure prevented a normal navigation result. Catch the navigation error, adjust the timeout or wait condition for the site, and record that no response URL was obtained.

7. Reliability and performance notes

  • Guard nullable results: handle the documented null cases before reading the URL.
  • Separate URL from success: record the response URL and status as separate fields in logs.
  • Choose a suitable navigation wait condition: waiting for a full load can take longer on pages with slow resources. Use a wait condition appropriate to the work; it does not change what response.url() means.
  • Keep redirects observable: store the requested URL, final response URL, and redirect chain if you need an audit trail.
  • Check your installed version: consult the Puppeteer API reference for the version in your project if version-specific behavior matters.

Reading url() is a local accessor call on an existing response. Most of the cost and time in this workflow comes from launching or reusing a browser and loading the page, rather than retrieving the URL string.

8. Or skip the browser setup

If your goal is to capture a page rather than inspect Puppeteer’s response object, ScreenshotNeo returns a screenshot or PDF from one request. See the API documentation.

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

Cookie banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. ScreenshotNeo also offers an MCP server for AI agents, and includes 1,000 screenshots per month free 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.

9. FAQ

Is response.url() asynchronous?

No. It returns a string, so call it directly without await.

Does page.goto() throw for a 404?

An HTTP status such as 404 can be returned as a response. Check status() rather than treating every non-2xx status as a missing response.

How do I get the URL before redirects?

Read the URLs from response.request().redirectChain(); those are the earlier requests in the redirect sequence.

What if page.goto() returns null?

There is no response URL to read from that result. Handle the null case and retain the URL you originally supplied if you need it for logging.