PDFMonkey alternative: move Liquid templates to one API call
Updated by Cyril Nicodème
A PDFMonkey code template is HTML and SCSS with Liquid on top, and Liquid itself isn't PDFMonkey's. So moving to a PDFMonkey alternative that takes HTML is a smaller job than it looks: render the Liquid in your own code and send the result. What breaks on the way is the dozen filters PDFMonkey added to Liquid. We moved the invoice from PDFMonkey's own Liquid docs with one script and one registered filter, and PDFShift sent the PDF back in the response to one POST.
Even PDFMonkey's sync endpoint, POST /api/v1/documents/sync, answers with the document and its download_url, so the file is still a second download. It waits up to 6 minutes, and for production its docs point you to async generation with webhooks.
Move a PDFMonkey template into your own code
We took the invoice from "A taste of Liquid" in PDFMonkey's Liquid docs: a loop over line items, times, date, an if, and with_delimiter on the price column. We saved it as template.liquid, put three line items in payload.json (the JSON you'd send PDFMonkey as payload), ran npm i liquidjs, and then this:
import { Liquid } from 'liquidjs';
import fs from 'node:fs';
const engine = new Liquid();
const template = fs.readFileSync('template.liquid', 'utf8');
const payload = JSON.parse(fs.readFileSync('payload.json', 'utf8'));
const html = await engine.parseAndRender(template, payload);
const response = await fetch('https://api.pdfshift.io/v3/convert/pdf', {
method: 'POST',
headers: {
'X-API-Key': process.env.PDFSHIFT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ source: html }),
});
if (!response.ok) {
throw new Error(`PDFShift returned ${response.status}: ${await response.text()}`);
}
fs.writeFileSync('invoice.pdf', Buffer.from(await response.arrayBuffer()));
The PDF came back in 1.6 s: one A4 page with the invoice number, the date, the client's name and the three line items. We ran it without an API key (delete the X-API-Key line), so the page carries a "Created via PDFShift" watermark.
The price column was wrong, though, and nothing said so.
PDFMonkey's own filters
PDFMonkey's docs say its added filters "are exclusive to PDFMonkey and are not available in standard Liquid implementations". liquidjs doesn't stop on a filter it doesn't know. It printed the raw number: 1482 where the invoice needs 1,482.00, and 57.8 for 57.80. On an invoice, that's a wrong money column with no error in your logs. One registered filter, added after new Liquid(), fixed it:
engine.registerFilter('with_delimiter', (v) => Number(v).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }));
It prints 1,482.00, 57.80 and 50.00. It ignores the precision: argument and always gives two decimals with a comma for thousands, which is what this invoice uses. PDFMonkey's docs name eleven more: push, slice_by, sum, to_sentence, where_exp, in_time_zone, ensure_protocol, format, entities, json and parse_json. This invoice only needed with_delimiter. Search your templates for each name, check what it renders, and register what's missing.
with_delimiter.Liquid
liquidjs rendered the loop, the variables, times, date and the if with no change to the template. PDFMonkey's snippets and partials become plain files in your repo that your templates include.
SCSS
A PDFMonkey template's styles can be SCSS. Variables and mixins aren't CSS, so compile it with Sass first and put the CSS in the template's <style> tag.
Template settings
A PDFMonkey template keeps its paper size, margins, header and footer as settings. On PDFShift, they're fields in the request: format for the paper size, margin, and header and footer objects, each with its own source. Inside those, {{page}} prints the page number and {{total}} the page count.
Get a free API key and run one of your own templates through the script.
PDFShift vs PDFMonkey
| PDFShift | PDFMonkey | |
|---|---|---|
| What you send | HTML or a URL, in source | A template id and a JSON payload. There's no raw HTML endpoint: your own HTML goes through a pass-through template |
| Where templates live | In your repo, next to the code that fills them | In your PDFMonkey account |
| Webhooks | Optional: webhook sends the result to your URL | Sent through Svix, for success, failure and a quota warning. They leave out your payload, so you pass what you need in meta |
| Stored documents | Not stored by default. With filename (which gives you a URL) or webhook, kept 2 days on S3 | 1 day on Free and Starter, 1 week on Pro, no limit on Pro+ and Premium. Share links on Pro+ and Premium |
| Free plan | 50 credits a month, no card | 20 documents a month, with no external images, fonts, CSS or JS. A 30-day trial gives 300 documents, no card |
| Entry plan | Starter, $9 a month for 500 credits | Starter, €5 a month for 300 documents |
| Next plans up | $24 for 2,500 credits, $39 for 5,000 | Pro, €15 for 3,000 documents; Pro+, €60 for 5,000 |
| Past the quota | Overage per extra credit, from $0.04 on Starter down to $0.001 on the 1,000,000-credit plan | A "quota exceeded" error until the quota resets, unless you turn on pay-as-you-go (€0.008 to €0.005 a document, not on Free) or buy Boost Packs (1,000 documents for €5) |
| Longest render | 30 s on the free plan; up to 15 minutes upon request on paid plans | 30 s on Free and Starter, 2 min on Pro, 3 min on Pro+, 5 min on Premium |
| Hosting | OVH in France (Gravelines and Strasbourg); stored PDFs on AWS S3 in Paris | Fly.io and AWS in Paris; database on Neon in Frankfurt |
Prices are monthly, each in its own currency, and PDFMonkey's exclude VAT. One PDFShift credit covers a document up to 5 MB. Every plan we sell is on our pricing page.
Where else PDFMonkey users go
The other PDFMonkey alternatives sort by why you're leaving:
- You want to send your own HTML, as this page does: PDFShift, DocRaptor (on the Prince engine), Doppio, Api2Pdf, PDFBolt and CustomJS.
- You want another template editor: APITemplate, CraftMyPDF, PDF Generator API, PDFNoodle, DocSpring, Documint, Anvil and PDFgen. For Word, Google Docs or spreadsheet templates, Carbone, Docmosis and ActiveMerge.
- You want to run the renderer yourself: Gotenberg, Puppeteer or Playwright, WeasyPrint, or wkhtmltopdf, whose repo has been archived and read-only since January 2023. Or use a browser someone else hosts: Browserless.
If you need to edit PDFs or pull data out of them rather than make them, that's a PDF toolkit: Nutrient, PDF.co, Foxit or Adobe.
Frequently asked questions
Can I use PDFShift from Zapier, Make or n8n?
Yes. n8n has a verified PDFShift community node. In Make, you call our API with its HTTP module, and in Zapier, with webhooks. Each one sends the same POST as the script on this page.
How do I preview a template without PDFMonkey's editor?
Run the script with your sample payload, and write the HTML to a file instead of sending it. Paste that HTML into our free HTML viewer to check it before you spend a credit.
What happens to my PDFMonkey Builder templates?
Builder templates are built on Vue, not Liquid, so the port on this page doesn't apply to them. To send one to PDFShift, you'd write it again as HTML and CSS.
Does PDFShift render with Chrome, like PDFMonkey?
Yes. PDFShift renders with Chromium. PDFMonkey renders in a Chrome-based browser, and its current engine, v5, is based on Chrome 133. In our test, the PDFMonkey invoice's table borders and Helvetica font came out as written.
Moving a PDFMonkey template and something doesn't come out right?
Drop us a line.