Templates API

List your templates, generate and merge PDFs, render raw HTML, and check your usage.

Base URL: https://pdfstork.com/api/v1

All endpoints require an Authorization: Bearer header. See Authentication. Each PDF costs one credit; see Rate Limits & Quotas.

List templates

Endpointbash
GET /api/v1/templates

Returns the templates of the token's team, 50 per page. Pass ?page=2 to get the next page. The type is editor for a template made with the WYSIWYG editor and html for an HTML template.

Requestbash
curl https://pdfstork.com/api/v1/templates \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
Responsejson
{
  "data": [
    {
      "id": 123,
      "name": "Invoice",
      "type": "editor",
      "has_base_pdf": true,
      "created_at": "2026-03-15T10:00:00+00:00",
      "updated_at": "2026-03-28T14:30:00+00:00"
    },
    {
      "id": 456,
      "name": "Contract",
      "type": "html",
      "has_base_pdf": false,
      "created_at": "2026-03-20T09:00:00+00:00",
      "updated_at": "2026-03-20T09:00:00+00:00"
    }
  ],
  "links": {
    "first": "https://pdfstork.com/api/v1/templates?page=1",
    "last": "https://pdfstork.com/api/v1/templates?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 50,
    "total": 2
  }
}

Get a template

Endpointbash
GET /api/v1/templates/{template_id}

Returns one template, with the same fields as the list.

Responsejson
{
  "data": {
    "id": 123,
    "name": "Invoice",
    "type": "editor",
    "has_base_pdf": true,
    "created_at": "2026-03-15T10:00:00+00:00",
    "updated_at": "2026-03-28T14:30:00+00:00"
  }
}

Generate PDF

Endpointbash
POST /api/v1/templates/{template_id}/generate

Generates a PDF from the published version of the template, injecting the provided data. Costs one credit.

Request body

The body must be a JSON object with a data object. An empty body is refused with a 422 (The request body is empty…), and so is a data that is not an object (The data field must be an object.).

FieldTypeDefaultDescription
dataobject{}Key-value pairs for template variables
outputstring"base64"base64, url or binary
filenamestring"document.pdf"Output filename. Letters, numbers, dashes, underscores and dots only
url_expiryinteger60URL validity in minutes, from 1 to 1440. Only used with output: "url". A value outside this range is refused with a 422
metadataobjectNoneDocument properties to write into the PDF: title, author, subject, keywords, creator. Each overrides the template's own value and may use {{ variables }}. Basic plan and above
Requestbash
curl -X POST https://pdfstork.com/api/v1/templates/123/generate \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "data": {
      "customer_name": "Acme Corp",
      "invoice_number": "INV-2026-042"
    },
    "output": "base64",
    "filename": "invoice-042.pdf"
  }'

Response (base64)

The usage object already counts the PDF you just generated. percentage is the share of the monthly quota used, from 0 to 100, and pack_credits the prepaid credits left.

200 OKjson
{
  "success": true,
  "pdf": "JVBERi0xLjQKJeLj...",
  "filename": "invoice-042.pdf",
  "size": 12345,
  "usage": {
    "current": 42,
    "limit": 5000,
    "percentage": 0.8,
    "pack_credits": 0
  }
}

Response (url)

200 OKjson
{
  "success": true,
  "url": "https://de-s3.storage.bunnycdn.com/pdfapi-pdfs/pdfs/abc123.pdf?X-Amz-Signature=...",
  "filename": "invoice-042.pdf",
  "expires_at": "2026-03-30T11:00:00+00:00",
  "size": 12345,
  "usage": {
    "current": 42,
    "limit": 5000,
    "percentage": 0.8,
    "pack_credits": 0
  }
}

Response (binary)

Returns raw PDF bytes with Content-Type: application/pdf. Ask for it with "output": "binary" in the body: an Accept: application/pdf header alone does not change the output format.

Merge PDFs

Endpointbash
POST /api/v1/templates/merge

Generates several documents and merges them into one PDF, in the order given. Each template can receive its own data. Up to 50 documents per request. A merge of N documents costs N credits. Pro plan only.

FieldTypeDefaultDescription
templatesarrayRequiredThe documents to merge, each as {"id": 123, "data": {…}}
outputstring"base64"base64, url or binary
filenamestring"document.pdf"Output filename. Letters, numbers, dashes, underscores and dots only
url_expiryinteger60URL validity in minutes, from 1 to 1440. Only used with output: "url". A value outside this range is refused with a 422
metadataobjectNoneDocument properties of the merged file (title, author, subject, keywords, creator). Placeholders are filled from the first document's data. Without it, the merged file keeps the properties of its first document
Requestbash
curl -X POST https://pdfstork.com/api/v1/templates/merge \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "templates": [
      { "id": 123, "data": { "customer_name": "Acme Corp" } },
      { "id": 456, "data": { "customer_name": "Acme Corp" } }
    ],
    "output": "url",
    "filename": "acme-bundle.pdf"
  }'

The response has the same shape as the generate endpoint for the chosen output.

HTML to PDF

Endpointbash
POST /api/v1/html-to-pdf

Renders raw HTML without a saved template. The HTML may use the same Twig syntax as the HTML editor, filled from data. Costs one credit.

FieldTypeDefaultDescription
htmlstringRequiredThe HTML to render
dataobject{}Values for the Twig variables
configobject{}Page settings: format (default A4), orientation (portrait or landscape), margins (top, right, bottom, left in mm, 10 by default), header, footer (HTML) and metadata
outputstring"base64"base64, url or binary
filenamestring"document.pdf"Output filename. Letters, numbers, dashes, underscores and dots only
url_expiryinteger60URL validity in minutes, from 1 to 1440. Only used with output: "url". A value outside this range is refused with a 422
Requestbash
curl -X POST https://pdfstork.com/api/v1/html-to-pdf \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "html": "<h1>Hello {{ name }}</h1>",
    "data": { "name": "Jane Doe" },
    "config": {
      "format": "A4",
      "orientation": "portrait",
      "margins": { "top": 20, "right": 15, "bottom": 20, "left": 15 },
      "footer": "<div style=\"font-size: 9px\">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span></div>"
    },
    "output": "base64"
  }'

The response has the same shape as the generate endpoint for the chosen output.

Check usage

Endpointbash
GET /api/v1/usage

Returns your current monthly usage and quota information, wrapped in a data object. has_quota_remaining stays true past the quota as long as prepaid credits are left, and plan is discovery, basic or pro. daily_usage covers the last 31 days.

Requestbash
curl https://pdfstork.com/api/v1/usage \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
Responsejson
{
  "data": {
    "current_month_usage": 42,
    "quota_limit": 5000,
    "quota_percentage": 0.8,
    "pack_credits": 0,
    "has_quota_remaining": true,
    "plan": "basic",
    "daily_usage": [
      { "date": "2026-03-28", "count": 15 },
      { "date": "2026-03-29", "count": 12 },
      { "date": "2026-03-30", "count": 15 }
    ],
    "overage_count": 0
  }
}

Error responses

StatusDescription
401Missing or invalid API token
402Monthly quota and prepaid credits both used up. The body gives the usage and a purchase_url
403The template belongs to another account, the account's email address is not verified, or the request uses a feature your plan does not include (the body gives required_plan)
404Template not found, for example {"message": "Template 999 not found."}
422Validation error (see the errors object), or a Twig error in an HTML template. No credit is consumed
429Rate limit exceeded (60 requests per minute per team)
500PDF generation failed
Validation error examplejson
{
  "message": "The output must be one of: base64, url, binary.",
  "errors": {
    "output": [
      "The output must be one of: base64, url, binary."
    ]
  }
}

A template whose Twig does not compile is refused before rendering. The response names the part (template, header or footer) and the line:

Twig error examplejson
{
  "message": "Twig error in the template, line 2: missing endif (the if opened here is never closed)",
  "part": "template",
  "line": 2,
  "errors": {
    "template": [
      "Twig error in the template, line 2: missing endif (the if opened here is never closed)"
    ]
  }
}
Validate before you ship
The HTML editor checks your Twig as you type and shows the failing line, so most of these errors never reach the API.