DocRaptor vs PDFShift: Prince vs Chromium for PDFs

DocRaptor vs PDFShift tested: Prince against Chromium on the same invoice, layout and CSS pages, with pricing at three volumes and working code for both.

By Simon Chiu · Updated

DocRaptor and PDFShift both turn HTML into PDF over a REST call, but they use different engines. DocRaptor runs Prince, a print-first typesetting engine. PDFShift runs Chromium, the engine inside Chrome. That one fact predicts most of what you will see: Prince is strong at print features such as running headers, footnotes and page counters, and Chromium is strong at the CSS and JavaScript that web apps use today.

Pick DocRaptor when the PDF is a designed print document and your team will write print CSS for it. Pick PDFShift when the PDF is a web page or app view that already looks right in Chrome.

Disclosure: I build RenderScreenshot, which also makes PDFs from HTML. Neither DocRaptor nor PDFShift is our product. The tests below ran on 2026-10-15. The last section says where our API fits and where it does not.

This page is part of our comparison hub. For a wider list of PDF services, see the HTML to PDF API roundup.

The verdict in one table

If you need... Choose Why
Print typography: footnotes, running headers, cross-references, PDF/UA or PDF/A output DocRaptor Prince implements paged-media CSS that browsers do not
Output that matches what users see in Chrome PDFShift Same engine as the browser
CSS grid, :has(), color-mix(), CSS nesting PDFShift Prince ignored all four in our test
Excel (XLS/XLSX) files from the same API DocRaptor PDFShift makes PDF and images only
Images as well as PDFs PDFShift It has PNG, JPEG and WebP endpoints
No surprise charges for large documents DocRaptor It bills per document, at any size
Lowest entry price per document PDFShift Cheaper at every volume we priced below

What we tested and how

We could not run PDFShift without a paid account, so this is an engine test, not a service benchmark. We sent four HTML files from our test corpus through two engines:

  • Prince, through DocRaptor's API in test mode (test: true), default pipeline 10.1 (Prince 15.1).
  • Chromium 148, through Puppeteer page.pdf() on our machine. PDFShift is not Chromium 148: its X-Processor-Version header accepts Chromium 116 or 142. We use local Chromium to show how the Chromium engine treats the same CSS, not to measure PDFShift's servers.

Method: 2026-10-15, macOS on an Apple M1 Pro, Node 22.18, three runs per file and engine. DocRaptor test documents carry a watermark band. Each file has text markers that pdftotext can find, and we checked the layout by eye on a 50 dpi raster of page 1.

Test file What it checks Prince (DocRaptor) Chromium
invoice.html 60-row table over 3 A4 pages, repeated thead, @page margin box with counter(page) 3 pages, header repeats, "Page 1 of 3" printed 3 pages, header repeats, "Page 1 of 3" printed
layout.html Flexbox row, CSS grid with a spanning cell, floats Flex row correct, grid stacked into one column, floats correct All three correct
modern-css.html Ten modern CSS features 5 of 10 applied (see below) 10 of 10 applied
js.html A sync script, a script-drawn chart, and a text update after 800 ms With javascript: true: sync text and chart ran, the 800 ms update did not Sync text and chart ran, the 800 ms update did not (no wait set)

The CSS feature results for Prince were: custom properties, calc(), linear-gradient, box-shadow and transform worked. clamp(), aspect-ratio, color-mix(), :has() and CSS nesting were ignored. Neither engine waited for the 800 ms timer by default. DocRaptor waits if your page calls docraptorJavaScriptFinished(). PDFShift waits if you pass wait_for with the name of a global function that returns true.

Two rows of PDF page renders. Left column, Prince through DocRaptor test mode: the CSS grid stacks into one column and the clamp, aspect-ratio, color-mix, has and nesting tests show no styling. Right column, Chromium: grid in three columns and every CSS test styled.
Page 1 of layout.html (top) and modern-css.html (bottom). Left: Prince 15.1 via DocRaptor, test mode (the grey band is DocRaptor's test watermark). Right: Chromium 148, page.pdf({ format: 'A4', printBackground: true }). 2026-10-15.

What the numbers do not show. DocRaptor's test-mode calls took between 1.5 and 2.2 seconds from our machine. That is network plus queue plus render, from one location, for watermarked documents. We do not compare it with local Chromium times, because a local call has no network hop. Run the script at the end of this page from your own region to get a fair number.

Feature and parameter map

Both APIs take JSON. The table maps the common options. Names are checked against DocRaptor's API reference and PDFShift's convert endpoint on 2026-10-15.

Task DocRaptor PDFShift
Endpoint POST https://api.docraptor.com/docs POST https://api.pdfshift.io/v3/convert/pdf
Auth API key as the HTTP Basic username X-API-Key header
HTML input document_content source (HTML string)
URL input document_url source (URL; detected automatically)
Free test output test: true (watermarked, unlimited) sandbox: true (watermarked, 10 per minute)
Run JavaScript Off by default; javascript: true On by default; disable_javascript: true turns it off
Wait for the page docraptorJavaScriptFinished() in your page wait_for (global function), delay (max 10 s), wait_for_network
Print or screen CSS prince_options[media], default print use_print, default false (screen)
Paper size CSS @page { size: ... } format (A4, Letter, or {width}x{height})
Long jobs async: true, then poll /status/{id} or set callback_url webhook
Store the file Hosted documents (extra cost) filename (S3 link kept 2 days) or s3_destination
Other outputs XLS, XLSX PNG, JPEG, WebP endpoints

The most common DocRaptor surprise is in that table: it applies print media rules by default. If your PDF looks nothing like the page in the browser, set prince_options: { media: 'screen' } first. PDFShift starts from screen CSS, which is why a page that looks right in Chrome usually looks right in its output.

Pricing at three volumes

List prices from each vendor's pricing page, checked on 2026-10-15. Each cell is the cheapest monthly plan whose quota covers the volume.

PDFs per month DocRaptor PDFShift
1,000 $75 (Premium, 1,250 docs) $24 (Boost, 2,500 credits)
10,000 $399 (Bronze, 15,000 docs) $99 (Business, 25,000 credits)
100,000 Enterprise, contact sales (largest listed plan is 40,000 docs for $1,000) $249 (100k plan)

Two billing rules change these numbers for some workloads:

  • PDFShift counts one credit per 5 MB of output. A 14 MB PDF uses 3 credits. Image-heavy reports cost more than the table shows.
  • DocRaptor charges per document at any size and lets paid plans go over the limit at a per-document rate. Its free plan allows 5 documents a month with no overage.

Both free tiers are small: 5 documents (DocRaptor) and 50 credits (PDFShift) a month. The watermarked test modes are where you will do most of your evaluation.

Code: the same invoice through both

Both scripts are Node 22 ESM with no dependencies. Each throws on a non-2xx response, so a failed render exits with a non-zero code.

DocRaptor. We ran this against DocRaptor's live API with a test key on 2026-10-15. The page counter in @page printed "Page 1 of 1".

// DocRaptor: HTML to PDF with the Prince engine.
// test: true makes free, watermarked documents, so you can try this before you pay.
import { writeFile } from 'node:fs/promises';

const key = process.env.DOCRAPTOR_API_KEY;
if (!key) throw new Error('Set DOCRAPTOR_API_KEY');

const html = `<!doctype html><html><head><style>
  @page { size: A4; margin: 18mm;
          @bottom-right { content: "Page " counter(page) " of " counter(pages); } }
  body { font: 11pt Helvetica, Arial, sans-serif; }
</style></head><body><h1>Invoice INV-1015</h1><p>Total due: $3,141.59</p></body></html>`;

const res = await fetch('https://api.docraptor.com/docs', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Basic ' + Buffer.from(`${key}:`).toString('base64'),
  },
  body: JSON.stringify({
    test: true,            // remove for billable, unwatermarked output
    type: 'pdf',
    document_content: html,
    javascript: false,     // true runs DocRaptor's JavaScript engine before Prince
  }),
});
if (!res.ok) throw new Error(`DocRaptor ${res.status}: ${await res.text()}`);
await writeFile('invoice-docraptor.pdf', Buffer.from(await res.arrayBuffer()));
console.log('saved invoice-docraptor.pdf');

PDFShift. We do not have a PDFShift account, so we ran this against a local mock that checks the documented auth header and the required source field. It has not run against PDFShift's live API.

// PDFShift: HTML or URL to PDF with Chromium. sandbox: true makes free, watermarked documents.
import { writeFile } from 'node:fs/promises';

const key = process.env.PDFSHIFT_API_KEY;
if (!key) throw new Error('Set PDFSHIFT_API_KEY');

const html = `<!doctype html><html><head><style>
  @page { size: A4; margin: 18mm; }
  body { font: 11pt Helvetica, Arial, sans-serif; }
</style></head><body><h1>Invoice INV-1015</h1><p>Total due: $3,141.59</p></body></html>`;

const res = await fetch('https://api.pdfshift.io/v3/convert/pdf', {
  method: 'POST',
  headers: { 'X-API-Key': key, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    source: html,          // a URL works here too; PDFShift detects which one you sent
    sandbox: true,         // remove for billable, unwatermarked output
    format: 'A4',
  }),
});
if (!res.ok) throw new Error(`PDFShift ${res.status}: ${await res.text()}`);
await writeFile('invoice-pdfshift.pdf', Buffer.from(await res.arrayBuffer()));
console.log('saved invoice-pdfshift.pdf');

Both responses are the PDF bytes on success. That makes a switch between them a small change: keep your HTML, swap the request builder. The work is in the CSS. A template tuned for Prince's print features (@footnote, string-set, prince-* properties) will lose those features in Chromium. A template that uses grid or :has() will lose layout in Prince.

Run the engine test on your own templates

Do not trust our four files for your documents. This loop sends each of your HTML files to DocRaptor in test mode and prints the page count and any text markers. Put a marker string such as MARKER-END at the bottom of each template, so a missing marker tells you content was cut.

// Send every .html file in ./templates to DocRaptor test mode; report pages and markers.
import { readdir, readFile, writeFile } from 'node:fs/promises';
import { execFileSync } from 'node:child_process';

const auth = 'Basic ' + Buffer.from(`${process.env.DOCRAPTOR_API_KEY}:`).toString('base64');
for (const name of (await readdir('templates')).filter((f) => f.endsWith('.html'))) {
  const t0 = performance.now();
  const res = await fetch('https://api.docraptor.com/docs', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: auth },
    body: JSON.stringify({ test: true, type: 'pdf', document_content: await readFile(`templates/${name}`, 'utf8') }),
  });
  if (!res.ok) { console.error(name, res.status, await res.text()); process.exitCode = 1; continue; }
  const file = `out-${name.replace('.html', '.pdf')}`;
  await writeFile(file, Buffer.from(await res.arrayBuffer()));
  const pages = execFileSync('pdfinfo', [file]).toString().match(/Pages:\s+(\d+)/)[1];
  const markers = execFileSync('pdftotext', [file, '-']).toString().match(/MARKER-[A-Z-]+/g) ?? [];
  console.log(name, `${Math.round(performance.now() - t0)} ms`, `${pages} pages`, markers.join(' '));
}

We ran this loop against DocRaptor test mode with two of our corpus files on 2026-10-15. It needs pdfinfo and pdftotext from Poppler (brew install poppler or apt install poppler-utils). To do the same against PDFShift, change the URL, the auth header and the body to the PDFShift shape shown above, and keep sandbox: true.

When DocRaptor is the better choice

  • Books, reports and statements with print rules. Prince supports paged-media CSS such as footnotes, running headers and leaders that Chromium does not.
  • Accessible or archival PDFs. If a regulator asks for PDF/UA or PDF/A, a print engine built for that is the safer start.
  • Big documents. One price per document regardless of size, and an async mode that allows up to 600 seconds per job.
  • Excel output. The same API makes XLS and XLSX files.

When PDFShift is the better choice

  • Your HTML was built for browsers. Dashboards, receipts from your web app, pages with charts drawn by JavaScript.
  • Price. At each of the three volumes above, its list price is lower.
  • Compliance flags. is_hipaa and is_gdpr switch off features that would store your document on PDFShift's side.
  • You also need images. The same key converts to PNG, JPEG and WebP.

Where RenderScreenshot fits

RenderScreenshot also renders with Chromium, so its PDFs behave like the Chromium column above. It is a screenshot API first: the same request can return a PNG, WebP or PDF, and every render is cached with a public CDN link in the X-Cache-URL header. The PDF options are listed in the PDF parameters reference.

// RenderScreenshot: the same HTML to PDF through the Node SDK.
import { writeFile } from 'node:fs/promises';
import { Client, TakeOptions } from 'renderscreenshot';

const client = new Client(process.env.RS_API_KEY);
const html = '<h1>Invoice INV-1015</h1><p>Total due: $3,141.59</p>';

const pdf = await client.take(
  TakeOptions.html(html).format('pdf').pdfPaperSize('a4').pdfPrintBackground()
);
await writeFile('invoice-rs.pdf', pdf);
console.log('saved invoice-rs.pdf');

We tested this snippet against a local mock of our API, not the live service. Choose DocRaptor over us for print-typography work. Choose us if you need screenshots and PDFs from one key. If you are still choosing between two Chromium services, read PDFShift vs Api2Pdf.

To run the engine test above with RenderScreenshot as a third column, get a free API key. No credit card is needed.

Last tested 2026-10-15 with Node 22.18.0, Puppeteer 24.43.1 (Chromium 148.0.7778.97) and DocRaptor pipeline 10.1 in test mode. Prices and parameters checked on the vendors' sites on the same date.

Frequently asked questions

Does DocRaptor use Chrome?
No. DocRaptor converts HTML with the Prince engine. Its default pipeline in October 2026 is Prince 15.1. JavaScript is off unless you set javascript to true, and DocRaptor then runs its own JavaScript engine before Prince lays out the page.
Which one renders CSS grid and flexbox correctly?
In our test, Prince (through DocRaptor) laid out a flexbox row correctly but stacked a three-column CSS grid into one column. Chromium laid out both. PDFShift renders with Chromium, so modern layout CSS behaves as it does in Chrome.
Can I test both for free?
Yes. DocRaptor makes unlimited watermarked test documents when you send test set to true. PDFShift has a sandbox flag that makes watermarked documents that do not use credits, limited to 10 per minute.
Can PDFShift or DocRaptor make screenshots?
PDFShift has PNG, JPEG and WebP conversion endpoints next to its PDF endpoint. DocRaptor makes PDF and Excel files only.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.