Inkspan › Guides › How to generate a PDF from a template in Next.js

How to generate a PDF from a template in Next.js

Call the Inkspan /v1/generate/template endpoint from Next.js to generate a PDF from a template — no headless browser, font stack, or rendering infrastructure in your deploy. Keep your API key in the environment and POST.

Run this in a Route Handler rather than a Server Component — you need to stream a binary response, and the key must stay server-side. Set INKSPAN_KEY in your environment; a NEXT_PUBLIC_ prefix would ship it to the browser.

Next.js example

// app/api/pdf/route.ts
export async function POST() {
  const res = await fetch('https://api.inkspan.dev/v1/generate/template', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.INKSPAN_KEY!,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({"template": "<h1>{{ name }}</h1>", "data": {"name": "Ada"}}),
  });

  if (!res.ok) return new Response(await res.text(), { status: res.status });

  return new Response(await res.arrayBuffer(), {
    headers: { 'Content-Type': 'application/pdf' },
  });
}

Request parameters

NameTypeRequiredDescription
templatestringyesA Jinja2 template. Supports {{ variables }}, {% for %} loops, {% if %} conditionals, and filters.
dataobjectyesJSON object whose top-level keys become template variables.
filenamestringnoFilename in the response's Content-Disposition.

Response

Returns a PDF as a binary stream with Content-Type: application/pdf and Content-Disposition: attachment; filename=document.pdf. Write the response body straight to disk — do not parse it as JSON.

Production notes

Route Handlers on Vercel have an execution time limit. For large documents, generate in a background job and return a URL rather than blocking the request.

Errors

StatusMeaningFix
401Invalid or missing API keySend your key in the X-API-Key header.
402Monthly quota exceededThe response reports used/quota. Upgrade at inkspan.dev/#pricing.
422Missing or invalid parameterCheck required fields in the table above — the body names the offending field.

Failed requests do not count against your quota; only successful ones are metered.

Get a free API key →

Same operation in cURL · Python · Node.js · Other frameworks: Django · Laravel · Ruby on Rails · PHP · All guides