HTML to PDF API
The HTML to PDF API turns HTML, or any public web page, into a PDF. Pages are rendered the way a modern web browser shows them, so CSS layouts, web fonts, SVG, images and charts drawn with JavaScript all come out right, and the text in the PDF stays selectable and searchable.
Generate invoices, receipts, reports, certificates and tickets from one HTML template: put {{tags}} in the HTML and send the values as JSON in data. Add headers and footers with page numbers, choose the paper size, orientation and margins, and set the PDF's title, author, watermark and password in the same call.
Converting a web page waits for it to load, runs its JavaScript and scrolls through it so lazy-loaded images appear. Cookies, headers and basic authentication reach pages that need a login.
Each page of the PDF uses one of your plan's monthly document pages, not API calls: Free 50, Developer 10,000, Business 100,000, Professional 500,000. Your dashboard shows the pages you've used this month. See pricing.
Going the other way? The PDF to HTML API turns PDFs into HTML. The Word to PDF API converts Word documents, and the PDF to Image API turns PDF pages into images.
At a Glance
| Input | HTML (up to 4 MB) as text, a file or the raw body, or a web page url |
|---|---|
| Output | A PDF, as base64 in JSON or as a PDF file; large PDFs as a download link |
| Pages per PDF | Up to 500 |
| Templates | {{tags}} filled from JSON data |
| Plan usage | 1 document page per page of the PDF |
| Free plan | PDFs up to 5 pages, from HTML up to 1 MB |
Related APIs
- PDF to Image API - PDF pages to JPG, PNG or WebP images
- PDF to JPG API - PDF pages to JPG images
- PDF to PNG API - PDF pages to lossless PNG images
- Image to PDF API - JPG, PNG and other images to one PDF
- JPG to PDF API - JPG photos and scans to one PDF
- PNG to PDF API - PNG screenshots and charts to one PDF
- Word to PDF API - Word documents (.docx or .doc) to PDF
- PDF to HTML API - PDFs to HTML, fixed or reflowable
- Image to Text API - Read the text in an image (OCR)
https://api.api-ninjas.com/v1/htmltopdf
Converts HTML, or a web page, to PDF. Returns the PDF as base64 in JSON, or as a PDF file. Accepts GET or POST: use POST to send HTML, or GET with url.
Parameters
Send one of html, file or url. All other parameters are optional. In a POST, put the parameters in a JSON body (where data, headers and cookies can be JSON objects), or send them as form fields or query parameters.
htmloptionalThe HTML to convert: a full document or a fragment, up to 4 MB (1 MB on the Free plan). Send it in a JSON body, as a form field, or as the raw request body with
Content-Type: text/html. Images, stylesheets and fonts it links to are loaded from the internet; usebase_urlfor relative links.fileoptionalAn
.htmlfile as a multipart form upload, instead ofhtml.urloptionalA public
http://orhttps://web page to convert, instead ofhtml. The page is loaded like in a browser: its JavaScript runs and lazy-loaded images are scrolled into view before the PDF is made.dataoptionalA JSON object that fills the template tags in
html,headerandfooter:{{name}}inserts a value (HTML-escaped),{{#items}}...{{/items}}repeats a block for each item of a list, and{{{html}}}inserts a value as HTML. Up to 1 MB. Works withhtml, noturl.base_urloptionalThe address that relative links in
htmlare resolved against, for examplehttps://example.com/invoices/for<img src="logo.png">.page_sizeoptionalPaper size:
A4(default),Letter,Legal,Tabloid,Ledger,A0,A1,A2,A3,A5orA6. A size set by the page's own CSS (@page { size: ... }) takes precedence.widthoptionalCustom page width with a unit, for example
210mm,8.5in,20cmor800px. Send it withheight, instead ofpage_size.heightoptionalCustom page height with a unit, for example
297mm. Send it withwidth.orientationoptionalportrait(default) orlandscape.marginoptionalPage margins in CSS shorthand: one to four lengths with units, for example
1cmor20mm 15mm. Default1cm, or 2 cm at the top and bottom when there is a header or footer. Margins set by the page's own@pageCSS take precedence.scaleoptionalZoom from
0.1to2. Default1;0.8fits more on each page.print_backgroundoptionalInclude background colors and images:
true(default) orfalse.mediaoptionalWhich CSS styles apply:
print(default, the page's print styles) orscreen(the page as it looks on screen).headeroptionalHTML printed at the top of every page, up to 50,000 characters. It can use
{{page}},{{pages}},{{date}},{{title}}and{{url}}, and the values indata. It is styled on its own: page styles do not apply, so put any CSS inline.footeroptionalHTML printed at the bottom of every page, with the same tags as
header, for examplePage {{page}} of {{pages}}.page_numbersoptionaltrueadds a centered "Page 1 of 3" footer when you don't send afooter. Defaultfalse.page_rangesoptionalPages to keep, for example
1-3,1,4or2-(page 2 to the end). Default: all pages.cssoptionalExtra CSS added to the page before it is converted, up to 200 KB, for example to hide a cookie banner.
javascriptoptionalRun the page's JavaScript:
true(default) orfalse.wait_untiloptionalWhen the page counts as loaded:
load(default, after images and stylesheets),domcontentloaded(as soon as the HTML is read) ornetworkidle(once the network has been quiet for half a second; best for pages that load data with JavaScript).wait_foroptionalA CSS selector to wait for before converting, for example
#chartor.report-ready, so content drawn by JavaScript is in the PDF.delayoptionalExtra milliseconds to wait before converting, up to
10000, for animations or charts.lazy_loadoptionalScroll through the page first so lazy-loaded images appear:
trueby default forurl,falseforhtml.viewport_widthoptionalBrowser window width in pixels, from
320to3840. Default1280. Responsive pages lay out for this width; use390for the mobile layout.user_agentoptionalThe User-Agent header sent when loading
url. Works withurlonly.headersoptionalExtra HTTP headers for
urlas a JSON object, for example{"Authorization": "Bearer ..."}. They are sent to the page's own host only, never to other sites the page loads from. Works withurlonly.cookiesoptionalCookies for
url, as a JSON object or asname=valuepairs separated by semicolons, for examplesession=abc123; theme=dark. Works withurlonly.url_usernameoptionalUser name for pages behind HTTP basic authentication. Works with
urlonly.url_passwordoptionalPassword for pages behind HTTP basic authentication. Works with
urlonly.timezoneoptionalTime zone the page sees, for dates its JavaScript shows, for example
America/New_York.localeoptionalLanguage and region the page sees, for example
de-DE, for pages that adapt to the browser's language.titleoptionalTitle stored in the PDF's document properties, shown by most PDF viewers. Default: the page's own
<title>.authoroptionalAuthor stored in the PDF's document properties.
subjectoptionalSubject stored in the PDF's document properties.
keywordsoptionalKeywords stored in the PDF's document properties.
passwordoptionalEncrypts the PDF (AES-256) so it opens only with this password. Up to 128 characters.
watermarkoptionalText printed diagonally across every page, for example
DRAFTorCOPY. Up to 100 characters.taggedoptionaltrue(default) makes an accessible tagged PDF, with the structure screen readers use. Setfalsefor a slightly smaller file.outlineoptionaltrue(default) adds bookmarks for the page's headings, shown in a PDF viewer's sidebar.filenameoptionalFile name for the PDF when it is returned as a file (
Accept: application/pdf). Defaultdocument.pdf.
Headers
X-Api-KeyrequiredAPI Key associated with your account.
Acceptoptionalapplication/pdfreturns the raw PDF file instead of JSON.Content-Typeoptionalapplication/jsonfor a JSON body, ortext/htmlwhen the request body is the HTML itself. Multipart uploads set their own content type.
Response
The /v1/htmltopdf endpoint returns a JSON object with the following fields, or an error if the request is unsuccessful (see errors).
page_countNumber of pages in the PDF.
sizeSize of the PDF in bytes.
warningsImages, stylesheets, fonts and other resources the page could not load, as messages. The PDF is made without them; an empty list means everything loaded.
pdfThe PDF file, base64-encoded.
pdf_urlPaid plans: for a PDF larger than about 4 MB, a link to download it instead of pdf. The link works for one hour.
expires_atWhen pdf_url stops working (UTC, ISO 8601).
With Accept: application/pdf, the response body is the PDF file itself (Content-Type: application/pdf), or for a PDF larger than about 4 MB, a redirect (HTTP 303) to its download link.
Limits
- HTML up to 4 MB. PDFs up to 500 pages.
- PDFs up to about 4 MB come back in the response; larger ones, up to 100 MB, as a download link that works for one hour.
- Each request has about 20 seconds to load and convert. For very long documents, send them in parts with
page_ranges. - Web pages must be on the public internet, on port 80, 443, 8080 or 8443. Pages that need a login can be reached with
cookies,headersorurl_usernameandurl_password. - Free plan: PDFs up to 5 pages and 4 MB, from HTML up to 1 MB.
Document pages
This API uses your plan's monthly document pages, not API calls. Each page of the PDF uses one: a 10-page PDF uses 10. With page_ranges, only the pages you keep are counted.
Live Demo
Press Send to convert the sample invoice and preview the PDF here.
The live demo converts the first page, from HTML up to 100 KB. No API key is needed to try it.
Code Examples
# Fill an HTML template and save the PDF
curl -X POST "https://api.api-ninjas.com/v1/htmltopdf" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/pdf" \
-d '{"html": "<h1>Invoice {{number}}</h1><p>Bill to: {{customer}}</p><p>Total due: {{total}}</p>", "data": {"number": "INV-2026-0042", "customer": "Jane Doe", "total": "$348.00"}, "page_size": "Letter", "page_numbers": true}' \
--output invoice.pdf
# Convert a web page
curl "https://api.api-ninjas.com/v1/htmltopdf?url=https://example.com" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Accept: application/pdf" \
--output page.pdfErrors
An invalid request returns HTTP 400 with a JSON body holding a readable error message. If too many PDFs are being made at once, the API returns HTTP 503: retry after a few seconds. Authentication and quota errors use the standard format described in error codes.
| Cause | Example error |
|---|---|
| No HTML or url in the request | Send the HTML as html (or upload an .html file) or a web page as url. |
Both html and url | Send either html or url, not both. |
A url that is not a web address | url must be a full http:// or https:// link. |
A private or local address in url | url must point to a public internet address. |
| The web page returns an error | url returned HTTP 404. |
| The page keeps loading past the time limit | The page did not finish loading in time. Try wait_until=domcontentloaded, or check that the page loads without logging in. |
Nothing matches wait_for in time | No element matched wait_for (#chart) before the time limit. |
An unknown page_size | page_size must be one of: Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6. |
A length without a unit, e.g. margin=10 | margin must be a length with a unit, for example 10mm, 1cm, 0.5in or 40px. |
data with url | data fills an HTML template, so it needs html instead of url. |
| More than 500 pages | This PDF would have 620 pages. The most per request is 500 pages; use page_ranges to convert part of it. |
| A PDF over 100 MB | The PDF is larger than 100 MB. Use page_ranges to convert part of the document. |
| No document pages left this month | You have used all 50 document pages included in your Free plan this month. They renew on December 1, 2026. Upgrade at https://api-ninjas.com/pricing for more. |
| Free plan: more than 5 pages | This PDF would have 8 pages. The Free plan converts documents of up to 5 pages. Upgrade at https://api-ninjas.com/pricing for documents up to 500 pages. |
| Free plan: HTML over 1 MB | HTML on the Free plan can be up to 1 MB. Upgrade at https://api-ninjas.com/pricing for up to 4 MB. |
| Free plan: a PDF over 4 MB | The PDF is larger than 4 MB. Upgrade at https://api-ninjas.com/pricing for PDFs up to 100 MB. |
Frequently Asked Questions
Can I use the HTML to PDF API for commercial purposes?
Yes, but you must have a premium subscription. Commercial use of the HTML to PDF API is not permitted on the free tier. See our pricing page to choose a plan that fits your needs.How do I get an API key and start using the HTML to PDF API?
Sign up for a free account to instantly get your API key, then pass it in theX-Api-Keyheader on every request. The documentation above includes ready-to-run examples in cURL, Python, JavaScript, Node.js, and more.What happens if I exceed my quota for the month?
Your API requests will simply be denied once you hit your monthly quota — you will never be charged more than the plan you signed up for. To increase your quota, you can upgrade your plan any time on our pricing page.What happens if a request to the HTML to PDF API fails?
The API responds with a standard HTTP status code and a JSON error message describing the problem. See our error codes reference for the full list of codes and how to resolve each one, or contact support if you need help.How do I convert HTML to PDF with an API?
POST the HTML to/v1/htmltopdfin a JSON body, e.g.curl -X POST "https://api.api-ninjas.com/v1/htmltopdf" -H "X-Api-Key: YOUR_API_KEY" -H "Content-Type: application/json" -H "Accept: application/pdf" -d '{"html": "<h1>Hello</h1>"}' --output hello.pdf. Without theAcceptheader the response is JSON with the PDF base64-encoded inpdf. You can also upload an.htmlfile or send the HTML as the raw request body withContent-Type: text/html.Can I convert a web page URL to PDF?
Yes: passurlinstead ofhtml, e.g./v1/htmltopdf?url=https://example.com. The page loads like in a browser: its JavaScript runs and it is scrolled through so lazy-loaded images appear. For pages that draw content late, addwait_until=networkidle,wait_forwith a CSS selector, ordelay. Pages behind a login can be reached withcookies,headersorurl_usernameandurl_password.How do I generate PDFs like invoices from a template?
Write the template once in HTML with{{tags}}, and send the values for each document indata:{"html": "<h1>Invoice {{number}}</h1>{{#items}}<p>{{name}}: {{price}}</p>{{/items}}", "data": {"number": "INV-0042", "items": [{"name": "Plan", "price": "$149"}]}}.{{#items}}...{{/items}}repeats a block for each item in a list, values are HTML-escaped, and{{{value}}}inserts HTML as is. The same tags work inheaderandfooter.How do I add page numbers, headers and footers?
Setpage_numbers=truefor a centered "Page 1 of 3" footer, or send your ownfooterandheaderHTML with the tags{{page}},{{pages}},{{date}},{{title}}and{{url}}, e.g.<div style="text-align:right">{{page}} / {{pages}}</div>. Headers and footers are styled on their own, so put their CSS inline. The page margins grow to 2 cm at the top and bottom to make room, unless you setmargin.How much of my plan does HTML to PDF use?
Can I use HTML to PDF on the Free plan?
Yes: the Free plan includes 50 document pages a month, for PDFs up to 5 pages and 4 MB made from HTML up to 1 MB or from a web page. Paid plans make PDFs up to 500 pages from HTML up to 4 MB, and return PDFs over 4 MB as a download link, up to 100 MB. See pricing.
Explore API integration use cases for recommended APIs and workflow examples.