HTML · File · URL → PDF

Your HTML.
Print-ready PDF.
One request.

Send HTML, upload a file, or point us at a URL — get back a PDF that looks exactly like the page. Every font, every language, every page break where you put it. Built for invoices that have to be right the first time, at 4am, on the hundred-thousandth one.

HTML, .html/.htm/.zip upload, or a URL 300 renders free No credit card Vietnamese, Japanese, Chinese, emoji
POST /api/v1/html-to-pdf no key needed
Either one — the editor below already has something to render.
Drop it here — .html, .htm, or a .zip of the folder
A4, 15mm margins · 25 renders, no card

Four ways in. One result.

Whichever one fits the code you already have — or no code at all. Same options, same output.

An HTML string

Template it in your own code and post it as JSON. The usual choice when the document is generated per request — an invoice, a report, a ticket.

"source": { "html": "<h1>Invoice</h1>" }

An uploaded file

Post the file itself as multipart — .html or .htm. No escaping a 400 KB template into a JSON string, and no shell quoting to get wrong.

A page that links to css/style.css, images or fonts needs them too: send a .zip of the folder and every relative path still resolves. index.html is the entry point, otherwise the shallowest page. Or send the files with a paths array, which renders identically. Drop one into the box at the top of this page.

curl -F "file=@invoice.html" \
     -F 'options={"format":"A4"}'

# a folder, with its css/ and fonts/
curl -F "file=@invoice.zip"

A live URL

Point us at a page and we load it the way a browser would, so its own stylesheets, images and fonts resolve. For pages behind a bot wall, add via_crawler and we fetch it through our crawling infrastructure — proxies, stealth browser, challenges solved.

"source": { "url": "https://…" },
"options": { "via_crawler": true }

No markup at all

Design the document by dragging boxes onto the page — text, images, tables, barcodes — then render it with your data. Every box you name becomes a field. Open the editor →

POST /templates/<id>/render
"data": { "customer": "ACME GmbH" }

The four things that ruin a PDF

Each one is a support ticket you will get from somebody else's API. We fixed them before you hit them.

Every language, right the first time

Vietnamese, Chinese, Japanese, Korean, Thai, Arabic, Cyrillic and colour emoji come out as themselves — never tofu boxes, never the wrong accent. Send an invoice to any market without checking the output by hand.

Nothing prints half-drawn

Charts, web fonts and lazy images finish before the page is captured. You say what "ready" means — an element, a JS condition, network idle, or a delay — instead of hoping the renderer guessed right.

The page is the size you asked for

Ask for A5 landscape and you get A5 landscape — the dimensions, not just the orientation. Headers, footers, page numbers, margins and page ranges all behave the way print people expect.

Your HTML can't phone home

Sub-resources cannot reach loopback, private or cloud-metadata addresses. If your users author the templates, that matters more than it sounds.

Why you can trust the output

We would rather show you what is tested than tell you who else is buying.

19 documents that break renderers

The test suite renders a 400-row table, a 20-column spread, right-to-left text, shadow DOM, CSS filters, web fonts and forced page breaks — then compares the result against a second independent engine and fails if a single character moved.

E-invoices checked by the real validators

ZUGFeRD and Factur-X output is checked with the official EN 16931 validators — Mustangproject and KOSIT, the same tools your buyer's tax software uses — not against our own idea of the spec.

The SSRF test counts real requests

A test stands up an actual listener on a private address, points caller HTML at it, and fails if even one request arrives — plus a control request that must arrive, so an empty result can never come from a broken test.

It runs our own business

The same renderer produces the invoices that live Shopify stores send their customers, with real money and real tax authorities on the other end. If it breaks, it breaks for us first.

And when we cannot do something you asked for, you get an error that names the option — never a PDF that quietly came back without it. Failed renders are never billed.

Priced per render

Not per megabyte, not per feature. Every plan gets every option.

Test your own HTML before you buy300 free renders, no card and no signup.
Try API free →
Free

$0

First 300 renders

Test your own HTML. No signup, no card.

Try API free →
Basic

$5/mo

2,500 renders

$0.0020 each, plus 14,167 conversion pages.

3-day free trial — cancel before it ends and you pay nothing.

Start free trial →
Starter

$9.99/mo

7,500 renders

$0.0013 each, plus 33,333 conversion pages.

3-day free trial — cancel before it ends and you pay nothing.

Start free trial →
MOST POPULAR
Professional

$29.99/mo

30,000 renders

$0.0010 each, plus 125,000 conversion pages.

3-day free trial — cancel before it ends and you pay nothing.

Start free trial →
Business

$99.99/mo

150,000 renders

$0.0007 each, plus 666,667 conversion pages.

3-day free trial — cancel before it ends and you pay nothing.

Start free trial →

One subscription, not two. The same plan covers the HTML-to-PDF API and the document conversion and OCR tools. Renders and pages have separate allowances, so a busy month on the API never eats the quota you bought for conversions.

Volume / monthSayPDF PDFShiftDocRaptorApi2Pdf
2,500 renders$5$24$149$12.50
7,500 renders$9.99$99$399$37.50
30,000 renders$29.99$149$1,000$150
150,000 renders$99.99$499Quoted$750
Per render, at 150k$0.0007$0.0020—$0.005

Competitor prices are their own published list prices, re-checked 1 October 2026, taking the smallest tier that actually covers that volume — you cannot buy 7,500 renders from PDFShift, so that row is their 25,000 tier at $99. Api2Pdf is its flat $0.005 per PDF. DocRaptor's published ladder ends at 40,000 documents for $1,000, so 150,000 is quoted rather than listed and we will not invent a number for it. Please check them yourself before you decide; we would.

Plans renew monthly. One render is one call, whatever the page count and whatever finishing it asks for. OCR and scanned-document conversions are billed separately — see full pricing.

How it compares

 SayPDF PDFShiftDocRaptor
Renders like a browserYesYesOwn engine
Rendering options473750
CJK, Vietnamese, Arabic, Thai, Korean — fonts embeddedMeasured yesNot testedMeasured yes
Newer emoji, U+1F9FE 🧾RendersNot testedEmpty box
ZUGFeRD / Factur-X e-invoiceYesNoNo
PDF/A & PDF/UAYesNoAdd-on
Password protectionYesYesYes
30,000 renders / month$29.99$149$1,000
Free tierFirst 300, one-off50 credits / month5 docs / month
An unwatermarked PDF before you sign upYes, aboveAccount firstWatermarked tests

Prices and free tiers as published on their own sites, re-checked 1 October 2026. Option counts are counted, not quoted: PDFShift's from their OpenAPI document, DocRaptor's from their API reference, ours from the parameter map in our own source. The font rows are a head-to-head render of one HTML document through both APIs on 1 October 2026, compared with pdffonts and pdftotext; PDFShift is marked not tested because its free tier needs an account. Check them before you decide — we would, and the full comparison across nine other vendors says where each one wins.

When this is the wrong choice

Four cases where we would tell you to buy something else. If none of them is you, the box at the top of this page settles it in about ten seconds.

You need print-publishing typography

Running headers, footnotes, cross-references, precise widow and orphan control — CSS paged media as the standard actually specifies it. That is DocRaptor and PrinceXML, and it is genuinely better at it than Chromium. We render like a browser, which is the right engine for invoices and reports and the wrong one for a book.

You need a vendor your procurement team recognises

If the purchase needs a signed DPA, a SOC 2 report, a security questionnaire returned in a week and a support contract with named hours, buy from a company with a compliance department. SayPDF is small. That is why it is cheap, and it is a real reason not to choose it.

You have the ops capacity to self-host

The renderer here is Gotenberg, which is open source and free. If you already run containers and would rather own the uptime than pay for it, run it yourself — you will not get a better deal from us than $0. You are paying here for the queue, the fonts, the font fallbacks, the retries and nobody being woken up.

Your HTML is not finished

No API renders a page whose layout is wrong; it renders the wrong layout very accurately. If the thing that keeps breaking is the document itself, the useful tool is a browser's print preview and an afternoon, not a subscription.

Still comparing? The ten-vendor table has each one's cheapest paid plan, free tier, billing unit and whether you can render before signing up, with the date each figure was checked.

Boring, on purpose

A document API is infrastructure. It should be the least interesting thing in your stack.

Fast, and steady

A typical invoice comes back in well under a second, and the tenth thousand takes as long as the first. The renderer is kept warm rather than started per request.

It tells you the truth

An option we cannot apply is a clear error naming that option — never a PDF that quietly came back missing what you asked for. Failed renders cost you nothing.

Two engines, one output

If the primary renderer is unavailable, a second one takes over automatically. Same engine family, same result — you get a PDF, not an incident.

Archive-grade output

PDF/A and PDF/UA when you need to file it or make it accessible, password protection and watermarks when you need to control it.

Everything print needs, on one endpoint

Forty-seven options, all documented, all included on every plan. Nobody hides page numbers behind an upgrade.

format A0–A6, Letter, auto
width / height in·cm·mm·px
margin string or per side
landscape
scale 0.1 – 2
prefer_css_page_size
pages "1-5", "1,3,8"
single_page
zoom
print_background
omit_background
use_print print | screen
css inject a stylesheet
javascript inject a script
disable_javascript
header / footer HTML
delay ms
wait_for expression
wait_for_selector
wait_for_network
lazy_load_images
timeout
user_agent
http_headers
cookies
auth basic
raise_for_status
fail_on_console
fail_on_resource
tagged accessible PDF
outline bookmarks
pdfa 1b / 2b / 3b · soon
pdfua · soon
metadata
paged Paged.js for real page breaks
print_css_defaults repeat table headers, avoid splits
fail_on_resource_status refuse on 4xx/5xx sub-resources
via_crawler for pages behind a bot wall
saved_page a page a browser saved to disk
engine gotenberg or puppeteer
protection passwords · soon
watermark · soon
flatten · soon
optimize · soon
facturx ZUGFeRD · soon

See every option in the docs →

EU e-invoicing · live

A German or French e-invoice, from the same call

Pass your CII XML and get back a PDF/A-3 with the invoice attached, the right AFRelationship and the Factur-X XMP extension schema — a file a German buyer's system will accept, not a PDF with an XML zipped next to it.

Profiles: MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED, XRECHNUNG. Germany's mandate starts 1 January 2027.

Live on every plan, including the free one. E-invoice embedding, PDF/A, PDF/UA, password protection and watermarking need no request and no add-on — pass the option and you get the file. If the PDF engine is ever down the API answers 503 and names the option, rather than returning a PDF that quietly lacks it.

E-invoicing API docs
ZUGFeRD / Factur-X
{
  "source": { "html": "<h1>Rechnung RE-2026-1001</h1>" },
  "options": {
    "facturx": {
      "xml": "<rsm:CrossIndustryInvoice>…",
      "conformance": "EN 16931"
    }
  }
}

→ PDF/A-3 · factur-x.xml embedded · XMP written

Questions people actually ask

Can I send a URL instead of HTML?

Yes — source: { "url": "https://…" }. The page loads from its own origin, so relative stylesheets, images and links resolve the way they do in a browser. The URL has to be publicly reachable; private and loopback addresses are refused.

My page draws a chart with JavaScript. Will it be in the PDF?

If you tell us to wait for it, yes. Use wait_for_selector, wait_for with a JS expression, or a plain delay. We do not guess: an engine that silently waits "long enough" is one that silently stops waiting long enough.

Do you store my HTML or the PDF?

The PDF is returned in the response body when you ask for it inline or as base64 — nothing is kept. Hosted output (output: "url") is stored so the link works, and is deleted on the retention schedule in our privacy policy.

What happens to page numbers and headers?

Nothing, unless you ask. There is no header or footer by default. Supply header / footer HTML and use the pageNumber, totalPages, date, title and url classes.

Is a 400-row invoice table going to break?

It paginates, repeats the <thead> on every sheet and does not slice a row in half. That behaviour is on by default and can be turned off with print_css_defaults: false if you would rather own the print CSS yourself.

What exactly is "the first 300 renders free"?

A one-off grant on your account, not a monthly reset. Every page you render counts against it until it runs out, then a plan takes over. The response tells you where you stand: freePagesRemaining in the JSON body, or the X-Free-Pages-Remaining header on a binary response. Failed renders are not counted — if we could not produce the PDF, it does not come out of your grant.

How fast is it?

Chromium is kept warm, so a typical invoice comes back in a few hundred milliseconds rather than paying a browser launch on every call. Heavy documents, PDF/A conversion and e-invoice embedding take longer — they are doing more work.

Start rendering in 30 seconds

Your first 300 renders are on us — no credit card, no sales call. One POST and you have a PDF.