Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesTo get a PDF returned by a website as a Node.js buffer, wait for the matching Puppeteer HTTPResponse and call await response.buffer(). Start waiting before the click or navigation that triggers the request, then identify the PDF response by its URL, status, or Content-Type. Use page.pdf() instead only when you want Puppeteer to generate a new PDF from the rendered page.
Get the PDF response as a buffer
HTTPResponse.buffer() resolves to a Node.js Buffer containing the response body. The key is selecting the response caused by your action—not assuming that the click itself returns the PDF or that the next response is the right one.
Wait for the response, trigger the download, and save it
This CommonJS example assumes page is an already-open Puppeteer page and that clicking #download-pdf requests a PDF. It writes the returned bytes to document.pdf.
const fs = require('node:fs/promises');
const responsePromise = page.waitForResponse(response => {
const contentType = response.headers()['content-type'] || '';
return response.status() === 200 &&
contentType.toLowerCase().includes('application/pdf');
});
await page.click('#download-pdf');
const response = await responsePromise;
const pdfBuffer = await response.buffer();
await fs.writeFile('document.pdf', pdfBuffer);
Create the waitForResponse() promise before the click. If you click first and wait second, a fast request may already have completed before Puppeteer starts listening. Awaiting the promise after the click lets the action and response wait overlap correctly.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall#1 Best Overall
Match a stable URL when the content type is unreliable
If the endpoint has a known path, URL matching can be more dependable than relying on a server header. Include a status check so an error page returned from the same endpoint is not mistaken for the file.
const responsePromise = page.waitForResponse(response =>
response.url().includes('/reports/') && response.status() === 200
);
await page.click('#download-pdf');
const response = await responsePromise;
const pdfBuffer = await response.buffer();
For a site with multiple report requests, make the predicate more specific: match the exact report path or a known query parameter, and check the content type too when the endpoint supplies it. Puppeteer responses expose url(), status(), headers(), and request() to help distinguish candidates.
Make response selection robust
Use the narrowest useful predicate
A predicate that only checks for status 200 can match images, API calls, or an HTML page. Prefer a combination suited to the site:
- Known endpoint: match the PDF route or report identifier, plus a successful status.
- Useful response header: require
Content-Typeto includeapplication/pdf. Compare case-insensitively and allow for parameters such as a charset by checking for inclusion rather than exact equality. - Ambiguous route: combine URL and content-type checks; inspect the response request if the page makes similar requests for several actions.
Do not assume a filename in the URL proves the body is a PDF. Conversely, some servers return a PDF without an accurate content-type header, which is when a stable URL and later byte validation can help.
Set a timeout and handle a missing match
waitForResponse() waits for a response satisfying the predicate. If the action fails, opens a different route, or the predicate is too restrictive, the wait can time out. Catch that failure at the operation boundary and report the action and expected endpoint rather than proceeding with an undefined response.
try {
const responsePromise = page.waitForResponse(response =>
response.url().includes('/reports/quarterly.pdf') &&
response.status() === 200,
{ timeout: 15000 }
);
await page.click('#download-pdf');
const response = await responsePromise;
const pdfBuffer = await response.buffer();
await fs.writeFile('quarterly.pdf', pdfBuffer);
} catch (error) {
console.error('Could not capture the expected PDF response:', error);
}
Choose a timeout that fits the site and your job deadline; it is not a guarantee the server will finish within that time. If a timeout is frequent, verify the click actually triggers the request and that your filter matches the observed URL, status, and headers.
Save or forward the bytes without corrupting them
Keep the result as a Buffer when writing a file or passing it to a binary-storage or upload API. Do not convert it to a UTF-8 string: arbitrary PDF bytes are not text, and decoding them as text can alter or lose byte values.
await fs.writeFile('document.pdf', pdfBuffer);
// Example shape for an API that accepts binary data:
await uploadClient.upload({ body: pdfBuffer });
Puppeteer documents that a response buffer might be re-encoded by the browser based on HTTP headers or other heuristics. If exact byte fidelity matters, check the endpoint’s headers and validate the resulting file in your application. A typical PDF begins with the byte sequence represented by %PDF-; that check is a useful sanity check, not a substitute for parsing or validating the full file.
Handle bodyless responses and listener failures
Not every browser response has a readable body. CORS preflight requests commonly use OPTIONS; status codes 204 and 304 are also cases where a body is unavailable or not expected. Avoid calling buffer() on every response in a broad listener.
Filter defensively in a response listener
A listener is useful when you need to observe responses across several actions. Filter before reading, and catch errors because a matching response body may still be unavailable.
page.on('response', async response => {
if (response.request().method() === 'OPTIONS') return;
if ([204, 304].includes(response.status())) return;
const contentType = response.headers()['content-type'] || '';
if (!contentType.toLowerCase().includes('application/pdf')) return;
try {
const pdfBuffer = await response.buffer();
await fs.writeFile('captured.pdf', pdfBuffer);
} catch (error) {
console.error('PDF body unavailable:', response.url(), error);
}
});
For a single user action that should produce one PDF, waitForResponse() is usually easier to reason about: it gives the operation one explicit response to await. A listener can observe multiple matching PDFs, so production code may need its own deduplication or destination naming.
Choose between a server PDF and a Puppeteer-generated PDF
response.buffer() captures bytes the server returned. page.pdf() asks Puppeteer to render the current page as a new PDF. These are different sources of truth; rendering a page is not a way to retrieve the original server file.
| Question | response.buffer() |
page.pdf() |
|---|---|---|
| What are the bytes? | The body of a selected network response, such as a PDF download. | A newly generated PDF of the current rendered page. |
| When to use it | The server already creates and returns the PDF you need. | You want a PDF representation of the page DOM as rendered by Puppeteer. |
| Return type | Promise<Buffer>. |
Promise<Uint8Array>; convert to a Node buffer with Buffer.from() if required. |
| Rendering behavior | Does not create a print layout; it reads the response body. | Generates a PDF using the print CSS media type. |
Generate a PDF from the current page
const pdfBytes = await page.pdf({
format: 'A4',
printBackground: true
});
const pdfBuffer = Buffer.from(pdfBytes);
await fs.writeFile('page.pdf', pdfBuffer);
Use the response route when you need the server’s finished document, including its server-side report content or authorization behavior. Use page rendering when the browser page itself is the document you intend to print and a print-style rendering is acceptable.
Or skip the browser setup
If your goal is to create a PDF capture of a webpage rather than retrieve a PDF file that a site already serves, ScreenshotNeo offers a screenshot API with PDF output. It is not a replacement for collecting an existing server response; it captures a page as a new document. For API parameters and setup, see the ScreenshotNeo documentation.
One GET request can return a PDF. For example, using the documented endpoint and adapting the target URL:
Rank #4
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o page.pdf
- Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; each cleanup step can be turned off.
- Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing status.
- An MCP server provides screenshot tools for AI agents and MCP clients.
- The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.
Create a free ScreenshotNeo account to start with 1,000 screenshots a month and no card.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Troubleshoot common problems
The wait times out
- Cause: The click did not trigger a network request, the request failed before a matching response, or the predicate excludes the actual response.
- Fix: Confirm the selector and action, inspect the endpoint and response headers, then relax or refine the predicate based on what the site actually returns. Keep the wait promise registered before triggering the action.
The saved file is HTML or an error document
- Cause: The matched response may be a login page, an error response, or another request to a similar URL.
- Fix: Check
response.status(),response.url(), and the content type before saving. Match the report identifier or endpoint more narrowly; validate the resulting bytes when the server’s headers are unreliable.
buffer() fails or produces no usable body
- Cause: The response may be bodyless, unavailable, or not the intended PDF; broad listeners can also encounter preflight and other non-document traffic.
- Fix: Filter out
OPTIONS,204, and304cases where appropriate, and catch read errors aroundresponse.buffer().
The file exists but a PDF reader rejects it
- Cause: The response may not have been a PDF, or browser/header-driven re-encoding may have affected the response buffer.
- Fix: Verify status and headers, inspect whether the output begins with
%PDF-, and validate the document using your application’s PDF handling. If exact fidelity is required, investigate the endpoint’s response headers and whether its bytes are being transformed.
The wrong PDF is saved when several requests happen
- Cause: A generic content-type predicate can match an unrelated PDF request on the same page.
- Fix: Combine the content type with an exact route, report ID, or other stable request detail, and use distinct filenames when capturing multiple documents.
Performance and reliability considerations
Reading a response into a buffer means the complete response body is held in memory before the file write completes. For a large PDF or many simultaneous captures, limit concurrent work to suit the memory available to the process and avoid retaining buffers after they have been persisted or uploaded. A response listener that saves every matching document can multiply memory and disk use if the page makes repeated requests.
Reliability depends on identifying the right response and handling failures at each boundary: the triggering action, response wait, body read, and persistence step. Log the selected response URL and status alongside errors, but avoid logging credentials or sensitive document contents. Retry only when the action is safe to repeat; a click that creates a report or otherwise changes server state may not be idempotent.
Frequently Asked Questions
Can I use response.buffer() after the page has navigated?
Yes, if you retained the matching HTTPResponse object and its body is still available. In practice, register the response wait before the navigation or action so the relevant response is not missed.
Does response.buffer() create a PDF from the page?
No. It reads bytes from a network response. To render the current page into a PDF, use page.pdf().
Recommended Free Tools
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.




