How to Pass a Variable into a Puppeteer page URL
Build a URL from JavaScript variables and pass it safely to Puppeteer’s page.goto(), with examples for query parameters, path segments, and relative URLs.

To pass a variable into a Puppeteer page URL, construct the URL as a JavaScript string, then pass it to await page.goto(url). For query parameters, use Node.js’s URL API so values containing spaces, ampersands, or other reserved characters are encoded as parameter values.
import puppeteer from 'puppeteer';
const searchTerm = 'puppeteer page url';
const target = new URL('https://example.com/search');
target.searchParams.set('q', searchTerm);
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const response = await page.goto(target.href);
console.log('Navigated to:', page.url());
console.log('HTTP status:', response?.status() ?? 'no main-resource response');
} finally {
await browser.close();
}
page.goto() navigates to the URL string you give it. The URL variable is created in your Node.js process before the browser navigation starts. See the Puppeteer Page API and Node.js URL documentation.
1. Set up a runnable Puppeteer script
If Puppeteer is not already in your project, create a directory and install it with npm:
mkdir puppeteer-variable-url
cd puppeteer-variable-url
npm init -y
npm install puppeteer
Save the following as capture.mjs. The .mjs extension lets Node.js run the ES module import shown in the example without changing package.json.
import puppeteer from 'puppeteer';
const category = 'running shoes';
const destination = new URL('https://example.com/search');
destination.searchParams.set('q', category);
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const response = await page.goto(destination.href, {
waitUntil: 'domcontentloaded',
timeout: 30_000,
});
console.log('Final URL:', page.url());
console.log('Status:', response?.status() ?? 'navigation returned no response');
console.log('Title:', await page.title());
} finally {
await browser.close();
}
Run it with node capture.mjs. Replace the example host with a site you are permitted to access. The HTTP status check is useful because navigation can return a response with an error status; in headless shell, Puppeteer documents that valid HTTP statuses such as 404 and 500 do not themselves make goto() throw.
2. Put the variable in the correct URL component
First decide where the variable belongs. URL path segments, query parameters, and fragments have different meanings and encoding rules. A string that happens to work in one position may change the structure of another URL when it contains reserved characters.
Query parameter: use URL.searchParams
This is the recommended pattern for search terms, filters, IDs supplied as query values, and similar inputs:
const term = 'red shoes & socks';
const target = new URL('https://example.com/search');
target.searchParams.set('q', term);
console.log(target.href);
// https://example.com/search?q=red+shoes+%26+socks
set(name, value) adds the parameter if it is missing and replaces existing values with that name. Use append() when the destination intentionally accepts repeated keys:
const target = new URL('https://example.com/search');
target.searchParams.append('tag', 'puppeteer');
target.searchParams.append('tag', 'node.js');
await page.goto(target.href);
Use getAll('tag') when reading repeated values; get('tag') returns the first value. Query parameters are serialized by the URL API. Do not apply encodeURIComponent() to a value and then pass that encoded result to searchParams.set(): doing so can encode the percent signs a second time.
Path segment: encode the value for a path
If the variable identifies one path segment, encode that segment instead of treating it as a query parameter. For example, a slash inside a user-provided ID should not accidentally become a path separator.
const userId = 'team/a & b';
const target = new URL(
`/users/${encodeURIComponent(userId)}`,
'https://example.com'
);
await page.goto(target.href);
Keep the distinction clear: searchParams handles query values; encodeURIComponent() is appropriate for an individual path component. Do not encode an entire URL as though it were a single component.
Relative URL: provide an explicit base
A relative value such as /products/42 is not a complete destination by itself. Resolve it against a known origin before calling Puppeteer:
const path = '/products/42';
const target = new URL(path, 'https://example.com');
await page.goto(target.href);
The base must be a valid absolute URL. Using an explicit base makes it clear which host will receive the navigation and avoids depending on a page’s current location.
3. Choose a construction method
| Input and destination | Recommended construction | What to watch |
|---|---|---|
| Query value | URL.searchParams.set() |
Use the unencoded original value; the API serializes it. |
| One path segment | encodeURIComponent(value) inside a path template |
Encode the segment, not the whole URL. |
| Relative path | new URL(relative, base) |
Set a trusted, explicit base. |
| Known-safe literal path | Template literal | Only when the value is valid for that exact path position. |
| Complete URL supplied as input | new URL(input) plus validation |
Check protocol and allowed host before navigation. |
Template literals are concise for controlled values:
const userId = '42';
const url = `https://example.com/users/${userId}`;
await page.goto(url);
They do not automatically make arbitrary input safe for a URL component. Use the structured constructor when values may contain spaces, ampersands, question marks, slashes, or other special characters.
4. Validate URLs that come from outside your code
If a URL, hostname, or path comes from a request, file, database, or user input, validate it before navigating. At minimum, require an expected protocol and restrict the hostname to the destinations your application intends to visit. This prevents malformed values and helps avoid letting untrusted input steer a browser to unintended internal or external addresses.
function makeTarget(input) {
const target = new URL(input);
const allowedHosts = new Set(['example.com', 'www.example.com']);
if (target.protocol !== 'https:') {
throw new Error('Only HTTPS URLs are allowed');
}
if (!allowedHosts.has(target.hostname)) {
throw new Error(`Host is not allowed: ${target.hostname}`);
}
return target;
}
const target = makeTarget('https://example.com/products/42');
await page.goto(target.href);
For a fixed host and variable query value, prefer constructing from the fixed base and setting the query parameter. This avoids accepting a whole destination URL when you only need one value.
5. Control navigation and inspect the result
page.goto(url, options) accepts navigation options, including a wait condition and timeout. For example, domcontentloaded waits for the document to be parsed, while load waits for the load event. Choose the condition based on what the next step needs; a page that renders content later may require waiting for a specific selector.
const response = await page.goto(target.href, {
waitUntil: 'domcontentloaded',
timeout: 30_000,
});
if (response && !response.ok()) {
throw new Error(`Navigation returned HTTP ${response.status()}`);
}
await page.waitForSelector('main', { timeout: 10_000 });
Check response before calling methods on it: Puppeteer documents that navigation to about:blank or to the same URL with only a different hash can return null. Also, a successful goto() call is not proof that the intended page content appeared; inspect the response status, final URL, and a page-specific selector or title.
6. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
Cannot navigate to invalid URL or URL parsing fails |
The string is empty, malformed, or missing a scheme. | Log the constructed URL and create a valid absolute URL with https://, or resolve a relative path using new URL(path, base). |
| The server receives an unexpected query value | Manual concatenation let &, #, or ? change the URL structure. |
Set query values using target.searchParams.set(name, value). |
| A slash in an identifier opens a nested route | A path value was inserted without component encoding. | Encode that one path segment with encodeURIComponent(). |
The value appears with %25 or other extra encoding |
The input was encoded before being passed to an API that encodes it again. | Pass the original text to searchParams.set(); avoid double-encoding. |
Navigation timeout exceeded |
The page did not reach the selected wait condition before the timeout. | Check network access and the target response; choose a wait condition suited to the page, set a considered timeout, or wait for a needed selector after DOM content loads. |
| No exception, but the page is an error page | The server returned an HTTP error status that did not throw. | Inspect response?.status() and handle non-success status codes explicitly. |
| The URL is correct but content is not ready | Navigation completed before client-side rendering or a needed element appeared. | Wait for a stable, page-specific selector and handle its timeout. |
7. Performance, reliability, and cost
Building a URL with URL and URLSearchParams is local string processing; the main wait is usually browser navigation and page behavior. Avoid launching a fresh browser for every URL in a large job when your program can safely reuse a browser process and create pages as needed. Always close pages or the browser when finished so browser processes do not linger.
Set a deliberate navigation timeout rather than allowing a stalled request to hold a worker indefinitely. Pick a wait condition tied to the work: waiting for the full load event can be unnecessary for a task that only needs the parsed document, while a selector wait makes sense when the next action depends on rendered content. Handle timeouts and non-success responses as separate outcomes, and record the destination and failure reason without logging secrets embedded in query values.
Each Puppeteer run uses your compute and browser resources. The total cost depends on where and how you run Node.js and Chrome; this URL construction pattern does not set a hosting price or guarantee a runtime. For repeated jobs, account for browser startup, page concurrency, memory use, and retries. Retry transient failures with limits and backoff rather than creating an unbounded loop.
8. Or skip the browser setup
If the goal is to get a screenshot of a URL rather than automate a browser workflow, ScreenshotNeo offers a website screenshot API. Send one GET request with the target URL and receive an image or PDF. See the ScreenshotNeo API documentation for request options.
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}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
- Cookie banners are accepted and removed before the shot, along with known newsletter popups and chat widgets; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed. Response headers say which page verdict applied and whether it was billed.
- An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
- The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, with no card required.
9. FAQ
Can I pass a number or other non-string value?
URL components are strings. Convert a numeric ID explicitly with String(id) when constructing a path or query value, so the intended representation is clear.
Can I pass a URL object directly to Puppeteer?
Pass its serialized string, usually target.href, to page.goto(). This makes the conversion explicit and easy to log before navigation.
Does Puppeteer add variables to a URL automatically?
No. Your Node.js code builds the destination string first; Puppeteer navigates to the URL it receives.
What if the destination already has query parameters?
Parse the complete base with new URL(), then use searchParams.set() to add or replace a key without manually rebuilding the existing query string.


