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:
Recommended Free Tools
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.
#1 Best Overall
Complete Playwright procedure
- Install Playwright and its browser binaries. Use the version that matches your project and follow its installation command.
- Create an absolute URL. Include a scheme such as
https://. Relative paths work when you configure abaseURL, but an absolute URL is clearer for standalone scripts. - Add parameters with
setorappend. Keep values as strings and let the URL API encode them. - Launch an isolated browser. Playwright runs headless by default in its BrowserType API; pass
headless: truewhen you want that choice to be explicit. - Create a page and call
page.goto(target.toString()). Keep the final URL available for logging so a failed run can be reproduced. - 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.
- Close the browser in a
finallyblock. 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.
Rank #2
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':
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11const 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:
Rank #4
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.
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:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →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.
Quick Recap
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.

