View all our guides

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.

The steps to one PDF on PDFMonkey and on PDFShiftTwo rows of steps. PDFMonkey, four steps: create a document from a template id and a payload, the document waits with the status pending, your app polls or waits for a webhook, then it downloads the file from a link that's valid for 1 hour. PDFShift, two steps: POST the HTML, and the PDF comes back in the response.PDFMonkeyCreatedocumentStatus:pendingPoll orwebhookDownloadlink(1 hour)PDFShiftPOSTthe HTMLPDF in theresponse
PDFMonkey's document flow as its API docs describe it, above one PDFShift request.

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.

The invoice price column without and with the with_delimiter filterTwo boxes. Without the filter, liquidjs printed Spider silk 1482, Spandex fabric 57.8, Web fluid 50. With the filter registered, it printed 1,482.00, 57.80 and 50.00.Without the filterWith the filterSpider silk1482Spider silk1,482.00Spandex fabric57.8Spandex fabric57.80Web fluid50Web fluid50.00
The price column of PDFMonkey's sample invoice, rendered with liquidjs on November 11, 2026, before and after registering 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

PDFShiftPDFMonkey
What you sendHTML or a URL, in sourceA template id and a JSON payload. There's no raw HTML endpoint: your own HTML goes through a pass-through template
Where templates liveIn your repo, next to the code that fills themIn your PDFMonkey account
WebhooksOptional: webhook sends the result to your URLSent through Svix, for success, failure and a quota warning. They leave out your payload, so you pass what you need in meta
Stored documentsNot stored by default. With filename (which gives you a URL) or webhook, kept 2 days on S31 day on Free and Starter, 1 week on Pro, no limit on Pro+ and Premium. Share links on Pro+ and Premium
Free plan50 credits a month, no card20 documents a month, with no external images, fonts, CSS or JS. A 30-day trial gives 300 documents, no card
Entry planStarter, $9 a month for 500 creditsStarter, €5 a month for 300 documents
Next plans up$24 for 2,500 credits, $39 for 5,000Pro, €15 for 3,000 documents; Pro+, €60 for 5,000
Past the quotaOverage per extra credit, from $0.04 on Starter down to $0.001 on the 1,000,000-credit planA "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 render30 s on the free plan; 100 s on paid plans, raisable to 15 minutes30 s on Free and Starter, 2 min on Pro, 3 min on Pro+, 5 min on Premium
HostingOVH in France (Gravelines and Strasbourg); stored PDFs on AWS S3 in ParisFly.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.