Gotenberg vs a hosted PDF API

Gotenberg is a well-maintained open-source Docker image (MIT license, regular v8 releases) that wraps Chromium and LibreOffice behind a multipart HTTP API. If you already run containers it is a great tool. This page is about the cases where operating it is more work than the PDFs are worth, and how to switch with minimal code changes.

Keep Gotenberg if…

A hosted API is simpler if…

Before and after

# Before: Gotenberg (multipart form, margins in inches)
curl --request POST http://localhost:3000/forms/chromium/convert/url \
  --form url=https://example.com \
  --form paperWidth=8.27 --form paperHeight=11.7 \
  --form marginTop=0.4 --form marginBottom=0.4 \
  --form marginLeft=0.4 --form marginRight=0.4 \
  --form printBackground=true \
  -o out.pdf
# After: Shotmint (query string or JSON, named paper sizes)
curl -G https://shotmint.creasework.com/v1/pdf \
  -H "X-API-Key: $SHOTMINT_KEY" \
  --data-urlencode "url=https://example.com" \
  -d format=A4 -d margin=10mm \
  -o out.pdf

Node.js: HTML (an invoice) to PDF

Gotenberg wants an index.html file part on /forms/chromium/convert/html. Here you POST the HTML string as JSON.

const html = "<h1>Invoice #42</h1><p>Total: 99.00 EUR</p>";
const r = await fetch("https://shotmint.creasework.com/v1/pdf", {
  method: "POST",
  headers: { "X-API-Key": process.env.SHOTMINT_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({ html, format: "A4", margin: "15mm" }),
});
if (!r.ok) throw new Error(await r.text());
require("fs").writeFileSync("invoice.pdf", Buffer.from(await r.arrayBuffer()));

Python: screenshot (replacing /forms/chromium/screenshot/url)

import os, requests
r = requests.get("https://shotmint.creasework.com/v1/screenshot",
    headers={"X-API-Key": os.environ["SHOTMINT_KEY"]},
    params={"url": "https://example.com", "width": 1280, "full_page": "true", "format": "webp"},
    timeout=60)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Option mapping

Gotenberg form fieldShotmint
url / index.html fileurl / html (JSON body, up to 2 MB)
paperWidth + paperHeightformat=A4 (A3, A5, Letter, Legal, Tabloid); custom sizes via CSS @page { size: … }
marginTop/Bottom/Left/Rightmargin=10mm (one value for all sides; use CSS @page margins for uneven ones)
landscape, scale, printBackgroundlandscape, scale, print_background (on by default)
emulatedMediaType=screenscreen_media=true
waitDelay=1sdelay=1000 (ms), or wait_until=networkidle
userAgentuser_agent
emulatedMediaFeatures (prefers-color-scheme)dark_mode=true
Screenshot route: width, height, format, quality, omitBackgroundwidth, height, format, quality, omit_background, plus full_page and selector
header.html / footer.html, nativePageRanges, waitForExpression, cookies, LibreOffice and merge routesNot supported. Repeat headers with a <thead> or fixed elements in your HTML

Migration gotchas

Try the output first, no key needed: HTML to PDF and URL to PDF. Related: HTML invoices to PDF, wkhtmltopdf alternative, Puppeteer alternative.

What it costs

One PDF or screenshot = one credit. €5 buys 2,000 renders, credits never expire and there's no subscription, so it beats an always-on container below a few thousand documents a month. Failed renders are refunded automatically.

Try it with 100 free renders

No signup, no card. Get a key in one click, then buy credits only if you need more (from €5 for 2,000, never expire).

Get a free API key Full API reference