Guide
Generate a PDF from HTML in Java
You have a Spring Boot controller, some HTML (an invoice, a statement, a label), and you need to return a PDF. The classic Java answers each carry baggage: iText wants an AGPL license or a commercial one, Flying Saucer chokes on modern CSS, and Playwright means a browser in your container. The lighter path is one HTTP call with the java.net.http.HttpClient that has shipped with the JDK since Java 11.
The minimal Spring Boot controller
The PDFPipe endpoint is POST https://api.pdfpipe.xyz/v1/pdf. Send a JSON body with your HTML and options, attach a bearer token, and you get back raw application/pdf bytes.
@RestController
public class InvoiceController {
private static final HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
private static final String KEY = System.getenv("PDFPIPE_KEY");
@GetMapping(value = "/invoice/{id}", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<byte[]> invoice(@PathVariable String id)
throws IOException, InterruptedException {
String html = """
<html>
<body style="font-family: sans-serif; padding: 40px">
<h1>Invoice #%s</h1>
<p>Amount due: $420.00</p>
</body>
</html>""".formatted(id);
String payload = new ObjectMapper().writeValueAsString(Map.of(
"html", html,
"options", Map.of("format", "A4")
));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.pdfpipe.xyz/v1/pdf"))
.timeout(Duration.ofSeconds(20))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + KEY)
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
HttpResponse<byte[]> response =
http.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
throw new ResponseStatusException(HttpStatus.BAD_GATEWAY,
"PDF render failed: " + new String(response.body()));
}
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"inline; filename=\"invoice-" + id + ".pdf\"")
.body(response.body());
}
}That is the whole feature: no native libraries, no font configuration on the server, no license discussion with legal. The only dependency in play is Jackson, which Spring Boot already ships.
Why not iText, Flying Saucer, or Playwright?
| Concern | Library in the JVM | HttpClient to PDFPipe |
|---|---|---|
| Licensing | iText is AGPL or paid; OpenPDF trails behind | Plain API pricing, your code stays yours |
| Modern CSS | Flying Saucer is CSS 2.1, no flexbox or grid | Current engine, renders what your browser renders |
| Container weight | Playwright adds a browser and ~700 MB to the image | Nothing beyond your jar |
| Heap pressure | Document object models for large reports get big | One response buffer, GC-friendly |
The pattern holds outside Spring too: the same HttpClient code works in Quarkus, Micronaut, a plain servlet, or a CLI tool, because it is just the JDK.
Timeouts and error handling
Both timeouts are set above: connect on the client, request on the call. Renders are idempotent, so a single retry on a 5xx or an IOException is safe. If you are on virtual threads (Java 21+), the blocking send call parks the virtual thread and costs you nothing while the render runs.
// Virtual-thread friendly retry, render calls are idempotent.
HttpResponse<byte[]> response;
try {
response = http.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() >= 500) {
response = http.send(request, HttpResponse.BodyHandlers.ofByteArray());
}
} catch (IOException firstFailure) {
response = http.send(request, HttpResponse.BodyHandlers.ofByteArray());
}Keep the API key in an environment variable or your secrets manager. The options object also accepts other page sizes (for example Letter), margins, and landscape orientation, documented in full on the docs page.
Paste your own HTML into the live playground on the home page to see the PDF render instantly, then read the docs for every option.
See pricing →