How to Generate PDF Invoices with DocRaptor
Generate branded PDF invoices with DocRaptor: build an HTML template, send it to the API, handle the PDF response, and troubleshoot rendering and delivery.
To generate a PDF invoice with DocRaptor, render your invoice data into HTML, send that HTML to DocRaptor’s JSON API at https://api.docraptor.com/docs with type set to pdf, then save the returned PDF bytes. Keep the API key on your server. A basic request uses document_content for inline HTML; use document_url when DocRaptor should fetch an invoice page itself.
This guide covers the synchronous API flow, runnable cURL, Python, and Node.js examples, print-oriented invoice markup, test mode, JavaScript, alternate delivery modes, operational limits, and common failures. It explains PDF generation only; a correctly rendered PDF does not establish that an invoice meets tax or legal requirements.
1. Prepare invoice data and an HTML template
Keep invoice facts in structured application data: seller and customer details, invoice number, dates, line items, quantities, unit prices, applicable tax values, totals, and payment instructions. Render those values through a controlled template. The following is a small static example to demonstrate the document structure; adapt its fields to your application and requirements.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Invoice INV-1042</title>
<style>
@page { size: A4; margin: 18mm; }
body { font: 12px/1.45 Arial, sans-serif; color: #20242a; }
h1 { font-size: 26px; margin: 0 0 18px; }
.row { display: flex; justify-content: space-between; gap: 24px; }
table { width: 100%; border-collapse: collapse; margin-top: 24px; }
th, td { padding: 9px 7px; border-bottom: 1px solid #d9dde2; text-align: left; }
th:last-child, td:last-child { text-align: right; }
thead { display: table-header-group; }
tr { page-break-inside: avoid; }
.total { margin: 20px 0 0 auto; width: 240px; text-align: right; }
.muted { color: #606873; }
</style>
</head>
<body>
<h1>Invoice</h1>
<div class="row">
<section><strong>From</strong><br>Northstar Studio<br>billing@example.com</section>
<section><strong>Bill to</strong><br>Acme Ltd<br>accounts@example.com</section>
</div>
<p><strong>Invoice number:</strong> INV-1042<br>
<strong>Issued:</strong> 2026-10-04<br>
<strong>Due:</strong> 2026-11-03</p>
<table>
<thead><tr><th>Description</th><th>Qty</th><th>Unit price</th><th>Amount</th></tr></thead>
<tbody>
<tr><td>Design services</td><td>4</td><td>$125.00</td><td>$500.00</td></tr>
<tr><td>Hosting</td><td>1</td><td>$25.00</td><td>$25.00</td></tr>
</tbody>
</table>
<div class="total">
<p>Subtotal: $525.00<br>Tax: $0.00</p>
<p><strong>Total due: $525.00</strong></p>
</div>
<p class="muted">Payment instructions: Pay by bank transfer using invoice INV-1042 as the reference.</p>
</body>
</html>
Escape customer-supplied text before inserting it into HTML and validate numeric values and dates in your application. That is general secure template practice, not a DocRaptor-specific invoice schema. Keep calculations in application code so the values displayed in the document are derived consistently; do not rely on PDF rendering to calculate totals.
Design for paginated output
- Set page size and margins with
@pageso print dimensions are deliberate. - Use repeated table headers and avoid splitting a line item across pages where practical.
- Check long descriptions, large item counts, and unusually long customer names for overflow.
- Use absolute or otherwise stable URLs for external images and fonts, and verify they are reachable by the renderer.
- Preview page breaks with representative short and long invoices. A one-page sample will not reveal every pagination issue.
2. Generate the PDF with the DocRaptor API
DocRaptor’s direct REST flow is a JSON POST to https://api.docraptor.com/docs. The request needs a document input and type: "pdf". The examples below use Basic Authentication with the API key as the username and a blank password, the preferred direct REST authentication method in DocRaptor’s overview. Get an API key from your DocRaptor account and store it as a server-side secret.
cURL
export DOCRAPTOR_API_KEY='YOUR_API_KEY'
curl --fail-with-body --silent --show-error \
--user "$DOCRAPTOR_API_KEY:" \
--header 'Content-Type: application/json' \
--data '{"type":"pdf","name":"invoice-INV-1042","test":true,"document_content":"<html><body><h1>Invoice INV-1042</h1><p>Total due: $525.00</p></body></html>"}' \
https://api.docraptor.com/docs \
--output invoice-INV-1042.pdf
This compact request uses test: true for layout iteration. Replace the short inline HTML with your rendered template. Test documents are watermarked and do not count toward plan quotas, so turn test mode off for deliverables.
Python
import os
import requests
api_key = os.environ["DOCRAPTOR_API_KEY"]
html = """<!doctype html>
<html><body>
<h1>Invoice INV-1042</h1>
<p>Total due: $525.00</p>
</body></html>"""
payload = {
"type": "pdf",
"name": "invoice-INV-1042",
"test": True,
"document_content": html,
}
response = requests.post(
"https://api.docraptor.com/docs",
auth=(api_key, ""),
json=payload,
timeout=90,
)
response.raise_for_status()
content_type = response.headers.get("Content-Type", "")
if "pdf" not in content_type.lower():
raise RuntimeError(f"Expected a PDF response, got {content_type!r}: {response.text[:500]}")
with open("invoice-INV-1042.pdf", "wb") as pdf_file:
pdf_file.write(response.content)
Node.js
import { writeFile } from "node:fs/promises";
const apiKey = process.env.DOCRAPTOR_API_KEY;
if (!apiKey) throw new Error("Set DOCRAPTOR_API_KEY");
const html = `<!doctype html>
<html><body>
<h1>Invoice INV-1042</h1>
<p>Total due: $525.00</p>
</body></html>`;
const payload = {
type: "pdf",
name: "invoice-INV-1042",
test: true,
document_content: html,
};
const basic = Buffer.from(`${apiKey}:`).toString("base64");
const response = await fetch("https://api.docraptor.com/docs", {
method: "POST",
headers: {
Authorization: `Basic ${basic}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
signal: AbortSignal.timeout(90000),
});
if (!response.ok) {
const detail = await response.text();
throw new Error(`DocRaptor returned HTTP ${response.status}: ${detail}`);
}
const contentType = response.headers.get("content-type") ?? "";
if (!contentType.toLowerCase().includes("pdf")) {
throw new Error(`Expected PDF content, got ${contentType}`);
}
await writeFile("invoice-INV-1042.pdf", Buffer.from(await response.arrayBuffer()));
These examples check the HTTP result and PDF content type before saving bytes. Without those checks, an API error body can accidentally be stored with a .pdf extension. DocRaptor documents official client libraries as another way to submit requests; the direct HTTP pattern here makes the request and response behavior visible.
3. Choose inline HTML or a source URL
DocRaptor accepts either document_content or document_url. They are alternative ways to supply the document:
| Input | Use it when | Things to check |
|---|---|---|
document_content |
Your application has rendered the invoice HTML and submits it directly. | Escape untrusted values, include needed styles, and ensure external assets are accessible. |
document_url |
The invoice is available at a URL that DocRaptor can fetch. | Ensure the URL is reachable by DocRaptor and does not depend on a browser session or private local network access. |
For a URL input, replace document_content in the JSON with a fetchable address, for example "document_url":"https://your.example/invoices/INV-1042/render". Do not put private invoice data at a publicly accessible URL unless that access model is intentional. The API documentation establishes URL fetching, but your application must decide how that document is authenticated and protected.
4. Iterate with test mode, then issue the real PDF
Set test to true while adjusting typography, logos, page breaks, and totals. DocRaptor says test documents are unlimited and do not count against monthly quotas; test PDFs are watermarked. Use a test document to inspect rendering, then set test to false or omit it for production output.
- Render a representative invoice with few line items and one with enough items to span multiple pages.
- Inspect the PDF itself: page count, clipped text, repeated headers, image loading, and final totals.
- Change one layout variable at a time so page-break regressions are easier to locate.
- Generate a non-test document only when the output is ready for delivery.
5. Decide whether invoice rendering needs JavaScript
JavaScript execution is disabled by default. A server-rendered invoice with its values already present in the HTML generally does not need it. Enable a JavaScript option only if the output depends on script-generated content, such as a client-rendered component or chart. DocRaptor documents two supported JavaScript engines and warns that enabling both can execute JavaScript twice. That can duplicate script side effects or change output, so select only what the template needs and validate the rendered PDF.
For script-dependent content, configure the relevant javascript option in the request according to the current API reference. Allow for script execution in your render-time budget and make scripts deterministic: avoid relying on user interaction, local browser state, or content that appears only after an unpredictable external event.
6. Select synchronous, asynchronous, or hosted delivery
A normal synchronous request returns PDF bytes in its response body. This is the simplest option when a render is expected to finish quickly and the caller can wait. DocRaptor also documents asynchronous generation and hosted documents, which have different retrieval and delivery behavior.
| Mode | Response pattern | Choose it when |
|---|---|---|
| Synchronous | The response contains the generated PDF bytes. | The request can complete within the synchronous time limit and your application is ready to stream or store the bytes. |
| Asynchronous | The request returns a status_id; retrieve the result later through the documented status flow. |
Rendering may take longer or should run as a background job. Your application must track job state and handle completion or failure. |
| Hosted | DocRaptor returns a public document URL for retrieval. | A hosted URL fits your delivery model. Review hosted-output size, expiry, download, and hosting-fee terms before relying on it. |
Use DocRaptor’s current API reference for the exact request flags and retrieval endpoints for async and hosted modes. Do not treat an async acknowledgement as the PDF, or assume a hosted URL has the same privacy properties as an authenticated application download.
7. Handle failures and protect invoice delivery
- Check status before saving: non-success responses are errors, not PDFs. Keep the error details in server logs without returning secrets to the end user.
- Use bounded timeouts: a client timeout does not prove that the remote render failed. Avoid blind retries that could create duplicate work; use your own job identifier and retry policy.
- Keep credentials server-side: never embed the API key in public frontend JavaScript. DocRaptor specifically warns that its browser JavaScript approach exposes the key in page source.
- Make invoice creation idempotent in your app: tie a generated file to an invoice version or identifier, and decide whether edits create a new PDF or replace an earlier file.
- Validate the finished artifact: rendering success alone does not prove that all fields, totals, or page breaks are correct.
8. Limits, throughput, and cost planning
DocRaptor’s API limits page currently lists a one-minute limit for synchronous generation, ten minutes for asynchronous generation, and 30 simultaneous requests. It lists a 100 MB output-size limit for hosted documents. The same page says there are no hard limits on page count or document complexity, although complex documents can take several minutes. These operational figures and plan terms can change, so check the live documentation before rollout.
For reliability and throughput, queue large batches, cap concurrency at the documented limit, and track render durations and failures. Prefer async processing when user requests should not wait on a potentially long render. Cache a PDF only when it corresponds to the same invoice data and template version; invalidate it whenever either changes. If a synchronous operation approaches the one-minute limit, simplify the template or move that workflow to the documented async mode.
Plans have monthly document quotas. DocRaptor’s overage documentation includes an example of 9 cents per document for Professional, and gives a Professional quota example of 325 documents per billing cycle; these are documentation examples, not a guarantee of your current account terms. Hosted documents have a separate plan-based fee model: the hosting page describes a small monthly hosting fee per document and charges for downloads after the first five, at a plan-based rate. Check the current pricing, overage, and hosting pages and your account terms before estimating production cost.
9. Troubleshooting common problems
| Symptom | Likely cause | What to do |
|---|---|---|
| Saved file is not a valid PDF | An HTTP error response or other body was saved as if it were PDF data. | Check the HTTP status and content type before writing the response body. Log the API error response securely. |
| Authentication fails | The API key is missing, invalid, malformed, or sent using the wrong authentication format. | Confirm the key is loaded on the server and use Basic Auth with the key as username and a blank password for the direct REST request. |
| Invoice content is blank or incomplete | The supplied HTML is empty, a fetched URL is inaccessible, or content depends on disabled JavaScript. | Inspect the exact generated HTML; for URL input, verify it is reachable by the renderer; enable JavaScript only if the template requires it. |
| Images or fonts are missing | Asset URLs are invalid or inaccessible to the renderer, or resources are not ready when rendering occurs. | Use stable absolute URLs and check that the renderer can fetch them. Reproduce with a minimal document to isolate the resource. |
| Text or line items are clipped | Content exceeds a fixed-width area or a row/page break is unsuitable for long values. | Test long descriptions and names; adjust wrapping, column widths, margins, or page-break rules. |
| JavaScript content is absent | JavaScript is disabled by default, or the script requires browser state or timing that is unavailable. | Enable the needed engine using the documented option, then make the script deterministic and verify output. Avoid enabling both engines unless required. |
| Request times out | The document is complex, the synchronous limit is reached, or the client timeout is too short. | Reduce unnecessary scripts and assets, set an appropriate client timeout, and use async generation for longer jobs. |
| Batch requests are rejected or delayed | Concurrency exceeds the documented 30 simultaneous requests or account capacity. | Queue work and limit parallel requests; retry transient failures with backoff rather than an immediate tight loop. |
| Unexpected charge or hosted URL behavior | Test mode, quota, overage, hosting, or download terms were misunderstood. | Check current plan terms. Remember test PDFs are watermarked; review hosting fees and download limits before selecting hosted delivery. |
10. Or skip the browser setup
DocRaptor generates PDFs from HTML. If your task is to capture a webpage as an image or PDF rather than render an invoice template, ScreenshotNeo provides a one-request website screenshot API. It is a separate workflow from invoice PDF generation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for the other supported options and response details. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up for ScreenshotNeo’s free plan.
Frequently asked questions
Can DocRaptor create a PDF from a URL instead of supplied HTML?
Yes. The API accepts document_url as an alternative to document_content. The URL must be fetchable by the renderer.
Are test PDFs suitable to send to customers?
No. Test mode is for layout iteration, and DocRaptor says test PDFs are watermarked.
Does successful PDF generation mean the invoice is legally compliant?
No. PDF rendering confirms only that a document was generated. The research sources do not establish invoice requirements, tax treatment, or compliance for any jurisdiction; consult the relevant tax authority or qualified professional.
Can I call DocRaptor directly from browser JavaScript?
Do not expose the API key in public browser code. DocRaptor warns that its browser JavaScript library reveals the key in page source; submit requests from a server you control.
Official references
- DocRaptor API reference — request parameters, content inputs, test mode, and JavaScript options.
- DocRaptor API overview — authentication, requests, responses, and errors.
- HTML and JavaScript tutorial — request examples and browser key warning.
- API limits — current timing, concurrency, and hosted output limits.
- Overage documentation and hosted document details — quota, overage, and hosting fee models.
- JavaScript execution documentation — defaults and engine behavior.


