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.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

You can send a PDF to Google Drive without writing it to a local file: if a web page already serves the PDF, retrieve its response bytes; if you want a PDF of the rendered page, generate it with Puppeteer’s page.pdf(). Then pass the bytes to Drive API v3 with files.create. These are different workflows, and Puppeteer does not provide a general programmatic browser-download handler.

First decide: retrieve a PDF or generate one

“Fetch a PDF with Puppeteer” can mean either locating and retrieving a PDF that a site already serves, or printing the page currently open in Chromium into a new PDF. Use the first path when the document itself is the target. Use the second when you need a PDF representation of the rendered page.

What you need What to do Data sent to Drive
An existing PDF from a site Identify its final PDF URL or response, then request and validate the binary content in Node.js. HTTP response bytes
A PDF made from the rendered page Navigate with Puppeteer, wait for the desired state, then call page.pdf() or page.createPDFStream(). A Uint8Array or compatible stream

Puppeteer’s official Files guide says it “does not offer a way to handle file downloads in a programmatic way.” That is not a restriction on PDF generation: Puppeteer’s PDF method prints a page and returns a Uint8Array.

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

Set up Google Drive API access

Enable Drive API access for the Google Cloud project used by your application and initialize the Drive v3 client with an authentication method and scopes appropriate to where the code runs. The credentials, scope, destination folder access, and resulting file ownership depend on that setup; an authenticated request alone does not guarantee access to every Drive location.

Install the Node.js packages used in the examples:

npm install puppeteer googleapis

The following setup uses Google’s GoogleAuth client initialization pattern. Configure credentials in the environment or runtime according to Google’s authentication guidance rather than embedding secrets in source code.

import { google } from 'googleapis';

const auth = new google.auth.GoogleAuth({
  scopes: ['https://www.googleapis.com/auth/drive.file'],
});
const drive = google.drive({ version: 'v3', auth });

Use a scope and credential type that fit the deployment and the files the app must manage. See Google’s Create and manage files guide for file creation and client setup.

Path A: retrieve an existing PDF

Use Puppeteer to discover or trigger the PDF request

If the URL is known, you may not need a browser at all. If the site exposes the link only after interaction, or generates a signed URL as part of page activity, use Puppeteer to reach that state and observe the relevant request or response. The Puppeteer Page API documents navigation and request/response waiting methods.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
The Google Workspace Bible: [14 in 1] The Ultimate All-in-One Guide from Beginner to Advanced | Including Gmail, Drive, Docs, Sheets, and Every Other App from the Suite
  • The Google Workspace Bible: [14 in 1] The Ultimate All in One Guide from Beginner to Advanced Including Gmail, Drive, Docs, Sheets, and Every Other App from the Suite
  • ABIS BOOK

For example, when a stable link is available in the page, read its URL, then fetch that URL with Node’s HTTP client. Select the actual PDF link or response for your site; there is no universal selector or response pattern.

import puppeteer from 'puppeteer';
import { google } from 'googleapis';

const pageUrl = 'https://example.com/document-page';
const browser = await puppeteer.launch({ headless: true });

try {
  const page = await browser.newPage();
  await page.goto(pageUrl, { waitUntil: 'domcontentloaded', timeout: 30_000 });

  // Replace this selector with the actual PDF link on the target page.
  const pdfUrl = await page.$eval('a[href$=".pdf"]', link => link.href);
  const response = await fetch(pdfUrl, { signal: AbortSignal.timeout(60_000) });

  if (!response.ok) {
    throw new Error(`PDF request failed: HTTP ${response.status}`);
  }
  const contentType = response.headers.get('content-type') ?? '';
  const bytes = Buffer.from(await response.arrayBuffer());
  if (!contentType.toLowerCase().includes('pdf') || bytes.length === 0) {
    throw new Error(`Expected a non-empty PDF; received content-type: ${contentType || 'not provided'}`);
  }

  const auth = new google.auth.GoogleAuth({
    scopes: ['https://www.googleapis.com/auth/drive.file'],
  });
  const drive = google.drive({ version: 'v3', auth });
  const result = await drive.files.create({
    requestBody: { name: 'downloaded-document.pdf', mimeType: 'application/pdf' },
    media: { mimeType: 'application/pdf', body: bytes },
    fields: 'id,name,mimeType',
  });
  console.log(result.data);
} finally {
  await browser.close();
}

This example assumes the PDF is publicly fetchable from the Node process. A browser-visible link may depend on cookies, authorization headers, redirects, or a short-lived signed URL. In those cases, preserve the request context the site requires or use the browser response body when appropriate; do not assume that a URL copied from the page is independently accessible.

The MIME type is a useful check, but servers sometimes omit or mislabel it. For stricter validation, inspect the returned bytes and handle the site’s documented response behavior instead of treating any successful HTTP response as a PDF.

When the PDF URL is not exposed as a link

You can wait for a response while performing the page action that triggers the PDF request, then inspect the response URL and status. The exact click and response predicate depend on the site; avoid a broad predicate that could select an unrelated request.

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.
const [pdfResponse] = await Promise.all([
  page.waitForResponse(response =>
    response.url().includes('/download') && response.status() === 200
  ),
  page.click('button.download'),
]);

const pdfBytes = Buffer.from(await pdfResponse.buffer());

Use this only when the site’s response is actually the PDF. Check the response headers and body before uploading. If the site initiates a browser download rather than returning a response matching your predicate, Puppeteer’s Files guide does not offer a general API to handle that download as a file; locate the underlying resource request or use a supported endpoint instead.

Path B: generate a PDF from the rendered page

Use page.pdf() when you want what Chromium prints, not a PDF file that a website already hosts. Puppeteer’s PDF API uses print media by default. If the desired rendering is screen CSS, emulate that media type before generating the PDF.

import puppeteer from 'puppeteer';
import { google } from 'googleapis';

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.goto('https://example.com/report', {
    waitUntil: 'networkidle0',
    timeout: 60_000,
  });

  // Optional: use screen styles rather than the default print media.
  // await page.emulateMediaType('screen');

  const pdfBytes = await page.pdf({
    format: 'A4',
    printBackground: true,
  });

  const auth = new google.auth.GoogleAuth({
    scopes: ['https://www.googleapis.com/auth/drive.file'],
  });
  const drive = google.drive({ version: 'v3', auth });
  const result = await drive.files.create({
    requestBody: { name: 'page-report.pdf', mimeType: 'application/pdf' },
    media: { mimeType: 'application/pdf', body: Buffer.from(pdfBytes) },
    fields: 'id,name,mimeType',
  });
  console.log(result.data);
} finally {
  await browser.close();
}

Choose a navigation condition that matches the site. networkidle0 can be unsuitable for pages with persistent network activity, while domcontentloaded may be too early if the report renders asynchronously. When necessary, wait for a specific selector that proves the content is ready before printing.

Use the PDF stream when a stream-oriented path is useful

page.createPDFStream() returns a Web ReadableStream<Uint8Array>. The Google Node.js client documents media.body as accepting a Node.js Readable stream, so do not assume the Web stream can be passed directly. In current Node runtimes, convert it with Readable.fromWeb and verify compatibility with the installed Node.js and client versions:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { Readable } from 'node:stream';

const webStream = await page.createPDFStream({ format: 'A4', printBackground: true });
const nodeStream = Readable.fromWeb(webStream);

const result = await drive.files.create({
  requestBody: { name: 'streamed-report.pdf', mimeType: 'application/pdf' },
  media: { mimeType: 'application/pdf', body: nodeStream },
  fields: 'id,name,mimeType',
});

Google’s Node.js client documents stream support in the google-api-nodejs-client repository. A stream-based call avoids first collecting the full PDF in your own buffer, but that alone is not a guarantee of zero-memory transfer: buffering behavior can depend on the client, runtime, and upload mode.

Best Value
Google Drive Reference and Cheat Sheet: The unofficial cheat sheet reference for Google Drive
  • hole punched
  • high quality card stock
  • 4 pages
  • made in USA
  • keyboard shortcuts

Choose a Drive upload mode

Drive API v3 creates files with files.create. The upload mode determines how the content and metadata are sent, not whether the PDF came from a browser or an HTTP request.

Mode Use it when What to know
Simple media The upload is small and you do not need metadata in the same request. Send content with uploadType=media; add or update metadata separately if needed.
Multipart You want metadata such as the filename and media sent together. A single request carries both metadata and content; this is the pattern shown in the examples.
Resumable Recovery from an interrupted transfer or handling a larger transfer matters. Use Drive’s resumable upload flow; do not assume a size threshold without checking current Google guidance.

See Google’s Upload file data guide for the upload modes and their request patterns. A PDF buffer or stream is only the media body; the file name, MIME type, destination folder, and other metadata belong in the create request as applicable.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Errors, reliability, and cost considerations

Handle both sides of the transfer

  • Source request: enforce a timeout, check the HTTP status, and validate that the response is the expected PDF before sending it to Drive.
  • Drive request: catch authentication, authorization, quota, and network errors; log enough context to diagnose the failure without logging secrets or document contents.
  • Cleanup: close the Puppeteer browser in a finally block. If using streams, propagate errors and ensure failed streams do not leave the request hanging.
  • Retries: retry transient network failures cautiously. Avoid blindly repeating a create request after an ambiguous timeout, since the original upload may have succeeded; reconcile with Drive metadata or retain an idempotency strategy appropriate to the application.

Whether bytes are held in memory, how long a source request takes, and whether an upload can resume depend on the document and runtime. The official references do not establish a universal performance winner or a numeric upload-size threshold, so choose based on recovery needs and validate behavior in your deployment.

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

Common failures and fixes

Symptom Likely cause Fix
The uploaded file is an HTML error page or empty. The source redirected to a login/challenge page, returned an error, or did not serve the expected PDF. Check status, final URL, content type, and body before creating the Drive file. Pass the source site’s required cookies or authorization when permitted.
The PDF link selector times out or returns nothing. The link is dynamically rendered, hidden behind an interaction, or uses a different markup pattern. Wait for the actual page state, inspect the relevant element, or capture the response triggered by the site’s download action.
Drive returns a permission or authentication error. The credential lacks the needed scope or access to the target folder/account. Review the credential type, granted scopes, and folder access for the deployment identity.
PDF formatting differs from the browser view. page.pdf() defaults to print media, or content was not ready when printing began. Use page.emulateMediaType('screen') when appropriate and wait for the required content selector before generating.
The stream upload rejects its body type. A Web ReadableStream was supplied where the Node client expects a Node Readable. Convert with Readable.fromWeb in a compatible Node runtime, or use the Uint8Array buffer path.
A retry produces duplicate files. The first Drive create may have succeeded even though the client lost the response. Check for the expected file before retrying or otherwise make the application’s retry policy reconcile ambiguous outcomes.

Or skip the browser setup

If your goal is simply a clean image of a page rather than a PDF uploaded to Drive, ScreenshotNeo is a website screenshot API and MCP server. It does not replace this PDF-to-Drive workflow, but it can skip browser setup for screenshot capture. One GET request returns a PNG, JPEG, WebP, or PDF:

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

See the ScreenshotNeo API documentation for request options. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools, and 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000. Sign up for the free plan.

Frequently Asked Questions

Does Puppeteer’s page.pdf() download a PDF from a URL?

No. It prints the current rendered page to PDF. Retrieve an existing remote PDF from its resource URL or response instead.

Can I upload the PDF without saving it to disk?

Yes. Pass the PDF bytes or a compatible upload stream as the Drive media body; the examples keep the transfer in memory or stream form rather than writing a local file.

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

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.