The address your program posts to
Your program posts the description of one document to one address, under the key of its account, and the finished PDF comes back in the answer.
The call
One address answers, it answers to a POST, and the body is the description of the document you want. The key of your account travels in the usual header.
What may be in a description — the page, the items drawn on it, the fonts, the standard it is written to — is the whole of the API section, and none of it is repeated here.
| The call that draws a document | POST https://hqf-pdf.com/api/v1/render |
|---|---|
| The shape the body is sent in | application/json |
| What the body holds | the description of one document, and nothing else |
| How the key travels | Authorization: Bearer <key> |
| What a success carries | 200 application/pdf |
| The name the file is offered under | Content-Disposition: attachment; filename="document.pdf" |
| What a refusal carries | application/json {"error": "…"} |
What travels, and what does not
This address carries the description alone. No file travels beside it: a document drawn here draws on the fonts the service holds, and on nothing sent with the call.
A PDF server running on a machine of your own takes files beside the description, each as a named part of the body. That is a different call, written on the head page of the section.
The same call, twice over
From a shell
The body is the description itself. The answer is the PDF, so write it straight to a file.
curl -X POST https://hqf-pdf.com/api/v1/render \
-H "Authorization: Bearer $HQF_PDF_KEY" \
-H "Content-Type: application/json" \
--data-binary @invoice.json \
-o invoice.pdf
From Python
A refusal arrives as JSON carrying one sentence, and the status says whether the request is worth sending again.
import json
import pathlib
import requests
description = {
"standard_fonts": [{"name": "sans", "face": "helvetica"}],
"items": [
{
"type": "text",
"rect": {"llx": 56, "lly": 700, "urx": "{page_width} - 56", "ury": 780},
"content": ["Invoice 2026-014"],
"font": "sans",
"font_size": 24,
}
],
}
answer = requests.post(
"https://hqf-pdf.com/api/v1/render",
headers={"Authorization": f"Bearer {KEY}"},
json=description,
timeout=120,
)
if answer.status_code != 200:
raise SystemExit(answer.json()["error"])
pathlib.Path("invoice.pdf").write_bytes(answer.content)
Every answer, and what to do about it
A refusal is JSON carrying one sentence. The status is what tells a program whether the request is worth sending again: one alone is, and it is the one that says the render server was never reached.
| Answer | What it means | What to do |
|---|---|---|
200 |
The PDF is the body, handed over as it is drawn, under the file name the header above names. | Nothing to do. |
400 |
Either what was posted is not JSON at all, or the render server read the description and refused it. The sentence says which, and what it found. | Correct the description. The same one posted twice gives the same answer. |
401 |
The call carried no key, or a key nobody holds any more. | Present a live key of the account. |
402 |
Either the documents of the month are all used, or the drawing asked for something a licence key covers and the account has none. The sentence says which. | Wait for the month to turn, or move up an offer. |
405 |
The address was asked for any way but a POST. | Post the description. The same address opened in a browser shows a page. |
413 |
The body of the request, or the document it asks for, runs past a ceiling. The sentence names the size or the ceiling. | Ask for less in one call. |
500 |
Writing the PDF did not come off, on the render server's side. | Worth one more call. Tell us if it keeps happening. |
503 |
The render server could not be reached at all, or this site does not know where it is. Nothing was read of the description. | The one answer worth sending the same request again for. |
What a call costs
- One document is taken off the month before the description is forwarded.
- A render that never arrives gives its document back: a call answered 503 costs nothing.
- A call refused before anything is forwarded — no key, a body that is not JSON, a month with nothing left — costs nothing either.
- The pages written down are the ones the render server states, and nothing here opens the document to count them a second time.
Trying it by hand
The same address opened in a browser shows a box: paste a description into it and the document comes back on the spot. It draws for whoever is signed in, off the session rather than off a key, and it counts nothing.
It is the same address, the same description and the same render server, so what you try by hand is what your program will send.
Where to go next
The format of a description Your keys and what is left of your month What becomes of a document you send