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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Build the final URL with JavaScript’s URL and searchParams, then pass that URL to Playwright’s page.goto(). This keeps encoding correct, makes repeated parameters explicit, and lets you wait for the page state your task actually needs instead of guessing from a navigation event.

Build a URL safely, then navigate to it

Do not concatenate user-supplied values directly onto a URL. Create a URL object and let URLSearchParams encode spaces, ampersands and other reserved characters.

import { chromium } from 'playwright';

const target = new URL('https://example.com/search');
target.searchParams.set('q', 'headless browser');
target.searchParams.set('page', '2');

const browser = await chromium.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.goto(target.toString());
  // Assert on a meaningful page condition before reading results.
} finally {
  await browser.close();
}

set(name, value) replaces an existing value, so it is appropriate when a parameter should occur once. Use append(name, value) for repeated keys:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const target = new URL('https://example.com/products');
target.searchParams.append('tag', 'new');
target.searchParams.append('tag', 'sale');
console.log(target.toString());

The destination site decides what repeated keys mean. Some applications treat them as a list; others use only the first or last value. Confirm the receiving application’s contract rather than assuming every server parses duplicates identically.

Complete Playwright procedure

  1. Install Playwright and its browser binaries. Use the version that matches your project and follow its installation command.
  2. Create an absolute URL. Include a scheme such as https://. Relative paths work when you configure a baseURL, but an absolute URL is clearer for standalone scripts.
  3. Add parameters with set or append. Keep values as strings and let the URL API encode them.
  4. Launch an isolated browser. Playwright runs headless by default in its BrowserType API; pass headless: true when you want that choice to be explicit.
  5. Create a page and call page.goto(target.toString()). Keep the final URL available for logging so a failed run can be reproduced.
  6. Wait for the application condition your next action needs. Assert on a result element, a known state, or a page-specific signal before extracting data.
  7. Close the browser in a finally block. This prevents orphaned browser processes when navigation or assertions fail.

Navigation waits: choose the signal that matters

Playwright supports navigation conditions including load, domcontentloaded, networkidle and commit. A navigation event only says something about document loading; it does not prove that a client-rendered application has finished its work.

Prefer a meaningful element or state

If the next operation needs search results, wait for the results container or an application-specific success state. A web assertion expresses the real requirement and usually avoids racing a JavaScript render.

await page.goto(target.toString(), { waitUntil: 'domcontentloaded' });
await page.locator('[data-testid="search-results"]').waitFor();
const titles = await page.locator('[data-testid="result-title"]').allTextContents();

Use broad network waits cautiously

networkidle means Playwright observed no network connections for a period; it does not mean the page is correct. Analytics, polling and advertisements can keep a page active, while an application can still be incomplete despite temporary quiet. Playwright’s guidance discourages relying on networkidle as a general test-readiness signal.

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.

Browser navigation versus an HTTP API request

There are two different Playwright pathways that accept query parameters:

Need Use What happens
Render a real page, run JavaScript, interact with the DOM page.goto(url) A browser navigates to the URL and executes the page like a user-facing tab.
Call an HTTP endpoint without a browser APIRequestContext.get(url, { params }) Playwright serializes params into the URL query string and sends an HTTP request.

For an API endpoint, a browser adds startup time and complexity without providing DOM behavior. For a client-rendered page, an API request may return only an HTML shell or an entirely different response.

const response = await request.get('https://api.example.com/items', {
  params: { q: 'headless browser', page: 2 }
});
const data = await response.json();

The params value can be an object, a URLSearchParams instance or a query string. It is separate from page.goto(); do not expect an API request to execute page JavaScript.

Headless mode is not one identical browser

Playwright’s default Chromium selection uses a separate headless shell when no channel is specified. You can opt into the newer Chromium headless implementation with channel: 'chromium':

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const browser = await chromium.launch({
  headless: true,
  channel: 'chromium'
});

Installed branded Chrome or Edge channels use their own newer headless implementation and can behave differently from the shell. Choose deliberately and record the channel in CI so a local-to-server change is visible. The newer mode is intended to be closer to regular Chrome behavior, but that is a design distinction, not a universal performance guarantee.

Failures that look like successful navigation

HTTP errors

page.goto() does not throw solely because the server returned a status such as 404 or 500. Inspect the response when status matters:

const response = await page.goto(target.toString());
if (response && !response.ok()) {
  throw new Error(`Navigation returned HTTP ${response.status()}`);
}

PDF destinations

Headless mode does not support navigating to a PDF document as a browser page. Download or process the PDF through an HTTP client, or use a PDF capture workflow rather than expecting DOM navigation.

Profile and authentication problems

Use a separate automation profile or a fresh browser context. Automating a personal default Chrome profile is unsupported under Chrome’s current policy changes and can corrupt assumptions about cookies, extensions and session state.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When a query parameter changes the page’s behavior

A parameter has no magical meaning to a browser. The application must read it and act on it. For example, a server-rendering pipeline might add an empty headless parameter:

const renderUrl = new URL('https://example.com/article');
renderUrl.searchParams.set('headless', '');
await page.goto(renderUrl.toString());

Page code can then test new URL(location.href).searchParams.has('headless') and disable work that is unnecessary for rendering. Treat this as an application-specific convention, not a Playwright feature.

Protect analytics during prerendering

A prerendered visit and the later human visit can both send analytics hits, inflating pageview counts. If you use a query flag for server rendering, make analytics behavior an explicit part of that rendering path and verify it against the analytics system and current request-interception APIs. Do not copy an old interception snippet without checking the framework and analytics versions you run.

Puppeteer follows the same lifecycle

Puppeteer’s documented flow is likewise launch, create a page, navigate, interact and close. The URL construction remains ordinary JavaScript:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const target = new URL('https://example.com/search');
target.searchParams.set('q', 'headless browser');

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.goto(target.toString());
} finally {
  await browser.close();
}

The important design choice is the same in both frameworks: create the final URL before navigation, then wait for an application condition rather than treating the first navigation event as proof of readiness.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API when you need an image or PDF rather than a full browser automation script. It accepts query parameters in one GET request and removes cookie banners, newsletter popups and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf.

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

See the ScreenshotNeo documentation for parameters, PDF options, CSS and JavaScript controls, waits, device presets, signed links, webhooks and bulk capture. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

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.

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.