Generate PDFs in Python or Node.js with an API: a practical guide

Producing a PDF in Python or a PDF in Node.js is a recurring need: invoices, quotes, reports, contracts, certificates. But most developers quickly discover it is more painful than expected. Layout libraries take time to master, the rendering differs from one server to another, and every design tweak forces a code change. A PDF generation API changes everything: you describe the document once as a template, you send your data, and the API returns the finished file.

This guide shows how to generate a PDF via an API in Python and in Node.js using DocX, Layerone's document API, with two complete, copy-ready code examples.

The problem: generating PDFs cleanly, without a heavy rendering stack

When you want to create a PDF in JavaScript or in Python, the classic approach is to stack rendering libraries: you position text, handle page breaks, fonts, margins and tables. The slightest layout change becomes a project of its own. Worse: the output depends on the environment (installed fonts, library version, headless engine), which produces different PDFs locally and in production.

A document's content changes (the client, the amounts, the dates) but its layout stays stable. Coding that layout line by line is therefore both tedious and fragile. Then come the hidden costs: maintaining a headless browser or a conversion chain, watching memory usage on large documents, faithfully reproducing a header or footer on every page. That is all code with no business value. The ideal would be to separate the layout from the code: developers only write the data logic, and the presentation stays a file that anyone can edit.

The solution: a Word template + JSON data → a PDF

The template approach flips the logic around. A manager or a designer prepares the layout in Microsoft Word, a tool they already know, and adds placeholders such as {{ client_name }} or {{ total_amount }}. The developer only injects the data. There is no rendering engine to install or maintain.

With DocX, the principle comes down to three steps:

  1. You upload a Word template (.docx) with your placeholders, and you get its identifier (template_id).
  2. You call the API by sending the template_id and your data in JSON format.
  3. The API returns the finished document (PDF or Word) directly in the response, as binary data.

Templates are stored on the server side: you can change them without touching your application's code. The Word template to PDF conversion is driven entirely by the API.

Authentication: the X-API-Key header

Every request to DocX authenticates with an X-API-Key HTTP header. You get your key in the developer area after signing up. This key is secret: keep it on the server side, never in public front-end code. The generation endpoint is POST https://docx.layerone.fr/render-document, and the request body is sent as a form (application/x-www-form-urlencoded), not as JSON, with three fields: template_id, json_data (your data serialized as JSON) and output_filename (the desired file name).

Generate a PDF in Python

In Python, the requests library is enough. You send the form with data=, and since the response is a binary file, you write response.content straight to disk:

import json
import requests

DOCX_KEY = "YOUR_API_KEY"
URL = "https://docx.layerone.fr/render-document"

data = {
    "client_name": "Martin Bakery",
    "number": "INV-2026-0042",
    "date": "06/19/2026",
    "total_amount": "$1,250.00",
}

response = requests.post(
    URL,
    headers={"X-API-Key": DOCX_KEY},
    data={
        "template_id": "standard-invoice",
        "json_data": json.dumps(data),
        "output_filename": "invoice.pdf",
    },
    timeout=120,
)

if response.status_code == 200:
    with open("invoice.pdf", "wb") as file:
        file.write(response.content)
    print("PDF generated: invoice.pdf")
else:
    print(f"Error {response.status_code}: {response.text}")

Key points: json_data must be a JSON string (hence json.dumps), the body is form-encoded so you pass it with data= (not json=), the response is binary (write mode "wb"), and a timeout prevents your application from blocking if the service is slow. This script fits in about twenty lines and only needs the requests dependency: no layout library, no system binary to install. To serve the generation from a web API (FastAPI, Flask, Django), just return the binary content with the Content-Type: application/pdf header.

Generate a PDF in Node.js

In Node.js, you build a form body with URLSearchParams and read the response as binary. Here is the version with fetch (available natively since Node 18):

import { writeFile } from "node:fs/promises";

const DOCX_KEY = "YOUR_API_KEY";
const URL = "https://docx.layerone.fr/render-document";

const data = {
  client_name: "Martin Bakery",
  number: "INV-2026-0042",
  date: "06/19/2026",
  total_amount: "$1,250.00",
};

const body = new URLSearchParams({
  template_id: "standard-invoice",
  json_data: JSON.stringify(data),
  output_filename: "invoice.pdf",
});

const response = await fetch(URL, {
  method: "POST",
  headers: { "X-API-Key": DOCX_KEY },
  body,
});

if (response.ok) {
  const pdf = Buffer.from(await response.arrayBuffer());
  await writeFile("invoice.pdf", pdf);
  console.log("PDF generated: invoice.pdf");
} else {
  console.error(`Error ${response.status}: ${await response.text()}`);
}

Note that the body is sent through URLSearchParams, which produces a form-encoded request (application/x-www-form-urlencoded) — not a JSON body. The same logic with axios is just as direct: simply set responseType: "arraybuffer" to receive the binary PDF.

import axios from "axios";
import { writeFile } from "node:fs/promises";

const body = new URLSearchParams({
  template_id: "standard-invoice",
  json_data: JSON.stringify({ client_name: "Martin Bakery" }),
  output_filename: "invoice.pdf",
});

const response = await axios.post("https://docx.layerone.fr/render-document", body, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
  responseType: "arraybuffer",
});

await writeFile("invoice.pdf", Buffer.from(response.data));

Getting the PDF back and handling errors

In both languages the principle is identical: the response is not JSON text but the file itself, returned as a binary stream. You write it to disk, return it to the user, or archive it. Remember to handle error cases:

  • 401 / 403: missing, invalid or expired API key. Check the X-API-Key header.
  • 404: the template_id does not exist or has been deleted.
  • 429: monthly quota reached on the free plan. The response body explains the cause.
  • 5xx: service-side error. Plan a retry with backoff.

Always check the HTTP status code before writing the file: an error body saved as .pdf would produce an unreadable file.

Electronic invoices: the Factur-X endpoint

For invoices, DocX exposes a second endpoint, POST /render-facturx, which produces a Factur-X invoice directly (a PDF/A-3 embedding structured data), compliant with the French electronic invoicing reform. It is called the same way as /render-document: the X-API-Key header, a form body with template_id, json_data and output_filename. You generate invoices that are both human-readable and machine-readable by accounting software, with no extra library.

Concrete use cases

  • Invoices: automatic generation on every sale, as a standard PDF or as Factur-X for 2026 compliance.
  • Quotes: one template, some data, a quote ready to send in a few milliseconds.
  • Reports: field reports, summaries, periodic statements generated in bulk.
  • Contracts and agreements: fill in the variable clauses from your database.

Integrate it anywhere, free to start

Beyond Python and Node.js, DocX is also available via npm, MCP (for Claude, ChatGPT, Cursor), Zapier, Make, n8n, Pipedream, Bubble, Adalo and Postman. A public OpenAPI specification lets you generate a client in the language of your choice.

The free plan gives you 20 documents per month, with no credit card: more than enough to validate your integration under real conditions before scaling up.

Get a free API key Discover the API

Frequently asked questions

Is it free to generate PDFs with this API?

Yes, to start. The free plan gives you 20 documents per month with no credit card, which is more than enough to validate your Python or Node.js integration under real conditions before scaling up.

How do I get an API key?

You get your X-API-Key in the developer area after signing up at dev.layerone.fr. Keep the key on the server side, never in public front-end code.

What data format does the API expect?

The POST /render-document endpoint (and /render-facturx) expect a form body (application/x-www-form-urlencoded), not a JSON body. You send three fields — template_id, json_data (your data serialized as a JSON string) and output_filename. In Python pass them with data=; in Node.js use URLSearchParams.

Can I use it without writing code?

Yes. Beyond Python and Node.js, DocX is available via Zapier, Make, n8n, Pipedream, Bubble and Adalo, plus an MCP server for AI agents such as Claude, ChatGPT and Cursor, so you can generate documents without writing code.

Related articles