Convert HTML to PDF
Send us your HTML — a full document, an invoice template, a rendered report — and get back a PDF that looks exactly like the page a modern browser would draw. The HTML to PDF API runs the same layout, fonts, and CSS you already design against, so what you preview is what you ship. It is asynchronous by design: you POST once, get a task_id, and the finished file lands on your webhook or a signed link.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
The problem it actually solves
Every team that bills, reports, or ships receipts eventually needs a PDF, and most reach for a headless browser they now have to install, patch, sandbox, and scale. That means fonts that vanish on the server, a Chromium binary that eats memory under load, and a pipeline nobody wants to own. This endpoint removes it: you keep authoring in the HTML and CSS your designers already know, and we handle the rendering engine, the fonts, and the concurrency. Your invoice template stays a template, not fragile infrastructure.
How a request behaves
You send a POST to /pdf/from-html with your markup, optional CSS, page size, and margins. Because rendering a rich document takes real work, the call is asynchronous: it returns a task_id immediately instead of blocking your thread while pages paint. When the PDF is ready we notify you — a signed webhook is the recommended path — or you fetch it from a signed link valid for 24 hours. The result is a standard PDF, produced with the same box model, page breaks, and web fonts a browser applies.
Why HTML became the layout language for print
PDF has been the fixed-layout standard since the early 1990s, but almost nobody hand-authors it. HTML and CSS, meanwhile, grew a genuine print vocabulary: @page rules, page-break controls, and paged-media properties that let one stylesheet describe both a screen view and a printed sheet. That convergence is why generating PDFs from HTML feels natural — you are not learning a second document format, you are reusing the one your front end already speaks. The closer your template is to real web markup, the closer the output.
Fitting it into your automation
Because every job returns a task_id and reports completion over a webhook, the HTML to PDF API drops cleanly into queues, cron jobs, and event-driven flows without polling. Fan out a thousand statements at month-end and let each completion event trigger the next step — archive, email, or countersign. Failures never silently cost you: a task that cannot complete is retried three times with backoff, then returns a clear error. Successful pages are the only thing you pay for.
Access, pricing, and what happens to your data
Pricing is published and simple: $0.040 per request plus $0.001 per page, so a ten-page report costs five cents and you can predict a batch before you run it. Access requires prepaid balance; a request without it returns a 402 — that is deliberate, and it keeps the service fast and free of abuse. Your HTML and the resulting PDF live only through the retention window, are then deleted, and are never used to train anything.
What you can do with it
Invoices and receipts at scale
Render every order confirmation or monthly invoice from the same HTML template you preview in-app, so accounting, the customer copy, and the archived record are byte-for-byte identical. Trigger one call per order and attach the returned PDF to your transactional email.
Reports and statements on a schedule
Turn dashboards, analytics summaries, or account statements into printable PDFs on a nightly cron, with real page breaks and repeating headers instead of a screenshot. Each completion webhook can hand the file straight to your delivery or archival step.
Certificates, tickets, and labels
Generate course certificates, event tickets with unique codes, or shipping labels by injecting data into a styled HTML layout, then producing a print-ready PDF at the exact page size the printer expects.
Contracts and generated paperwork
Assemble agreements or quotes from dynamic HTML, freeze them into a fixed-layout PDF for signing, and store the signed-off document — the same markup your legal team edits becomes the file the client receives.
FAQ
How do I convert HTML to PDF with an API?
POST your markup to /pdf/from-html and you get back a task_id. When the PDF is ready it arrives on your webhook or a signed link, so a single call takes you from HTML to a finished file.
Is there a free tier or free trial?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
How much does it cost per PDF?
You pay $0.040 per request plus $0.001 per page, so a five-page document is $0.045. A task that fails after its retries is never charged.
Does my CSS and web fonts render correctly?
Yes — the endpoint uses the same box model, page-break rules, and web-font handling a modern browser applies, so a template that looks right in preview looks right in the PDF. Use @page and paged-media CSS to control size, margins, and breaks.
Why is the API asynchronous instead of returning the PDF directly?
Rendering a rich document is real work, so blocking a connection while pages paint is fragile at scale. Returning a task_id and delivering by webhook keeps your request fast and lets you generate large batches without timeouts.
Can I generate PDFs in bulk?
Yes. Because each job is independent and reports completion over a webhook, you can fan out thousands of documents at once and react to each finished file as its event arrives, with no polling.
How long is the result available and is my data kept?
A signed link stays valid for 24 hours, which is also a good window for your webhook handler to store the file. After the retention window your HTML and PDF are deleted and never used to train anything.
What happens if a conversion fails?
A task that cannot complete is retried up to three times with backoff, and only then returns a clear error. You are never charged for a failed conversion — you pay only for pages that are actually produced.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/pdf/from-html \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"html":"<h1>Factura 001</h1><p>Total: 100 EUR</p>"}'const res = await fetch("https://api.kit.forhosting.com/pdf/from-html", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"html": "<h1>Factura 001</h1><p>Total: 100 EUR</p>"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/pdf/from-html",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"html": "<h1>Factura 001</h1><p>Total: 100 EUR</p>"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/pdf/from-html", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"html":"<h1>Factura 001</h1><p>Total: 100 EUR</p>"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"html":"<h1>Factura 001</h1><p>Total: 100 EUR</p>"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/pdf/from-html", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"html": "<h1>Factura 001</h1><p>Total: 100 EUR</p>"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "pdf.from_html",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_mb | 25 |
max_pages | 200 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
413 | input_too_large | The file exceeds the size limit. |
422 | task_failed | The task failed after 3 retries. You are never charged for it. |