FormaTeX

\begin{article}

draft — not published

LaTeX API Authentication Best Practices

Secure your LaTeX compilation API integration — manage API keys safely, rotate credentials, and avoid common authentication mistakes.

·9 min read·
LaTeX API Authentication Best Practices

Every FormatEx integration starts with an API key. That key is a bearer credential — whoever holds it can compile documents against your account, consume your monthly quota, and in the wrong hands, rack up overages or expose your pipeline to abuse. Getting LaTeX API authentication right from day one costs almost nothing; cleaning up after a leaked key does not.

This guide covers the full lifecycle: creating scoped keys, storing them safely, rotating them without downtime, detecting leaks early, and integrating with secrets managers for production deployments.

How FormatEx API Key Authentication Works

FormatEx uses a simple, stateless scheme. Every request to the compile endpoint must include your API key in the X-API-Key header:

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

Under the hood, FormatEx stores a SHA-256 hash of your key — the raw value is shown only once at creation time and never stored in plaintext. If you lose it, you create a new one. There is no "view key" button.

The key prefix (fex_live_) lets you identify production keys at a glance in logs or incident reports, without exposing the secret portion. Remember this — it matters when you set up automated leak detection.

Creating and Scoping API Keys

The FormatEx dashboard lets you create multiple keys per account (limit depends on your plan: 2 on Free, up to 50 on Scale). Use this to scope keys by environment and purpose rather than sharing one key everywhere.

Recommended key topology:

Key nameEnvironmentUsed by
ci-pdflatexCI/CD pipelineGitHub Actions, test suite
staging-apiStaging serverYour staging backend
prod-apiProductionYour production backend
local-devDeveloper laptopsIndividual devs (rotate frequently)

Never share a production key with a CI pipeline. If your CI config is ever exposed (and they are — misconfigured public repos happen), your production quota and any soft-block plans are immediately at risk. Separate keys mean a compromised CI key affects only CI.

When you create a key in the dashboard (Dashboard → API Keys → New Key), copy it immediately into your secrets store. Do not paste it into Slack, a notes app, or a terminal with scrollback logging.

Storing API Keys Safely

The single most common mistake is hardcoding a key directly in source code:

typescript
// NEVER do this
const client = new FormatExClient({
  apiKey: "fex_live_abc123xyz789...",
});

That string will end up in your git history forever, visible to anyone with repo access — including future contributors, contractors, and anyone who ever gets read access to the repo. Deleting the line in a follow-up commit does not remove it from history.

The correct approach: environment variables.

typescript
// src/lib/formatex.ts
const API_KEY = process.env.FORMATEX_API_KEY;

if (!API_KEY) {
  throw new Error("FORMATEX_API_KEY environment variable is not set");
}

export async function compileLaTeX(latex: string, engine = "pdflatex") {
  const response = await fetch("https://api.formatex.io/api/v1/compile", {
    method: "POST",
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ latex, engine }),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Compilation failed: ${error.error}`);
  }

  return response.arrayBuffer();
}

For local development, use a .env.local file (never committed) with a dedicated local key. Add .env* to your .gitignore before you write the first line:

bash
# .env.local
FORMATEX_API_KEY=fex_live_your_local_dev_key_here
bash
# .gitignore
.env
.env.local
.env*.local

In Python projects, the same pattern applies via python-dotenv or your framework's built-in env loading:

python
import os
import httpx
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.environ["FORMATEX_API_KEY"]  # Raises KeyError if missing — good

def compile_latex(latex: str, engine: str = "pdflatex") -> bytes:
    response = httpx.post(
        "https://api.formatex.io/api/v1/compile",
        headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
        json={"latex": latex, "engine": engine},
        timeout=120.0,
    )
    response.raise_for_status()
    return response.content

Note the explicit raise_for_status() — never silently swallow HTTP errors from an authentication-gated API.

Rotating Keys Without Downtime

Keys should be rotated periodically — quarterly at minimum, immediately after any suspected exposure. The zero-downtime rotation procedure for FormatEx is straightforward because you can hold multiple active keys simultaneously:

  1. Create a new key in the dashboard with the same name convention (e.g., prod-api-2026-q3).
  2. Deploy your application with the new key value in your secrets store — the old key is still active, so in-flight requests complete normally.
  3. Verify the new key is working by checking compilation logs in the dashboard.
  4. Revoke the old key from the dashboard once traffic has fully shifted.

For automated rotation with AWS Secrets Manager, you can script steps 1 and 4 via the FormatEx dashboard API (when available) or handle them manually on a calendar reminder. The key point is that revocation happens after the new key is live, not before.

bash
# Example: update the key in an existing AWS Secrets Manager secret
aws secretsmanager put-secret-value \
  --secret-id prod/formatex/api-key \
  --secret-string '{"FORMATEX_API_KEY":"fex_live_new_key_here"}'

Your application should read the secret at startup (or refresh it periodically), not bake it into a container image or deployment artifact.

Using Secrets Managers in Production

For production deployments, environment variables in a .env file are not enough. A secrets manager provides audit logs, access control, versioning, and automatic rotation hooks. Here are the three most common options:

AWS Secrets Manager

python
import boto3
import json

def get_formatex_key() -> str:
    client = boto3.client("secretsmanager", region_name="us-east-1")
    secret = client.get_secret_value(SecretId="prod/formatex/api-key")
    return json.loads(secret["SecretString"])["FORMATEX_API_KEY"]

HashiCorp Vault

bash
# Store the key
vault kv put secret/formatex/prod api_key=fex_live_...

# Retrieve at deploy time
export FORMATEX_API_KEY=$(vault kv get -field=api_key secret/formatex/prod)

GitHub Actions (for CI pipelines)

Store the key as a repository or organization secret in GitHub (Settings → Secrets and variables → Actions), then reference it in your LaTeX compilation workflow in CI/CD:

yaml
- name: Compile LaTeX documents
  env:
    FORMATEX_API_KEY: ${{ secrets.FORMATEX_API_KEY_CI }}
  run: |
    curl -X POST https://api.formatex.io/api/v1/compile \
      -H "X-API-Key: $FORMATEX_API_KEY" \
      -H "Content-Type: application/json" \
      -d @payload.json \
      --output output.pdf

GitHub Actions secrets are masked in logs — if you accidentally echo $FORMATEX_API_KEY, GitHub replaces it with ***. This is a useful safety net but not a substitute for not logging keys in the first place.

Detecting and Responding to Leaked Keys

Leaked API keys are more common than most teams expect. A single accidental git push of a .env file, a Slack paste, or a verbose error log can expose a credential. You need a detection strategy, not just a prevention strategy. Understanding how FormatEx sandboxes and secures the compilation environment also helps you reason about the full attack surface.

Set up automated scanning:

  • GitHub secret scanning — free for public repos, available on GitHub Advanced Security for private repos. Add a custom pattern for fex_live_[A-Za-z0-9]{20,} to catch FormatEx keys specifically.
  • truffleHog — scans git history, not just the current HEAD. Run it as a pre-push hook or in CI.
  • gitleaks — another git-history scanner with a broad default ruleset.
bash
# Run truffleHog against your repo before pushing
trufflehog git file://. --only-verified

# Or as a pre-push git hook
echo 'trufflehog git file://. --only-verified' >> .git/hooks/pre-push
chmod +x .git/hooks/pre-push

When a key is confirmed leaked, act immediately:

  1. Revoke the key in the FormatEx dashboard right now — do not wait.
  2. Check your compilation logs (Dashboard → Usage) for unexpected usage in the past 24—72 hours.
  3. Rotate all keys in the same environment (assume lateral exposure).
  4. Find and remove the leaked credential from all locations (git history via git filter-repo, Slack message deletion, log scrubbing).
  5. Conduct a brief post-mortem to close the gap that allowed the leak.

Revoking a key in the dashboard takes effect immediately — any subsequent request using that key returns a 401.

Authentication Error Handling

Robust integrations handle auth errors explicitly rather than crashing or silently failing. For a complete reference of every status code the API can return, see the LaTeX API error codes guide:

typescript
type CompileError = {
  error: string;
};

async function compilePDF(latex: string): Promise<ArrayBuffer> {
  const response = await fetch("https://api.formatex.io/api/v1/compile", {
    method: "POST",
    headers: {
      "X-API-Key": process.env.FORMATEX_API_KEY ?? "",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ latex, engine: "pdflatex" }),
  });

  if (response.status === 401) {
    // Key is missing, invalid, or revoked
    throw new Error(
      "FormatEx authentication failed. Check FORMATEX_API_KEY in your environment."
    );
  }

  if (response.status === 403) {
    // Key is valid but plan limit exceeded or engine not allowed
    const body: CompileError = await response.json();
    throw new Error(`FormatEx authorization error: ${body.error}`);
  }

  if (!response.ok) {
    const body: CompileError = await response.json();
    throw new Error(`Compilation failed (${response.status}): ${body.error}`);
  }

  return response.arrayBuffer();
}

A 401 means your key is wrong or missing. A 403 means your key is valid but your plan does not permit the requested operation — for example, using the xelatex engine on a Free plan (which only supports pdflatex). These are distinct failure modes that warrant different handling. For production resilience beyond auth errors, pair this with exponential backoff and retry logic for rate limit responses.

Summary

Getting LaTeX API authentication right comes down to a handful of habits applied consistently:

  • One key per environment — never reuse a production key in CI or development
  • Environment variables only — no hardcoded keys, ever, anywhere in source
  • Secrets managers for production — AWS Secrets Manager, Vault, or equivalent
  • Automated leak detection — truffleHog or gitleaks in CI, GitHub secret scanning enabled
  • Quarterly rotation as a calendar event — do not wait for an incident
  • Immediate revocation on any suspected leak — the dashboard revokes in seconds

FormatEx is designed to support this workflow: multiple concurrent keys, instant revocation, and key prefixes that make it easy to identify credential type in logs. The infrastructure is there — use it.


Ready to build a secure LaTeX compilation pipeline? Sign up for FormatEx and get 15 free compilations per month on the Free plan — no credit card required.

\end{article}

Back to blog

\related{posts}

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