Search documentation

Search across all sections and endpoints

FormaTeX
Docsv1

API Reference

Complete reference for the FormaTeX REST API. Compile LaTeX documents to PDF with a single HTTP call.

Quick Example

bash
curl -X POST https://api.formatex.io/api/v1/compile \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"latex": "\\documentclass{article}\\begin{document}Hello!\\end{document}"}' \
  --output document.pdf

Authentication

Two auth methods: JWT tokens for account management, API keys for compilation.

MethodHeaderUsed For
JWT TokenAuthorization: Bearer <token>User management, billing, admin
API KeyX-API-Key: <key>Compilation endpoints

Errors

All errors return a consistent JSON format with an error message and optional context.

json
{
  "error": "human-readable error message",
  "plan": "free",
  "limit": 100,
  "used": 100
}

Status Codes

CodeMeaning
400Invalid request body
401Missing or invalid authentication
403Plan limit violated or account deactivated
404Resource not found
409Conflict (e.g., email already registered)
422Compilation failed
429Rate limit exceeded
503Billing not configured

Rate Limits

Endpoint GroupLimit
Auth20 req/min
User management60 req/min
Billing20 req/min
Admin60 req/min
Compilation100 req/min

API Keys

Create and manage API keys for compilation. All endpoints require JWT.

Compilation

Compile LaTeX to PDF. All compilation endpoints require an API Key.

Rendering

Render equations, TikZ diagrams, and compile full documents to rasterized images — all without touching your monthly compilation quota. All endpoints require an API key.

Document Analysis

Static analysis endpoints for LaTeX and BibTeX. None of these endpoints compile a document or count against your monthly quota. All require an API key.

Batch Generation

Generate many PDFs from a single LaTeX template in one API call. Supply your template and data rows — FormaTeX compiles each row concurrently and returns a ZIP archive containing all PDFs plus a manifest.json summary.

Typical uses: personalised certificates, invoices, letters, report cards, conference badges, mail-merge documents.

  • Up to 50 rows per request
  • Up to 5 concurrent compilations (auto-throttled)
  • Per-row timeout: 30 s
  • Overall request timeout: 4 min
  • Rate limit: 10 requests per minute

ZIP Archive Format

The returned ZIP always contains:

  • manifest.json — compilation summary (always the first entry)
  • <filename>.pdf — one PDF per successful row, named by the filename template
json
{
  "total": 3,
  "success": 2,
  "failed": 1,
  "results": [
    { "index": 0, "filename": "report-Martin-1.pdf", "success": true },
    { "index": 1, "filename": "report-Chen-2.pdf",   "success": true },
    { "index": 2, "filename": "report-López-3.pdf",  "success": false, "error": "Undefined control sequence \\badmacro" }
  ]
}

Duplicate filenames after template rendering are automatically deduplicated by appending -2, -3, etc.

LaTeX Escaping

By default all substituted values are LaTeX-escaped, so special characters in your data are safe:

text
&  →  \&                   %  →  \%
$  →  \$                   #  →  \#
_  →  \_                   {  →  \{
}  →  \}                   ~  →  \textasciitilde{}
^  →  \textasciicircum{}   \  →  \textbackslash{}

Use {{field | raw}} to suppress escaping when the field already contains valid LaTeX markup.

Conversions & PDF Tools

Convert LaTeX documents to other formats and manipulate existing PDFs. All endpoints require an API key.

Developer Tooling

Static tooling endpoints for LaTeX. None of these count against your monthly compilation quota. All require an API key.

Templates

Parameterized LaTeX documents with <<variable>> placeholders. Send data, get PDF — no LaTeX knowledge required for your API consumers.

How it works

  1. Browse the template library or create your own
  2. Send a compile request with variable values
  3. Receive a compiled PDF with all placeholders substituted

Variable schema

FieldTypeDescription
keystringVariable name used in placeholders
typestring | number | dateType validation applied to the value
requiredbooleanWhether the variable must be provided
defaultstringDefault value if not provided
descriptionstringHuman-readable description

Quick example

bash
curl -X POST https://api.formatex.io/api/v1/templates/cover-letter/compile \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"variables": {"name": "Alice Martin", "company": "Acme Corp"}}' \
  --output cover-letter.pdf

Without the Accept: application/json header, the response is a raw PDF binary.

Public Templates

Curated templates available to all users. No authentication required to list or view. Compilation requires an API key.

User Templates

Create and manage your own parameterized templates. Requires JWT authentication. Available on Pro plan and above.

Plans

View available plans and their limits. Public endpoint.

Plan Comparison

FeatureFreeProMaxEnterprise
Compilations/mo155002,00015,000
Timeout30s120s300s300s
Max Size1 MB10 MB25 MB50 MB
Compilation Runs13510
API Keys251550
EnginesAll 2All 4All 4All 4
At LimitHard blockSoft limitSoft limitSoft limit

Billing

Subscription management via Polar.sh. Requires JWT authentication.

Users

Manage your profile and view usage statistics. All endpoints require JWT.

Webhooks

Register HTTP callbacks to receive real-time notifications on compilation, quota, and plan events. Requires JWT. Failed deliveries are automatically retried up to 5 times with exponential backoff.

EventTrigger
compilation.successCompilation completed successfully
compilation.failedCompilation failed (LaTeX error or timeout)
quota.warningUsage reached 80% of monthly quota
quota.exceededUsage reached 100% — compilations are blocked
plan.changedPlan upgraded, downgraded, or revoked

Event Payload

json
{
  "event": "compilation.success",
  "data": {
    "compilationId": "uuid",
    "engine": "pdflatex",
    "durationMs": 1240,
    "inputSizeBytes": 2048,
    "apiKeyPrefix": "a2e67d28...",
    "timestamp": "2026-02-20T14:30:00Z"
  }
}

If a secret is set, the payload is signed with HMAC-SHA256 delivered in the X-FormaTeX-Signature header.

MCP Server

Compile LaTeX directly from AI agents — Claude Desktop, Cursor, Claude Code, n8n, and more — with zero local TeX Live installation. Uses your FormaTeX API key for authentication.

Available Tools

PDF output: Compiled PDFs are returned as base64 text in the tool result. Ask your agent to decode and save it: “Save the PDF to document.pdf”. Claude Code writes the file directly; Claude Desktop provides a one-liner decode script.

fix_latex

Prompt

Analyzes compilation errors, finds root causes, and returns corrected LaTeX source. Pass it your LaTeX source and error log.

“Use the fix_latex prompt to help me fix this error: [paste error log]”

Setup

How it works

Connect via URL to the hosted MCP server — no binary download. Ideal for teams sharing one MCP server, no-code platforms (n8n, Make.com), and cloud-based AI agents.

Hosted server

https://mcp.formatex.io/mcp

Configure your AI client

No binary needed — just add the URL and your API key:

json
{
  "mcpServers": {
    "formatex": {
      "url": "https://mcp.formatex.io/mcp",
      "headers": {
        "Authorization": "Bearer fex_your_api_key_here"
      }
    }
  }
}

Health check

bash
curl https://mcp.formatex.io/health
# → {"status":"ok","service":"formatex-mcp","version":"1.0.0"}

Troubleshooting

CLI

Compile LaTeX documents, watch for changes, format source files, and convert between formats — all from the terminal, with no local TeX Live installation.

bash
formatex compile thesis.tex

Installation

macOS / Linux

bash
curl -fsSL https://raw.githubusercontent.com/formatexio/cli/main/install.sh | sh

macOS (Homebrew)

bash
brew install formatexio/tap/formatex

Windows (PowerShell)

powershell
iwr https://raw.githubusercontent.com/formatexio/cli/main/install.ps1 | iex

Go

bash
go install github.com/formatexio/cli@latest

Or download a pre-built binary from the releases page for Linux, macOS, or Windows (amd64 and arm64).

Authentication

bash
formatex login

Enter your API key when prompted — it will be verified and saved to ~/.config/formatex/config.json. You can also set FORMATEX_API_KEY as an environment variable, or pass --api-key to any command.

Commands

formatex compile

Compile a .tex file or .zip project to PDF.

bash
formatex compile thesis.tex
formatex compile thesis.tex --engine xelatex --output dist/thesis.pdf
formatex compile project.zip --main chapters/main.tex
FlagDefaultDescription
--engine, -epdflatexpdflatex, xelatex, lualatex, latexmk
--output, -o<input>.pdfOutput PDF path
--runsautoCompiler passes (1–5)
--timeoutplan defaultMax compile time in seconds
--mainauto-detectedEntry-point .tex inside a ZIP
--openfalseOpen PDF after compilation

On failure, structured errors are printed with file, line number, and AI explanation (if enabled on your plan):

text
Compilation failed.

Errors:
  main.tex:42: Undefined control sequence \foo

AI: The command \foo is not defined. You may have meant \footnote,
    or add \newcommand{\foo}{...} to your preamble.

formatex watch

Watch a .tex file and recompile on every save.

bash
formatex watch thesis.tex
formatex watch thesis.tex --engine xelatex --open
FlagDefaultDescription
--engine, -epdflatexLaTeX engine
--output, -o<input>.pdfOutput PDF path
--runsautoNumber of compiler passes
--timeoutplan defaultMax compile time in seconds
--openfalseOpen PDF on first successful compile

Uses filesystem events (not polling) for instant detection. Debounced at 300ms to handle editor swap files.

formatex format

Format a LaTeX source file using latexindent.

bash
formatex format main.tex           # print to stdout
formatex format main.tex --write   # overwrite in-place
formatex format main.tex -o out.tex
FlagDefaultDescription
--write, -wfalseOverwrite the source file in-place
--output, -oWrite formatted output to a different file

formatex convert

Convert a LaTeX document to another format.

bash
formatex convert thesis.tex                    # → thesis.docx
formatex convert thesis.tex --format html      # → thesis.html
formatex convert thesis.tex --format odt
FlagDefaultDescription
--format, -fdocxdocx, html, odt, epub, markdown, txt
--output, -o<input>.<ext>Output file path

Global flags

FlagDescription
--api-keyAPI key (overrides config and env var)
--base-urlOverride the API base URL

CI / CD

Set FORMATEX_API_KEY as a secret and call the CLI directly:

yaml
- name: Compile LaTeX
  run: formatex compile thesis.tex --output dist/thesis.pdf
  env:
    FORMATEX_API_KEY: ${{ secrets.FORMATEX_API_KEY }}

For a dedicated GitHub Actions integration with artifact upload, see the FormaTeX GitHub Action.

Python SDK

The official Python client for the FormaTeX API. Supports compilation, rendering, conversion, linting, PDF tools, async jobs, and more.

Installation

bash
pip install formatex

Requires Python 3.9+.

Quick start

python
from formatex import FormaTexClient

client = FormaTexClient(api_key="fex_your_key_here")

result = client.compile(r"""
\documentclass{article}
\begin{document}
Hello, FormaTeX!
\end{document}
""")

with open("output.pdf", "wb") as f:
    f.write(result.pdf)

FormaTexClient

python
FormaTexClient(
    api_key: str,         # required — get yours at formatex.io/dashboard/api-keys
    *,
    timeout: float = 120.0,
)

Use as a context manager to close the underlying HTTP session automatically:

python
with FormaTexClient(api_key="fex_...") as client:
    result = client.compile(latex)

Compilation

compile

python
result = client.compile(
    latex: str,
    *,
    engine: str = "pdflatex",   # pdflatex | xelatex | lualatex | latexmk
    runs: int = 0,               # 0 = auto (1–5, capped to plan)
    timeout: int = 0,            # 0 = plan default
    files: list[dict] | None = None,  # companion files (images, .bib, etc.)
)
# → CompileResult(pdf: bytes, engine: str, duration_ms: int, size_bytes: int, job_id: str, log: str)

Attach companion files using the file_entry helper:

python
from formatex import FormaTexClient, file_entry
from pathlib import Path

result = client.compile(latex, files=[
    file_entry("logo.png", Path("assets/logo.png").read_bytes()),
    file_entry("refs.bib", Path("refs.bib").read_bytes()),
])

compile_smart

Automatic engine detection with an 18-step auto-fix pipeline. Pass engine="auto" or omit it.

python
result = client.compile_smart(latex, engine="auto")

compile_zip

Compile a multi-file project from a ZIP archive.

python
zip_bytes = Path("project.zip").read_bytes()
result = client.compile_zip(
    zip_bytes,
    main="chapters/main.tex",  # optional — auto-detected if omitted
    engine="xelatex",
)

compile_ipynb

Compile a Jupyter notebook to PDF.

python
nb_bytes = Path("notebook.ipynb").read_bytes()
result = client.compile_ipynb(nb_bytes, engine="pdflatex")

Markup sources

python
# Compile from Markdown
result = client.compile_markdown(markdown_str)

# Compile from HTML
result = client.compile_html(html_str)

# Compile from reStructuredText
result = client.compile_rst(rst_str)

Async compilation

For long-running documents, submit a job and poll for completion.

python
# Submit
job = client.async_compile(latex, engine="lualatex")
print(job.job_id, job.status)  # pending

# Poll manually
result_info = client.get_job(job.job_id)

# Or wait with exponential back-off (recommended)
result = client.wait_for_job(job.job_id, poll_interval=2, max_wait=300)
pdf_bytes = result.pdf

Rendering

python
# Render a LaTeX equation to PNG/SVG
img = client.render_equation(
    r"E = mc^2",
    format="png",   # png | svg
    dpi=150,
    display=True,   # display mode (vs inline)
)
with open("eq.png", "wb") as f:
    f.write(img.image)

# Batch render multiple equations
results = client.render_equations([
    {"latex": r"E = mc^2", "format": "png"},
    {"latex": r"\int_0^\infty e^{-x}\,dx", "format": "svg"},
])

Conversion

python
result = client.convert(
    latex,
    format="docx",  # docx | html | odt | epub | markdown | txt
)
with open("thesis.docx", "wb") as f:
    f.write(result.file)

Linting

python
lint = client.lint(latex)
print(lint.error_count, lint.warning_count)
for d in lint.diagnostics:
    print(f"{d.line}:{d.column} [{d.severity}] {d.message}")

PDF tools

python
# Extract text from page 0
extracted = client.pdf_extract(pdf_bytes, page=0)
print(extracted.text)

# Render pages as images
pages = client.pdf_pages(pdf_bytes, dpi=150)

# Compress
compressed = client.pdf_compress(pdf_bytes)

# Merge
merged = client.pdf_merge([pdf_a, pdf_b, pdf_c])

# Split into individual pages
split = client.pdf_split(pdf_bytes)

# Convert to PDF/A
pdfa = client.pdf_pdfa(pdf_bytes)

Document analysis

python
# Word count
wc = client.word_count(latex)

# Extract \usepackage dependencies
deps = client.extract_dependencies(latex)

# Check which packages are available on the server
status = client.check_packages(["tikz", "pgfplots", "fontawesome5"])

# Extract title, author, date, etc.
meta = client.extract_metadata(latex)

# Analyse a .bib file
bib = client.analyse_bibliography(bib_str)

Error handling

python
from formatex.exceptions import FormaTexError, CompilationError, RateLimitError

try:
    result = client.compile(latex)
except CompilationError as e:
    print("LaTeX errors:", e.log)
except RateLimitError:
    print("Rate limit hit — back off and retry")
except FormaTexError as e:
    print("API error:", e)

Node.js SDK

The official Node.js client for the FormaTeX API. Fully typed with TypeScript. Supports compilation, rendering, conversion, linting, PDF tools, async jobs, and more.

Installation

bash
npm install formatex-sdk
# or
pnpm add formatex-sdk

Requires Node.js 18+.

Quick start

ts
import { FormaTexClient } from "formatex-sdk";

const client = new FormaTexClient("fex_your_key_here");

const result = await client.compile(`
  \\documentclass{article}
  \\begin{document}
  Hello, FormaTeX!
  \\end{document}
`);

await fs.writeFile("output.pdf", result.pdf);

new FormaTexClient(apiKey, options?)

ts
const client = new FormaTexClient(
  apiKey: string,                  // required
  options?: FormaTexClientOptions, // { baseUrl?, timeout? }
);

Compilation

compile

ts
const result = await client.compile(latex: string, options?: CompileOptions);
// CompileOptions: { engine?, runs?, timeout?, files? }
// Returns: CompileResult { pdf: Buffer, engine, durationMs, sizeBytes, jobId, log }

Attach companion files using the fileEntry helper:

ts
import { FormaTexClient, fileEntry } from "formatex-sdk";
import { readFileSync } from "fs";

const result = await client.compile(latex, {
  engine: "xelatex",
  files: [
    fileEntry("logo.png", readFileSync("assets/logo.png")),
    fileEntry("refs.bib", readFileSync("refs.bib")),
  ],
});

compileSmart

Automatic engine detection with an 18-step auto-fix pipeline.

ts
const result = await client.compileSmart(latex, { engine: "auto" });

compileZip

Compile a multi-file project from a ZIP archive.

ts
const zip = readFileSync("project.zip");
const result = await client.compileZip(zip, {
  main: "chapters/main.tex", // optional
  engine: "xelatex",
});

compileIpynb

Compile a Jupyter notebook to PDF.

ts
const nb = readFileSync("notebook.ipynb");
const result = await client.compileIpynb(nb);

Markup sources

ts
// Compile from Markdown
const result = await client.compileMarkdown(markdownStr);

// Compile from HTML
const result = await client.compileHtml(htmlStr);

// Compile from reStructuredText
const result = await client.compileRst(rstStr);

Async compilation

ts
// Submit
const job = await client.asyncCompile(latex, { engine: "lualatex" });
console.log(job.jobId, job.status); // pending

// Wait with exponential back-off (recommended)
const result = await client.waitForJob(job.jobId, { maxWait: 300_000 });
await fs.writeFile("output.pdf", result.pdf);

// Or poll manually
const status = await client.getJob(job.jobId);
const pdf = await client.getJobPdf(job.jobId);

Rendering

ts
// Render a LaTeX equation
const img = await client.renderEquation(r`E = mc^2`, {
  format: "png",     // "png" | "svg"
  dpi: 150,
  display: true,
});
await fs.writeFile("eq.png", img.image);

// Batch render
const results = await client.renderEquations([
  { latex: r`E = mc^2`, format: "png" },
  { latex: r`\int_0^\infty e^{-x}\,dx`, format: "svg" },
]);

Conversion

ts
const result = await client.convert(latex, { format: "docx" });
// format: "docx" | "html" | "odt" | "epub" | "markdown" | "txt"
await fs.writeFile("thesis.docx", result.file);

Linting

ts
const lint = await client.lint(latex);
console.log(lint.errorCount, lint.warningCount);
for (const d of lint.diagnostics) {
  console.log(`${d.line}:${d.column} [${d.severity}] ${d.message}`);
}

PDF tools

ts
// Extract text from a page
const extracted = await client.pdfExtract(pdfBuffer, 0);
console.log(extracted.text);

// Render pages as images
const pages = await client.pdfPages(pdfBuffer, { dpi: 150 });

// Compress
const compressed = await client.pdfCompress(pdfBuffer);

// Merge
const merged = await client.pdfMerge([pdfA, pdfB, pdfC]);

// Split into individual pages
const split = await client.pdfSplit(pdfBuffer);

// Convert to PDF/A
const pdfa = await client.pdfPDFA(pdfBuffer);

Document analysis

ts
// Word count
const wc = await client.wordCount(latex);

// Extract \usepackage dependencies
const deps = await client.extractDependencies(latex);

// Check which packages are available
const status = await client.checkPackages(["tikz", "pgfplots", "fontawesome5"]);

// Extract title, author, date, etc.
const meta = await client.extractMetadata(latex);

// Analyse a .bib file
const bib = await client.analyzeBibliography(bibStr);

Error handling

ts
import {
  FormaTexError,
  CompilationError,
  RateLimitError,
  AuthenticationError,
} from "formatex-sdk";

try {
  const result = await client.compile(latex);
} catch (e) {
  if (e instanceof CompilationError) {
    console.error("LaTeX errors:", e.log);
  } else if (e instanceof RateLimitError) {
    console.error("Rate limited — back off and retry");
  } else if (e instanceof AuthenticationError) {
    console.error("Invalid API key");
  } else if (e instanceof FormaTexError) {
    console.error("API error:", e.message);
  }
}

GitHub Actions

Two ways to compile LaTeX in your GitHub Actions workflows: the dedicated FormaTeX Action, or the CLI directly.

FormaTeX Action

The simplest integration. Compiles your document and uploads the PDF as a build artifact automatically.

yaml
name: Compile LaTeX

on:
  push:
    branches: [main]
  pull_request:

jobs:
  compile:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: formatexio/action@v1
        with:
          file: thesis.tex
          engine: pdflatex        # optional — default: pdflatex
          output: dist/thesis.pdf # optional — default: <file>.pdf
        env:
          FORMATEX_API_KEY: ${{ secrets.FORMATEX_API_KEY }}

      - uses: actions/upload-artifact@v4
        with:
          name: thesis-pdf
          path: dist/thesis.pdf

Set FORMATEX_API_KEY as a repository secret under Settings → Secrets and variables → Actions.

CLI in CI

Install the CLI in your workflow and call it directly for more control.

yaml
name: Compile LaTeX

on:
  push:
    branches: [main]

jobs:
  compile:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install FormaTeX CLI
        run: curl -fsSL https://raw.githubusercontent.com/formatexio/cli/main/install.sh | sh

      - name: Compile
        run: formatex compile thesis.tex --output dist/thesis.pdf
        env:
          FORMATEX_API_KEY: ${{ secrets.FORMATEX_API_KEY }}

      - uses: actions/upload-artifact@v4
        with:
          name: thesis-pdf
          path: dist/thesis.pdf

Multi-file projects

Pass a ZIP archive when your document spans multiple files:

yaml
- name: Archive project
  run: zip -r project.zip . -x "*.git*"

- name: Compile
  run: formatex compile project.zip --main chapters/main.tex --output dist/thesis.pdf
  env:
    FORMATEX_API_KEY: ${{ secrets.FORMATEX_API_KEY }}

PR preview comments

Post a link to the compiled PDF as a pull request comment:

yaml
- name: Compile
  id: compile
  run: formatex compile thesis.tex --output dist/thesis.pdf
  env:
    FORMATEX_API_KEY: ${{ secrets.FORMATEX_API_KEY }}

- uses: actions/upload-artifact@v4
  id: upload
  with:
    name: thesis-pdf
    path: dist/thesis.pdf

- name: Comment on PR
  if: github.event_name == 'pull_request'
  uses: actions/github-script@v7
  with:
    script: |
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: `PDF compiled successfully. [Download artifact](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`,
      })

Matrix builds (multiple engines)

yaml
strategy:
  matrix:
    engine: [pdflatex, xelatex, lualatex]

steps:
  - uses: actions/checkout@v4
  - name: Compile with ${{ matrix.engine }}
    run: formatex compile thesis.tex --engine ${{ matrix.engine }} --output dist/thesis-${{ matrix.engine }}.pdf
    env:
      FORMATEX_API_KEY: ${{ secrets.FORMATEX_API_KEY }}

Ready to start compiling?

Get your API key in 30 seconds. 15 free compilations every month.

One quick thing

We track anonymous usage — page views, feature usage, compilation events — to understand what works and what doesn't. No ads, no personal data, no third-party sharing.

Cookie policy