Inkspan › Guides › How to generate a PDF from a template in PHP
Call the Inkspan /v1/generate/template endpoint from PHP 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.
Set CURLOPT_RETURNTRANSFER so the body comes back as a string rather than printing. Always check the HTTP status before writing the file.
<?php
$ch = curl_init('https://api.inkspan.dev/v1/generate/template');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . getenv('INKSPAN_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{"template": "<h1>{{ name }}</h1>", "data": {"name": "Ada"}}',
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code !== 200) { throw new Exception($body); }
file_put_contents('out.pdf', $body);
| Name | Type | Required | Description |
|---|---|---|---|
template | string | yes | A Jinja2 template. Supports {{ variables }}, {% for %} loops, {% if %} conditionals, and filters. |
data | object | yes | JSON object whose top-level keys become template variables. |
filename | string | no | Filename in the response's Content-Disposition. |
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.
Read the key with getenv() rather than hardcoding it, and keep it out of version control.
{% for i in items %}…{% endfor %}, not {{#each}}.data become variables. Passing {"data": {"invoice": {...}}} means you reference {{ invoice.number }}, not {{ number }}.| Status | Meaning | Fix |
|---|---|---|
401 | Invalid or missing API key | Send your key in the X-API-Key header. |
402 | Monthly quota exceeded | The response reports used/quota. Upgrade at inkspan.dev/#pricing. |
422 | Missing or invalid parameter | Check 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: Next.js · Django · Laravel · Ruby on Rails · All guides