Skip to Content
Create PDF from Template

Create PDF from Template

Send a POST request to this endpoint to render one of your templates into a PDF document, or to build one PDF from many documents with pages.

ℹ️

You will need to authenticate to use this API. See authentication.

ℹ️

This endpoint consumes 1 credit per 20 pages, rounded up: 1 to 20 pages cost 1 credit, 21 to 40 cost 2, up to 5 credits for 100 pages. A PDF longer than 100 pages is refused and costs nothing.

Request

POST https://api.ojo.so/v1/pdf/template

Request Body

ParameterTypeDescription
templateIdstringThe ID of the template to use. With pages, the default template, optional when every entry names its own or gives html
modifyobject(Optional) An object containing variables to modify in the template. With pages, shared by every template entry
pagesarray(Optional) The documents of one PDF, in order. See Documents from pages

Page size comes from a CSS @page rule in the template first, as described in Create PDF from HTML, then from the template’s saved size, then A4. A template variable works in @page too, so one template can serve both sizes:

@page { size: {{pageSize}}; }
curl --request POST \ --url https://api.ojo.so/v1/pdf/template \ --header 'Authorization: Bearer <API Key>' \ --header 'Content-Type: application/json' \ --data '{"templateId": "<Template ID>", "modify": {"customer": "Acme Inc.", "pageSize": "letter"}}'

Response

Everything went as planned and your PDF is generated.

{ "id": "pdf-id.pdf", "pdfUrl": "<content domain>/path/to/document.pdf", "pages": 1, "credits": 1, "createdAt": "2026-09-28T12:34:56Z" }

pdfUrl has no scheme. Prefix it with https:// to fetch the file.

Documents from pages

pages builds one PDF from up to 100 documents, merged in order. Each entry is a document that starts on a new page and prints at least one: a template with its own values, or HTML.

FieldTypeDescription
templateIdstring(Optional) The template for this entry. Defaults to the top-level templateId
htmlstring(Optional) HTML printed as is, with no variables. Give templateId or html, not both
modifyobject(Optional) Values for this entry

A template entry takes the template’s saved variables, then the top-level modify, then its own modify, each over the one before. A document longer than a page prints more pages. Each document has its own page size, by the rule above.

One certificate per attendee, in one file:

{ "templateId": "<Certificate template ID>", "modify": { "course": "TypeScript 101" }, "pages": [ { "modify": { "name": "Ada Lovelace" } }, { "modify": { "name": "Grace Hopper" } } ] }

A report with a cover, a body and terms from different templates:

{ "modify": { "customer": "Acme Inc." }, "pages": [ { "templateId": "<Cover template ID>" }, { "templateId": "<Report template ID>", "modify": { "quarter": "Q3" } }, { "html": "<h1>Terms</h1><p>...</p>" } ] }

The response adds the pages each entry printed:

{ "id": "pdf-id.pdf", "pdfUrl": "<content domain>/path/to/document.pdf", "pages": 3, "credits": 1, "entries": [ { "index": 0, "pages": 1 }, { "index": 1, "pages": 1 }, { "index": 2, "pages": 1 } ], "createdAt": "2026-09-28T12:34:56Z" }

pages makes one file. For one file per recipient, send one request each.

Limits

LimitValueWhen exceeded
Entries in pages100422 before rendering, nothing charged
Pages in the PDF100422, nothing charged
Rendered HTML of all entries5 MB422 before rendering, nothing charged
Rendering time60 s422, nothing charged
Size of the PDF50 MB422, nothing charged
pages requests in flight1 per account429 with Retry-After

The whole request is checked before anything renders, and a 422 lists every problem found.

A request with pages can take about a minute, plus any time spent waiting behind other renders. If your client gives up before the answer arrives, the PDF may still be made and charged. Find it with GET /v1/pdf or on the dashboard’s Storage page.