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

If a JavaScript API call returns an empty PDF, first check whether you received PDF bytes at all. Read a successful response with response.blob() or response.arrayBuffer()—not response.text() or response.json()—and verify the status, Content-Type, and byte count before saving it. A 200 response can still contain an HTML error page or JSON, and a CORS-opaque response can produce a zero-byte Blob.

Start by checking what the API actually returned

Open your browser’s DevTools, select the Network tab, and inspect the request that fetches the PDF. Check the final response after redirects, not just the initial request. Record the HTTP status, response type, Content-Type, Content-Length if available, and the downloaded size. Also check whether a preflight request occurred and whether it succeeded.

A 200 status only means the server reported success at the HTTP level. It does not prove the response body is a PDF. If the Content-Type is application/json or text/html, the server may have returned an error object, login page, proxy error, or other content. Don’t save that response as a PDF. Fetch’s response methods and status handling are documented by MDN.

When the body is readable, inspect its first bytes as a diagnostic: a PDF normally begins with the ASCII signature %PDF. If you see {, <, or an error message instead, the response is not the expected PDF. This check can help locate a wrong response, but it is not a complete test of PDF validity.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.

Download a PDF with Fetch

For a browser download, use blob() and check the response before creating a download link. If the server returns a useful error body on failure, read that body as text before throwing.

const response = await fetch('/api/report');

if (!response.ok) {
  const message = await response.text();
  throw new Error(`HTTP ${response.status}: ${message}`);
}

const type = response.headers.get('content-type') || '';
if (!type.includes('application/pdf')) {
  throw new Error(`Expected PDF, received ${type || 'no Content-Type'}`);
}

const blob = await response.blob();
if (blob.size === 0) {
  throw new Error('PDF body is empty');
}

const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'report.pdf';
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);

The browser’s Response provides body readers for different formats, including blob() and arrayBuffer(); choose the one that fits what you need to do with the bytes. See MDN’s Response reference. A Blob is convenient for downloads and object URLs. Use an ArrayBuffer when you need to inspect bytes, pass them to a PDF parser, or send them to another binary sink.

For byte inspection, read the response once as an ArrayBuffer and examine a short prefix. Do not first call blob() and then try to read the same response again: the body is consumed by a reader.

Rank #2
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
const response = await fetch('/api/report');
if (!response.ok) throw new Error(`HTTP ${response.status}`);

const bytes = await response.arrayBuffer();
const prefix = new TextDecoder().decode(bytes.slice(0, 8));
console.log({ byteLength: bytes.byteLength, prefix });

Set Axios to receive binary data

Axios needs an explicit binary response type. In a browser, use blob; when you need raw bytes in Node.js or for byte-level processing, use arraybuffer. Without the right response type, the returned data may not be handled as the binary body you expect. An Axios issue documents the symptom of response.data appearing as an empty object; treat it as an example of a failure report, not as a current specification for Axios behavior: Axios issue #1392.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const { data, headers, status } = await axios.get('/api/report', {
  responseType: 'blob',
  headers: { Accept: 'application/pdf' }
});

if (status < 200 || status >= 300) {
  throw new Error(`HTTP ${status}`);
}
if (!data || data.size === 0) {
  throw new Error('Empty PDF body');
}

const type = data.type || headers['content-type'] || '';
if (type && !type.includes('application/pdf')) {
  throw new Error(`Expected PDF, received ${type}`);
}

const blob = data.type ? data : new Blob([data], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'report.pdf';
link.click();
URL.revokeObjectURL(url);

For Node.js or other byte-oriented work, use responseType: 'arraybuffer', then pass the bytes to the destination without converting them to text. An HTTP status and a nonzero size are useful checks, but still confirm the response type and, when debugging, inspect a byte prefix.

Rule out an opaque CORS response

A cross-origin request can fail in a way that looks like an empty PDF if the browser gives JavaScript an opaque response. MDN states that an opaque response has status 0, inaccessible headers, and a null body; converting it to a Blob produces size 0 and an empty type. That Blob is unusable for URL.createObjectURL(). See MDN’s Blob method reference.

Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

Check the Network tab and console for CORS errors, then verify that the API permits the requesting origin. If the request uses credentials, its mode and credentials must also agree with the server’s CORS policy. A cross-origin response may also hide headers from JavaScript unless the server exposes them; compare what DevTools shows with what response.headers.get() returns.

To separate a CORS problem from a PDF-generation problem, request the endpoint directly where appropriate or make the request through a same-origin backend proxy. Do not use mode: 'no-cors' as a fix: it produces an opaque response that JavaScript cannot read as a usable PDF.

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.

Forward the bytes correctly from your server

If a backend or serverless route sits between the PDF provider and the browser, it must forward the actual bytes unchanged. Read the upstream body as an ArrayBuffer, convert it to a Node.js Buffer, and send it as bytes. Do not pass PDF data through res.json() or JSON.stringify().

Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
const upstream = await fetch(PDF_URL, options);

if (!upstream.ok) {
  const error = await upstream.json();
  return res.status(upstream.status).json(error);
}

const bytes = await upstream.arrayBuffer();
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename=document.pdf');
res.send(Buffer.from(bytes));

Use Content-Type: application/pdf to identify the body as a PDF. Content-Disposition: attachment; filename=... suggests download behavior and a filename; it does not turn a non-PDF body into a PDF. Preserve the upstream error status and body while diagnosing failures so the client can distinguish a generation error from a download-handling error.

Official examples from RenderPDF show checking upstream success, reading bytes with arrayBuffer(), and returning the PDF with these headers. They also warn: “Never expose API keys in client-side code. Use a backend proxy to make API calls.”

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

Choose between a direct browser call and a backend proxy

Approach What to consider
Direct browser request Requires a server CORS policy that permits the browser origin. Any API key embedded in client-side code is exposed to users.
Backend proxy Keeps API credentials on the server and gives you a place to log status, validate headers and bytes, and control how errors reach the client. The proxy must preserve the PDF bytes and set appropriate response headers.

Fetch and Axios can both work for binary responses. The practical differences are the body reader or Axios responseType, whether code runs in a browser or Node.js, what headers CORS exposes, how error bodies are handled, and who revokes a created object URL. Pick based on the runtime and the surrounding API flow, not on the assumption that one library makes invalid server responses valid.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.

Common empty-PDF symptoms and fixes

  • Status 200, but the file is blank or unreadable: Check Content-Type and the first bytes. The endpoint may have returned HTML or JSON instead of a PDF.
  • Blob size is zero and type is empty: Check for an opaque CORS response, a zero-byte server response, or a failed upstream request. For an opaque response, fix the server’s CORS policy or route through a same-origin backend.
  • Axios shows an empty object: Set responseType: 'blob' in a browser or responseType: 'arraybuffer' when handling raw bytes.
  • The API returns JSON on errors: Check response.ok before reading a successful response as a PDF. Read or preserve the error body separately rather than naming it with a .pdf extension.
  • The browser reports a CORS error or status 0: Confirm allowed origins, credentials, and preflight behavior. A browser CORS failure cannot be repaired by trying to parse its opaque body.
  • The proxy returns a damaged file: Ensure it forwards the ArrayBuffer or Buffer directly, sets application/pdf, and does not stringify or JSON-encode the bytes.
  • Content-Type is unavailable in JavaScript: Check whether the response is cross-origin and whether the server exposes the needed headers. DevTools may show details that the page’s JavaScript cannot access.

Or skip the browser setup

If your goal is to capture a webpage as a PDF, ScreenshotNeo can return a PDF from one API request. The JavaScript examples above address fetching and forwarding an existing PDF; ScreenshotNeo is an alternative when the PDF should be rendered from a URL.

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for free.

FAQ

Should I use blob() or arrayBuffer()?

Use blob() for a browser download or object URL. Use arrayBuffer() when you need to inspect or pass around raw bytes, including in a server-side proxy.

Does a 200 response mean the PDF is valid?

No. It means the HTTP request succeeded according to the server’s status code. Verify the response body type and size, and inspect bytes when troubleshooting.

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.