A PDF REST API turns data or HTML into a finished PDF over a single HTTP request: you POST the content, the service renders the document on its servers, and the response gives you the file or a download link. Many also process existing PDFs the same way, with endpoints to merge, split, compress or OCR a file you send. You skip running headless Chrome, managing fonts or patching PDF libraries yourself.
This guide explains the three ways to build a PDF (two that hosted APIs use, and one you run yourself), shows a real request in curl, Node and Python, covers page breaks, webhooks and retries, and compares entry pricing across ten vendors, checked on their own pricing and product pages on October 17, 2026.
Key takeaways
- Hosted PDF APIs mostly render HTML/CSS with headless Chrome or a print-CSS engine, or fill a template with JSON data; the alternative is running a low-level PDF library yourself.
- HTML/CSS gives the most design control for the effort; templates with JSON suit invoices, quotes and reports built from data.
- Use async jobs and webhooks for large documents, and an idempotency key so retries don't create duplicates.
- Pricing units differ (credits, calls, compute-seconds), so compare cost at your real monthly volume.
- For URL-to-PDF, check that the provider blocks requests to internal network addresses (SSRF).
What is a PDF REST API?
A PDF REST API is a web service that creates or processes PDF files through standard HTTP endpoints, usually authenticated with an API key or token in a request header. Because it's plain HTTP, any programming language or no-code tool that can make a request can use one, from a Node backend to a Zapier step.
"PDF generation API", "PDF generator API" and "REST API for PDF generation" usually mean the same thing: an API focused on creating new documents such as invoices, receipts, reports, certificates and contracts.
How does a PDF REST API work?
A PDF REST API works in four stages: your app sends HTML, a URL or template data with an API key; the service merges any data into the template; a rendering engine lays out the pages; and the API returns the PDF bytes, a signed download URL, or a job ID for long renders.
- Request: your app POSTs HTML (or a template ID with JSON data, or a URL) and options like page size and margins, authenticated with an API key.
- Templating: if you sent data, the service merges it into the template: names, line items, totals.
- Rendering: a rendering engine, often a headless Chromium browser or a print-CSS engine such as WeasyPrint, lays out the pages and writes the PDF.
- Response: the API returns the PDF directly as binary, or stores it and returns a signed URL. Long jobs return a job ID and notify you by webhook when the file is ready.
Which approach should you use: HTML, template + JSON, or a PDF library?
Use HTML/CSS when you want full design control and already have a web template; use a template filled with JSON when non-developers need to edit layouts or the documents are built from records; use a low-level PDF library only when you need precise control and are willing to run it yourself.
| HTML/CSS (headless Chrome or a print-CSS engine) | Template + JSON data | Low-level PDF library | |
|---|---|---|---|
| How it works | A browser or print-CSS engine lays out your HTML and writes the PDF | Visual or HTML template with placeholders, filled per request | Code draws text, lines and images at coordinates |
| Examples | Puppeteer, Playwright, hosted HTML-to-PDF APIs | CraftMyPDF, APITemplate.io, PDFMonkey, DOCX-template engines | pdf-lib, PDFKit, iText, ReportLab |
| Design control | High: anything CSS can do | Medium to high, limited by the template engine | Total, but slow to build |
| Who edits layouts | Developers | Developers or trained non-developers | Developers only |
| Dynamic tables and page breaks | Good with print CSS | Good if the engine supports loops | Manual |
| Speed per document | Slower: a browser or layout engine lays out every page | Similar to HTML, since many engines render HTML underneath | Fastest |
| Best for | Reports, branded docs, pages you already have | Invoices, quotes, certificates from data | Very high volume, fixed layouts, PDF editing |
If you run the HTML approach yourself, both Puppeteer and Playwright expose a page.pdf() call (Playwright's works only in Chromium) that prints the page with print CSS (Puppeteer docs, Playwright docs). A hosted API saves you from keeping a browser fleet patched, scaled and supplied with fonts.
What does a PDF API request look like?
A request is a POST with a JSON body and an API key header; the response either streams the PDF or returns JSON with a link to it. Some APIs take page size and margins as options; the ASHDOCS v1 endpoint reads them from print CSS in the HTML.
Here's a request to the ASHDOCS HTML to PDF API, with a US Letter page set by @page. Leave @page out and the page defaults to A4. All three samples read your key from the ASHDOCS_API_KEY environment variable, so set it to your test key first (get a free test key; test keys never use credits).
curl
curl -X POST https://www.ashdocs.com/api/v1/tools/html-to-pdf \
-H "X-API-Key: $ASHDOCS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042" \
-d '{
"html": "<style>@page { size: Letter; margin: 18mm }</style><h1>Invoice INV-2026-0042</h1><p>Amount due: $175.00</p>"
}'
Node.js (20+, save as pdf.mjs)
const res = await fetch("https://www.ashdocs.com/api/v1/tools/html-to-pdf", {
method: "POST",
headers: {
"X-API-Key": process.env.ASHDOCS_API_KEY,
"Content-Type": "application/json",
// Use a unique value per document, such as your order ID.
"Idempotency-Key": "order-1042",
},
body: JSON.stringify({
html: "<style>@page { size: Letter; margin: 18mm }</style><h1>Invoice INV-2026-0042</h1><p>Amount due: $175.00</p>",
}),
});
if (!res.ok) throw new Error(`PDF request failed: ${res.status}`);
const job = await res.json();
console.log(job.output_files[0].url);
Python (3.8+, pip install requests)
import os, requests
res = requests.post(
"https://www.ashdocs.com/api/v1/tools/html-to-pdf",
headers={
"X-API-Key": os.environ["ASHDOCS_API_KEY"],
# Use a unique value per document, such as your order ID.
"Idempotency-Key": "order-1042",
},
json={
"html": "<style>@page { size: Letter; margin: 18mm }</style>"
"<h1>Invoice INV-2026-0042</h1><p>Amount due: $175.00</p>",
},
timeout=60,
)
res.raise_for_status()
print(res.json()["output_files"][0]["url"])
Response (trimmed; the full envelope also includes request_id, tool_key and is_test)
{
"job_id": "job_01H...",
"status": "completed",
"credits_consumed": 0,
"output_files": [
{
"filename": "output.pdf",
"size": 4231,
"url": "https://storage.ashdocs.com/...signed",
"expires_at": "2026-10-17T13:00:00+00:00"
}
]
}
The url is a signed link that expires after one hour, so download or copy the file to your own storage if you need it later. credits_consumed is 0 because test keys never use credits; a live render costs one credit. The HTML-to-PDF reference lists every request field and the template error codes.
The samples reuse Idempotency-Key: order-1042, so a repeat within 24 hours returns the first response without rendering again. Change the key when you change the HTML, or when you rerun a sample after an hour: the replayed response may carry the first signed link, which will have expired.
Binary or URL? Some APIs stream the PDF bytes in the response body (Content-Type: application/pdf); others return JSON with a URL, as above. Binary is simpler for one-off downloads in code. A URL is easier in no-code tools and for files you'll pass to another step, such as an email or a Google Drive upload.
Want to run this request yourself? Get a free test key. Test keys never use credits.
How do you fill a PDF template with data?
Send the template with placeholders and a data object; the API replaces each placeholder and repeats rows for lists. ASHDOCS uses sandboxed Jinja2 syntax: {{ variable }} for values, {% for %} for repeating rows and {% if %} for optional sections. This is the same v1 endpoint as above.
{
"html": "<h1>Invoice {{ number }}</h1><table>{% for item in items %}<tr><td>{{ item.name }}</td><td>{{ item.amount }}</td></tr>{% endfor %}</table><p>Total: {{ total }}</p>",
"data": {
"number": "INV-2026-0042",
"items": [
{ "name": "Logo redesign", "amount": "$1,200.00" },
{ "name": "Menu boards (4)", "amount": "$600.00" }
],
"total": "$1,800.00"
}
}
Strict mode is on by default: if the template uses a variable that data doesn't contain, the API refuses to render and returns a 422 missing_template_variables error listing the missing names, instead of sending a customer a PDF that opens with "Dear ," and leaves a blank where the total should be. Anyone who has sent that PDF once never forgets the replies. Send HTML with template syntax but no data, and you get a 422 template_syntax_without_data; set literal_mode: true if the braces are meant to print as text.
For invoices specifically, you may not need a template at all. The Invoice API takes customer details and line items as JSON, calculates totals with tax, discounts and shipping, and assigns a gapless invoice number when you issue it. One POST with issue: true and a send block issues the invoice and emails the PDF.
How do you avoid broken page breaks and missing fonts?
Use print CSS to control where pages break and how big they are, repeat table headers on each page, and make sure every font is available to the renderer. Most "my PDF looks wrong" problems come down to these four, plus images and backgrounds. The fixes below work in Chromium and in print-CSS engines like WeasyPrint unless noted:
- Page breaks: add
break-inside: avoidto rows, cards and totals blocks so they don't split across pages, andbreak-before: pagewhere a section must start fresh (MDN: break-inside). - Table headers: put header rows in
<thead>so the renderer can repeat them on every printed page. - Page size and margins: set them with
@page { size: Letter; margin: 18mm; }. If you run Puppeteer or Playwright yourself, also passpreferCSSPageSize: truetopage.pdf(), or theformatoption overrides your@pagesize. If the API also has a margin option, don't set both to different values. - Fonts: a font that isn't installed on the renderer falls back silently. Load web fonts by absolute URL or upload them to the provider. On ASHDOCS, upload fonts as assets and pass their IDs in the v2 endpoint's
fontsfield. - Images: use public absolute URLs or base64 data URIs. Relative paths have nothing to resolve against.
- Backgrounds (Chromium): colored backgrounds print only with the renderer's background option or
-webkit-print-color-adjust: exact; print-color-adjust: exact;in CSS. Chromium versions before 136 (April 2025) read only the prefixed property.
break-inside: avoid on the totals box and table rows.The HTML to PDF CSS issues guide goes deeper on each of these, with the symptom, the CSS fix and a quick-reference table.
How do async jobs, webhooks and retries work?
Small documents render inside the request and come back in the response; large ones run as background jobs that return a job ID at once and call your webhook when the file is ready. Send the same idempotency key on the original request and on every retry of it, so a timeout never produces two documents or two charges.
- Sync: the response contains the PDF or its URL. Fine for one-page invoices.
- Async: the response returns a job ID immediately. You poll the job or, better, subscribe to a webhook such as
job.completed. On ASHDOCS, HTML of 2 MB or more always runs this way. - Webhooks: verify the signature on every webhook before trusting it. ASHDOCS signs its webhook callbacks with HMAC-SHA256 and retries failed deliveries. Webhooks come with the paid plans, not Free.
- Idempotency: send a unique
Idempotency-Keyheader per logical document, such as your order ID. If the same request arrives twice, the second returns the first result. The header is widely used and was described in an IETF Internet-Draft, now expired (Idempotency-Key draft). On ASHDOCS, every/api/v1POST accepts it, including HTML-to-PDF and the Invoice API (rate limits and idempotency). - Timeouts and retries: set a client timeout longer than the provider's render budget. Retry only on network errors, 408, 409, 429 and 5xx responses. The Idempotency-Key draft uses 409 while a request with the same key is still running. On 429, wait for the
Retry-Afterdelay. Back off exponentially with jitter (the ASHDOCS rate-limit docs suggest capping the wait at 30 seconds), and always resend the sameIdempotency-Key. Don't retry 400, 401, 403 or 422 until you've fixed the request. The draft returns 422 when a key is reused with a different body.
Can a PDF REST API edit existing PDFs?
Yes. Many PDF REST APIs focus on processing files rather than creating them. You upload a PDF, or pass its URL, to an endpoint such as merge, split, compress, OCR or PDF to Word, and the API returns the processed file.
pdfRest, for example, lists Merge PDFs, Split PDF, Compress PDF, OCR PDF, Redact PDF and PDF to Word on its homepage (checked October 17, 2026).
The request pattern matches generation, with two differences. The input is a file, so the request is usually multipart/form-data or a file URL instead of JSON with HTML. Large files are also more likely to run as async jobs. If your flow creates a document and then needs a second step, such as merging it with your terms or compressing it before email, check that one vendor covers both, so you have one key and one bill. ASHDOCS has separate endpoints to merge PDFs, split a PDF, compress a PDF and run OCR on a scan; the tools page lists the rest.
How much does a PDF REST API cost?
Five of the ten vendors below offer a free tier of 20 to 100 documents a month (Browserless gives 1,000 browser units), and most entry paid plans cost $5 to $30 a month, but they meter in different units (credits, calls, compute time), so compare at your real volume.
Prices below are from each vendor's own pricing or product pages, checked October 17, 2026.
| Vendor | Free tier | Entry paid plan | Unit | Entry plan per 1,000 docs* |
|---|---|---|---|---|
| ASHDOCS | 100 credits/month | $15/month for 1,500 credits | 1 credit per HTML render or issued invoice (URL-to-PDF: 2) | $10.00 |
| pdfRest | 100 calls/month (watermarked) | $9.99/month for 600 calls | API call | $16.65 |
| CraftMyPDF | 50/month | $29/month for 1,200 | PDF or image | $24.17 |
| APITemplate.io | 50/month | $24/month for 3,000 PDFs ($19/month billed annually) | $8.00 | |
| PDFMonkey | 20/month | €5/month for 300 | Document | €16.67 |
| PDF Generator API | 14-day sandbox trial (no card required) | €8/month for 50 credits (€80/year billed annually) | Credit (1 per document of up to 5 pages) | €160.00 |
| PDF.co | 1-month free trial: 10,000 credits, no card required | $8.99/month billed annually, 16,500 credits | Credit (varies by operation) | Depends on credits per call |
| api2pdf | One month of free credit for new accounts | $1/month plus usage | Per MB + per compute-second | About $0.59 (vendor example: $5.91 for 10,000 PDFs at 0.1 MB, 2 s each) |
| Browserless | 1,000 units/month | $25/month billed annually, 20,000 units | Up to 30 s of browser time | $1.25 if each PDF takes one unit |
| Anvil | 2,500 starter credits after you add a card | $0.10 per PDF after free credits | $100.00 |
*Plan price divided by included documents at monthly billing, except Browserless and PDF.co, whose rows show the annual-billing price their pages display by default (both also bill monthly at a higher price). Assumes you use the whole allowance and each document costs one unit. Your real cost depends on volume, overage rates and annual discounts.
To read this table at your own volume, work out documents per month first, then find the cheapest plan that covers it, overage included. Some credit-based operations cost more than one unit per document. ASHDOCS doesn't charge for failed renders; other vendors' policies vary. Retention matters too: api2pdf deletes hosted files after 24 hours, so copy files to your own storage.
At 1,500 HTML renders a month, ASHDOCS Starter costs $15, or $10 per 1,000. Compare plans.
For DocRaptor, PDFShift and extraction-tool pricing, see PDF API pricing compared, and for how DocRaptor, PDFShift and Api2Pdf differ on rendering engines, pricing units and free tiers, see the PDF API comparison.
Sources: ASHDOCS (pricing, HTML to PDF cost, URL to PDF cost), pdfRest, CraftMyPDF, APITemplate.io, PDFMonkey, PDF Generator API (pricing, trial), PDF.co (pricing, trial), api2pdf, Browserless, Anvil.
What should you check for security?
Before you send customer data through a PDF API, check five things: whether URL-to-PDF blocks internal and cloud-metadata addresses, how untrusted HTML and template values are escaped, how long inputs and outputs are kept, whether download links expire, and that live API keys stay on your server.
- SSRF on URL-to-PDF. If users can submit URLs, the renderer must refuse internal addresses (localhost, 169.254.169.254, private IP ranges); otherwise a crafted URL can read your cloud metadata (OWASP SSRF Prevention Cheat Sheet). ASHDOCS refuses private and metadata addresses before fetching.
- Untrusted HTML. Send user-supplied values in the
dataobject instead of pasting them into the HTML string. ASHDOCS runs templates in sandboxed Jinja2 (HTML-to-PDF reference) and HTML-escapes substituted values (HTML to PDF API), so don't escape them yourself first, orSmith & Coprints asSmith & Co. If you build the HTML string yourself, escape those values before sending it, and prefer engines that disable JavaScript where you don't need it. ASHDOCS HTML-to-PDF runs no JavaScript in either of its engines (WeasyPrint, or headless Chromium for HTML of 512 KB or more), which closes off script injection, so send finished markup: charts or content built by scripts in the browser won't render. URL-to-PDF is different: it loads the live page in headless Chromium and can wait for its scripts to finish. - Retention. Know how long input files and outputs are stored and where. Read the provider's sub-processor list and DPA.
- Signed URLs. Download links should expire; ASHDOCS links do.
- Keys. Keep live keys on the server, never in front-end code, and use test keys in development.
Can you use a PDF API without code?
Yes. Any no-code platform with an HTTP step can call a PDF REST API: Webhooks by Zapier, Make's HTTP module or n8n's HTTP Request node. You map fields from the trigger (a form, a spreadsheet row, an Airtable record) into the JSON body, then pass the returned file or URL to the next step, such as Gmail or Google Drive.
How ASHDOCS connects to each platform:
- Zapier: Webhooks by Zapier, a premium app that needs a paid Zapier plan (Zapier setup).
- Make: the HTTP module. For JSON responses, turn "Parse response" on so you can map the file URL; for a binary file, leave it off (Make setup).
- n8n: the n8n-nodes-ashdocs community node on self-hosted n8n. On n8n Cloud, use the HTTP Request node (set the response format to File for binary) until the node is verified (n8n setup).
The Make and n8n setup pages show the method, URL, X-API-Key header and JSON body for the Invoice API; swap in the HTML-to-PDF URL and body above.
The no-code PDF automation guide compares three ways to automate PDFs without code, including a Zapier step that sends the generated file to Gmail or Drive.
Building a document flow in Zapier, Make or n8n? Get a free test key and render your first PDF from a real record.
Frequently asked questions
Can I call a PDF REST API from front-end JavaScript? Not with a live key. Anyone can read a key shipped in browser code and spend your credits. Call the API from your server, a serverless function or a no-code tool such as Zapier, Make or n8n, and use test keys during development.
What is the difference between a PDF API and a PDF library? A PDF API is a hosted service you call over HTTP; the provider runs the rendering engine. A PDF library such as pdf-lib or iText runs inside your own code and servers, so you handle fonts, scaling and updates yourself.
How do I generate a PDF from HTML with an API? POST your HTML, with page size and margins set in print CSS, to the provider's HTML-to-PDF endpoint with your API key. The response returns the PDF bytes or a signed URL. Use print CSS for page breaks and absolute URLs for images and fonts.
Can a PDF API fill a template with JSON data?
Yes. Template-based endpoints accept placeholders such as {{ customer_name }} and loops for line items, then merge a JSON data object into the template before rendering. Strict modes reject requests with missing values instead of printing blanks.
Is there a free PDF REST API? Yes, several have free monthly tiers. On October 17, 2026, ASHDOCS and pdfRest each included 100 a month (pdfRest output is watermarked), CraftMyPDF and APITemplate.io 50, and PDFMonkey 20. ASHDOCS test keys never use credits. Check the overage price before you go live.
How do I stop duplicate PDFs when a request is retried?
Send an Idempotency-Key header with a unique value per document, such as your order or invoice ID, if the API supports it. A retried request with the same key returns the original result instead of creating a second PDF.
Is headless Chrome required to generate PDFs? No. Many HTML-to-PDF services use headless Chromium because it renders modern CSS accurately, and others use print-CSS engines such as WeasyPrint. Low-level libraries draw PDFs without a browser and are faster, but you lose HTML and CSS layout.
How much does a PDF API cost per 1,000 documents? It depends on the vendor's unit. On entry plans checked October 17, 2026, it ranged from about $0.59 per 1,000 on usage-based pricing (at 10,000 documents a month) to $100 or more per 1,000 on per-PDF or low-allowance plans; most fixed plans landed between $8 and $25. Divide the plan price by its included documents at your expected volume, and check overage rates.
PDF REST API: where to start
If you already have an HTML template, start with the request sample and the page-break CSS above. If your documents come from records, start with the template example.
Endpoint details live on the PDF generation API and HTML to PDF API pages.
Get a free test key and render your first PDF from your own HTML.