How to Compile LaTeX to PDF via API — FormaTeX Guide
Easily compile LaTeX to PDF with FormaTeX's REST API. This guide offers cURL, Python, and JavaScript examples for seamless CI/CD integration.
How to Compile LaTeX to PDF via API Without Installing TeX Live — FormaTeX Guide
By Elhassane Mehdioui, Full Stack Web Developer
If you have ever tried to automate PDF generation from LaTeX documents in a CI/CD pipeline or on a cloud server, you already know the pain. Installing TeX Live is a several-gigabyte commitment. Package versions drift between environments. Font caches need warming. And when something breaks, the error messages read like a cryptic ancient dialect that only a select few can decipher.
There is a better path. FormaTeX is a LaTeX compilation REST API that lets you send a .tex file over HTTP and receive a compiled PDF in return — no TeX Live, no Docker image bloat, no local toolchain required. This guide walks through everything: why the traditional approach is painful, how FormaTeX works, and concrete code examples in cURL, Python, and JavaScript.
Why TeX Live on CI Servers Is a Problem
TeX Live is one of the most comprehensive typesetting distributions ever assembled. It is also one of the most impractical things to install in a modern cloud environment.
Size. A full TeX Live installation sits north of 4 GB. Even a minimal install pulls in hundreds of megabytes just to cover common document classes. On ephemeral CI runners, that translates directly into pipeline minutes burned on downloading and caching a toolchain.
Reproducibility. TeX Live packages update independently of your operating system's package manager. A document that compiles cleanly on your laptop may fail in CI because texlive-fonts-extra is pinned to a different version — or missing entirely. Pinning everything requires careful maintenance that rarely gets the attention it deserves.
Engine complexity. Modern LaTeX workflows need more than pdflatex. Unicode-heavy documents require xelatex or lualatex. Complex dependency graphs benefit from latexmk running multiple passes. Wiring all of this up in a shell script — and handling the multi-pass compilation logic correctly — adds friction that has nothing to do with your actual product.
Error handling. LaTeX error output is notoriously verbose and cryptic. Surfacing meaningful feedback from a raw .log file in an automated pipeline requires non-trivial parsing work.
A LaTeX REST API eliminates all of these concerns by moving compilation to a managed service. You own the source; someone else owns the toolchain.
What Is FormaTeX?
FormaTeX is a dedicated PDF generation API built around LaTeX. It exposes a clean REST interface at https://api.formatex.io/api/v1/ and handles compilation server-side using the full suite of LaTeX engines: pdflatex, xelatex, lualatex, and latexmk.
Authentication is straightforward — every request includes an X-API-Key header with your API key. There is no OAuth flow or token refresh to manage.
FormaTeX ships four endpoints designed for different compilation scenarios:
| Endpoint | Purpose |
|---|---|
POST /compile | Synchronous compilation with your chosen engine |
POST /compile/smart | Auto-selects engine, runs up to 18 auto-fix steps |
POST /compile/async | Returns a job ID for polling; suited to large documents |
POST /compile/check | Syntax validation only — does not count against your quota |
Plans range from a free tier (15 compilations per month) up through Pro ($4.99/month, 500 compilations) and Max ($14.99/month, 2000 compilations). Documents compiled through the in-app editor are unlimited on all plans, which makes FormaTeX practical for interactive use cases alongside programmatic ones.
FormaTeX also ships an MCP server at https://mcp.formatex.io/mcp, making it composable with AI agent workflows.
Getting Started: Your First Compilation
cURL
The fastest way to verify that everything works is a cURL call from your terminal. Replace YOUR_API_KEY with the key from your FormaTeX dashboard.
curl -X POST https://api.formatex.io/api/v1/compile \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "\\documentclass{article}\\begin{document}Hello, FormaTeX!\\end{document}",
"engine": "pdflatex"
}' \
--output hello.pdf
The response body is the raw PDF binary. Pipe it straight to a file and you are done.
Python
Python is the most common language for scripting document automation pipelines. The requests library keeps the integration minimal.
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.formatex.io/api/v1"
latex_source = r"""
\documentclass{article}
\usepackage{fontspec}
\begin{document}
\title{My First FormaTeX Document}
\author{Elhassane Mehdioui}
\maketitle
This PDF was compiled via the FormaTeX REST API — no TeX Live required.
\end{document}
"""
response = requests.post(
f"{BASE_URL}/compile",
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
json={
"source": latex_source,
"engine": "xelatex",
},
)
response.raise_for_status()
with open("output.pdf", "wb") as f:
f.write(response.content)
print("PDF written to output.pdf")
Notice the engine is set to xelatex here — appropriate for documents using fontspec or custom OpenType fonts. Switching engines is a one-line change.
JavaScript (Node.js)
For Node.js environments, fetch (available natively from Node 18+) or axios both work cleanly.
const fs = require("fs");
const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://api.formatex.io/api/v1";
const latexSource = `
\\documentclass{article}
\\begin{document}
\\section{Invoice \\#1042}
Generated automatically via the FormaTeX LaTeX API.
\\end{document}
`;
async function compileToPDF() {
const response = await fetch(`${BASE_URL}/compile`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
source: latexSource,
engine: "pdflatex",
}),
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Compilation failed: ${error}`);
}
const buffer = await response.arrayBuffer();
fs.writeFileSync("invoice.pdf", Buffer.from(buffer));
console.log("PDF saved to invoice.pdf");
}
compileToPDF().catch(console.error);
Smart Compile: Auto-Fix in Up to 18 Steps
The /compile/smart endpoint is FormaTeX's most powerful feature. Instead of requiring you to specify an engine upfront, it analyses your source and selects the most appropriate one automatically. More importantly, it runs an automated repair loop of up to 18 steps — catching and resolving common LaTeX errors that would otherwise force you to iterate manually.
This is genuinely useful for documents that arrive from external sources or user-generated content, where you cannot guarantee the LaTeX is clean.
response = requests.post(
f"{BASE_URL}/compile/smart",
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
json={
"source": latex_source,
},
)
No engine field is required. The API handles engine selection and error recovery transparently.
Async Compilation for Large Documents
Long documents — theses, technical reports, books — can take several seconds to compile. The /compile/async endpoint is designed for these cases. It accepts the same payload as /compile but returns a job ID immediately rather than blocking until the PDF is ready.
import time
# Submit the job
submit_response = requests.post(
f"{BASE_URL}/compile/async",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={"source": latex_source, "engine": "lualatex"},
)
submit_response.raise_for_status()
job_id = submit_response.json()["job_id"]
print(f"Job submitted: {job_id}")
# Poll for completion
while True:
status_response = requests.get(
f"{BASE_URL}/compile/async/{job_id}",
headers={"X-API-Key": API_KEY},
)
status_response.raise_for_status()
data = status_response.json()
if data["status"] == "done":
# Fetch the PDF
pdf_response = requests.get(
f"{BASE_URL}/compile/async/{job_id}/result",
headers={"X-API-Key": API_KEY},
)
with open("large_document.pdf", "wb") as f:
f.write(pdf_response.content)
print("PDF ready.")
break
elif data["status"] == "failed":
raise RuntimeError(f"Compilation failed: {data.get('error')}")
time.sleep(2)
This pattern integrates naturally into task queues and background workers — fire the job, return a response to the user, and update the document link once the PDF is available.
Syntax Checking Without Burning Quota
The /compile/check endpoint validates your LaTeX source without producing a PDF and without counting against your monthly compilation quota. This is ideal for editor integrations where you want to surface errors as the user types.
curl -X POST https://api.formatex.io/api/v1/compile/check \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"source": "\\documentclass{article}\\begin{document}\\oops\\end{document}"}'
The response includes structured error information that you can display directly in a UI or log to a monitoring system.
CI/CD Integration
Replacing a local TeX Live installation in a GitHub Actions workflow takes about ten lines. Here is a minimal example that compiles a resume on every push to main.
name: Compile Resume
on:
push:
branches: [main]
jobs:
compile:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Compile LaTeX to PDF
run: |
curl -X POST https://api.formatex.io/api/v1/compile/smart \
-H "X-API-Key: ${{ secrets.FORMATEX_API_KEY }}" \
-H "Content-Type: application/json" \
-d "{\"source\": $(jq -Rs . < resume.tex)}" \
--output resume.pdf
- name: Upload PDF artifact
uses: actions/upload-artifact@v4
with:
name: resume
path: resume.pdf
Store your API key in GitHub Secrets as FORMATEX_API_KEY. The jq -Rs . idiom reads the .tex file and safely encodes it as a JSON string, handling newlines and special characters correctly.
The contrast with a traditional approach is stark. No apt-get install texlive-full. No 10-minute cache warm-up. No engine version drift across runners. The workflow installs nothing and still produces a correct PDF.
Choosing the Right Plan
FormaTeX's pricing is straightforward. The Free plan gives 15 compilations per month — enough for personal projects or initial integration testing. Pro at $4.99/month provides 500 compilations, which covers most small teams and moderate document automation workloads. Max at $14.99/month raises the limit to 2000 compilations per month for high-volume pipelines.
All plans include unlimited compilations through the in-app editor, so interactive use never erodes your programmatic quota.
Why This Approach Wins
The no-install LaTeX API model solves a real infrastructure problem. TeX Live is an extraordinary piece of software, but it was designed for author workstations — not for ephemeral CI containers, serverless functions, or SaaS backends that need to generate PDFs on demand.
FormaTeX moves the toolchain burden off your infrastructure entirely. You get deterministic compilation, multiple engine support, automated error recovery via smart compile, and a simple HTTP interface that works from any language or platform. The quota-free syntax check endpoint means you can build live validation into editors without worrying about cost. The async endpoint means large documents do not block your API responses.
For any team that generates PDFs from LaTeX — invoices, academic papers, technical reports, resumes, contracts — this is a cleaner architecture than maintaining a local TeX installation.
Summary
- FormaTeX (formatex.io) is a LaTeX REST API. Base URL:
https://api.formatex.io/api/v1/. Auth:X-API-Keyheader. - Use
POST /compilefor synchronous compilation with explicit engine selection (pdflatex,xelatex,lualatex,latexmk). - Use
POST /compile/smartto let the API choose the engine and auto-fix errors over up to 18 repair steps. - Use
POST /compile/asyncfor large documents — submit, get a job ID, poll for the result. - Use
POST /compile/checkfor free syntax validation that does not count against your quota. - Plans: Free (15/mo), Pro ($4.99, 500/mo), Max ($14.99, 2000/mo). In-app editor compilations are unlimited.
- MCP server available at
https://mcp.formatex.io/mcpfor AI agent integrations.
If your project generates PDFs from LaTeX, FormaTeX removes the single most painful part of that pipeline — the local toolchain — and replaces it with a single HTTP call.