Convert EPUB to PDF
The EPUB to PDF API takes a reflowable ebook and renders it into a fixed, paginated PDF ready to print, archive, or attach. It exists for the moment an ebook needs to stop adapting to whatever screen it's on and become the same document on every page, every time.
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.
Two formats built for opposite goals
EPUB was designed around reflow: text resizes and re-wraps to fit whatever screen or font size the reader chooses, which is exactly what makes a novel comfortable on a phone or an e-reader. PDF was designed to do the opposite — freeze a page so it looks identical everywhere. Those are genuinely different jobs, and converting between them means committing to one fixed layout: page size, margins, and typography have to be decided once, at conversion time, rather than left to whatever device opens the file.
Who actually needs this
Publishers preparing a print-on-demand run, authors sending a manuscript for print review, libraries and archives that need a stable format for long-term preservation, and course creators turning an ebook into a downloadable PDF handout all hit the same wall: EPUB doesn't paginate the way a printer or a PDF viewer expects. The epub to pdf api resolves the ebook's chapters, embedded images, and table of contents into pages, so the output reads like a properly typeset book rather than a dump of raw text.
How the conversion runs
Call POST /doc/epub-to-pdf with your source .epub and you get a task_id immediately — the render happens asynchronously on our global edge, so nothing blocks while a 400-page novel gets paginated. Once the PDF exists, you're notified by a signed webhook (recommended for automated pipelines) or a signed link valid for 24 hours, so downstream steps can pick up the file the moment it's ready rather than on a polling schedule you have to maintain.
Where it fits an automated workflow
This is built for systems, not single downloads: a self-publishing platform that converts every submitted manuscript to a print-ready PDF automatically, a documentation tool that ships both an EPUB for readers and a PDF for print, an internal library that archives ebooks in a format guaranteed to render the same in ten years as it does today. Because the endpoint is asynchronous and confirms delivery by webhook, it becomes one step in a larger pipeline rather than a manual export a person has to remember to run.
Rollout, pricing, and safe handling
This endpoint is currently in deployment and will begin accepting jobs shortly — the request format and pricing below are final, so integrations can be built ahead of launch. Billing is $0.052 per request plus $0.003 per page, a published rate with no tokens and no invented credits. A task that fails is never charged; we retry three times before returning a clear error instead of a silent failure. Access requires prepaid balance — there is no free tier or trial — which keeps the service fast and free of abuse. Uploaded ebooks are deleted after the retention window and never used to train anything.
What you can do with it
Print-on-demand preparation
A publisher converts a finished EPUB manuscript into a paginated PDF that matches the trim size and margins their print vendor requires.
Ebook-to-handout for courses
An online course platform offers each ebook lesson as a downloadable PDF handout alongside the reflowable EPUB version students read on mobile.
Long-term digital archiving
A library converts donated EPUB collections into PDF for its preservation archive, where a fixed, self-contained page format matters more than reflow.
Manuscript review before publication
An author sends a proof EPUB through the API to get a PDF that mirrors how the finished book will look on a printed page, before approving the final print run.
FAQ
How do I convert EPUB to PDF with an API?
Send your .epub file to POST /doc/epub-to-pdf and you receive a task_id right away. The epub to pdf api paginates the ebook asynchronously and delivers the PDF by signed webhook or a signed link valid for 24 hours.
Is the EPUB to PDF API live yet?
This endpoint is in deployment and will begin accepting jobs shortly. The path, request shape, and pricing are already final, so you can integrate against them ahead of launch.
Is it free to use?
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.
What page size does the output PDF use?
The conversion resolves the ebook's reflowable text into a fixed, standard page layout with consistent margins and typography, producing a print-ready PDF rather than a single continuous scroll.
Does it handle embedded images and a table of contents?
Yes. Chapters, embedded images, and the ebook's table of contents are carried into the paginated PDF so the structure of the original book is preserved.
Can I convert a large batch of ebooks at once?
Yes. Because the endpoint is asynchronous and returns a task_id per request, you can submit many EPUB files in parallel from a queue or batch job.
What happens if a conversion fails?
A failed task is never charged. We retry three times with backoff, and if the ebook still cannot be converted you get a clear error instead of a silent failure or a charge.
How long can I access the converted PDF?
The signed link stays valid for 24 hours, or you can receive the file immediately by signed webhook. After the retention window it is deleted and never used for training.
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/doc/epub-to-pdf \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"…"}'const res = await fetch("https://api.kit.forhosting.com/doc/epub-to-pdf", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"input": "…"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/doc/epub-to-pdf",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"input": "…"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/doc/epub-to-pdf", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"input":"…"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"input":"…"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/doc/epub-to-pdf", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"input": "…"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "doc.epub_to_pdf",
"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. |