webcapture Get a free key

Render dark-mode PDFs in TypeScript with fetch

One field flips the render's color scheme: colorScheme: dark emulates a visitor with prefers-color-scheme: dark before the page paints.

colorScheme takes light, dark, or site, and light is the default. dark emulates the media query before navigation, so the page's own dark stylesheet wins on the first paint instead of flashing light; site leaves the decision to the page. It moves only pages that answer prefers-color-scheme, so a page with no dark stylesheet renders identically in both.

The recipe

import { writeFile } from "node:fs/promises";

const ORIGIN = "https://webcapture.io";

interface Receipt {
  id: string;
  bytes: number;
  attempts: number;
  remaining: number;
  sourceUrl: string;
  sourceType: "url" | "html";
  download: string;
}

const res = await fetch(`${ORIGIN}/v1/convert`, {
  method: "POST",
  headers: {
    "Authorization": "Bearer wc_your_key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"url": "https://example.com/", "colorScheme": "dark"}),
});
const receipt = (await res.json()) as Receipt;

// receipt.download: ${ORIGIN}/v1/conversions/cnv_9f2c01ab.pdf
const file = await fetch(receipt.download, {
  headers: { "Authorization": "Bearer wc_your_key" },
});
await writeFile("page-dark.pdf", Buffer.from(await file.arrayBuffer()));

The choice is per call, so the same url can render both schemes in two calls and ship whichever the user's context needs. Dark mode is where pdf renders most often betray a site: a browser screenshot inherits the visitor's scheme, while an API render needs the field spelled out, which is exactly what this body does.

Same-key GET: 401 without it; 404 for another key.

The fields this recipe uses

Related

Method

Source: convert field reference, checked 2026-10-05. Corrections.