Documentation

Overview

html2pdf.app is an HTML to PDF conversion API. Send a public web page URL or raw HTML markup, and receive a ready-to-use PDF document in response. Call it from your own code, or from automation tools such as n8n: see Generate and Send PDF Invoice with n8n.

  1. Your app or workflowSends a web page URL or raw HTML to POST /v1/generate.
  2. html2pdf.appRenders it in headless Chromium, with its CSS, JavaScript and web fonts.
  3. Your PDFComes back in the response, or is sent to your callBackUrl.

Good to know

The page is rendered in a real browser, so it prints the way it looks in Chrome. These decide the result:

  • CSS media. Pages render with their screen styles. Set media to print to use your @media print rules instead. See Parameters.
  • Backgrounds. Background colours and images are printed. Set printBackground to false to leave them out.
  • Fonts. Common fonts such as Arial, Georgia and Times New Roman are installed. Load any other font, and an emoji font if you use emoji, as a web font. See Fonts.
  • Content that loads late. Set waitFor to wait up to 10 seconds before the PDF is generated, and lazyLoad for images that load on scroll. See Lazy-loaded images.

Quick start

A standard conversion is one authenticated POST request to https://api.html2pdf.app/v1/generate. Send a public web page URL or raw HTML in the required html field, and the API returns the generated PDF as binary data.

  1. Get an API keyCreate an account and use the API key sent after registration.
  2. Choose the inputSet html to a publicly reachable URL or a raw HTML string.
  3. Handle the resultCheck the HTTP status, then save or stream the successful response as a .pdf file.

Send your first request

Replace <your-api-key>, run the command, and cURL will write the generated PDF to document.pdf in the current directory.

curl --fail --show-error \
--request POST https://api.html2pdf.app/v1/generate \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <your-api-key>' \
--data '{
  "html": "https://www.example.com"
}' --output document.pdf

Handle the response

  • A successful synchronous request returns the PDF file as binary data. Do not parse it as JSON or text.
  • Check for a successful HTTP status before saving the body or sending it to a browser. The --fail option makes the cURL example fail on HTTP 4xx and 5xx responses.
  • To generate in the background, include callBackUrl and follow the callback workflow instead.

Continue with your language

Open a complete guide with production-oriented setup, PDF response handling, request options, and callback examples.

View all code examples

Authentication

Authenticate every request by passing the X-API-Key header with your API key. You will receive your API key by email after registration.

Your API key is private. Use it from your backend, server-side scripts, or trusted jobs. Do not expose it in browser JavaScript, public repositories, or client-side templates.

Send it in this header:

X-API-Key: <your-api-key>
The apiKey parameter. The key is also accepted as an apiKey query string parameter or as an apiKey field in the JSON body, so older integrations keep working. Use the header in new code: a key in a URL ends up in server and proxy logs, and in the body it sits among the document options. If your key has been sent in URLs, replace it with Rotate Key on the dashboard's API Keys page.

Parameters

POST is recommended. Send parameters as a JSON request body to avoid query-string length and escaping problems. GET requests are also supported, but every query parameter must be URL-encoded; avoid GET for raw HTML and long template values.
ParameterTypeDescriptionDefault
htmlstringRaw HTML markup or a publicly reachable URL to convert.required
callBackUrlstringGenerates the document asynchronously and sends the result to this URL. The callback payload contains the base64-encoded PDF as {"document": "..."}. See Asynchronous requests & callbacks.null
statestringOptional value returned unchanged in the callback payload. Use it to associate the result with the original request.null
landscapebooleanUses landscape instead of portrait page orientation.false
formatstringPage format. Supported values:
Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6
A4
widthintegerCustom page width in pixels. Use together with the height parameter.null
heightintegerCustom page height in pixels. Use together with the width parameter.null
singlePagebooleanPuts the whole document on one page, with no page breaks. The page is as wide as width, or as the format (its long side with landscape), and as tall as the content plus marginTop and marginBottom; height is ignored. Content up to 1.5 times wider than the page is scaled down to fit, and headerTemplate and footerTemplate appear once. For a URL whose images load on scroll, also set lazyLoad. See Single-page PDFs.false
marginTop
marginRight
marginBottom
marginLeft
integerWhitespace, in pixels, between the page edge and its content.0
filenamestringSets the filename returned with the generated PDF.null
waitForintegerNumber of seconds to wait before generating the PDF. Use this when the page needs time to finish JavaScript or load asynchronous resources. Supported range: 0 to 10.0
lazyLoadbooleanScrolls through the page before generating the PDF so that lazy-loaded images and content are loaded. Adds time on long pages, and effects the page runs while scrolling (such as a sticky header) can appear in the PDF. See Lazy-loaded images.false
mediastringCSS media mode used while rendering. Supported values: print and screen.screen
printBackgroundbooleanPrints background colours and images. Set it to false to leave them out.true
scalenumberScale applied to the document content. Supported range: 0.1 to 2.1
headerTemplate
footerTemplate
stringHTML markup rendered in the page header or footer. Use sufficient marginTop or marginBottom to make the template visible.null
userPasswordstringPassword required to open the encrypted PDF. Setting this value enables encryption.null
ownerPasswordstringOwner password that grants full access to the encrypted PDF and its permissions.null
permissionsarrayActions available to a user who opens the encrypted PDF. See the Encryption section for supported values.["print"]

Errors and HTTP statuses

Check the HTTP status before treating a response as a PDF. A non-2xx response is an error and must not be saved or returned with an application/pdf content type.

The body of an error response is JSON, with a numeric errorCode and a message that says what went wrong:

{
  "errorCode": 12,
  "message": "net::ERR_CONNECTION_RESET at https://example.com/"
}
StatusMeaningRecommended action
400The page could not be rendered, the source URL is not accessible, or a request parameter is invalid.Read the message, then correct the URL or the request values. For errorCode 12, see below.
401The API key is missing or invalid.Check the X-API-Key header and replace an invalid key.
403The account has reached a limit included in its current plan.Review the plan limits and the notification email sent to the account before retrying.
500An unhandled server error occurred.Retry after a short delay. Use increasing delays between repeated attempts and contact support if the error persists.

Pages that cannot be rendered

A 400 with errorCode 12 means the request was valid, but the page could not be converted. The message says why:

MessageMeaningRecommended action
The URL points to a PDF file… or …a file that browsers download…The URL serves a file, not a web page.Use the URL of a web page. Retrying will not help.
net::ERR_…, such as net::ERR_CONNECTION_RESET or net::ERR_HTTP2_PROTOCOL_ERRORThe site refused or dropped the connection, or did not answer in time.This can be temporary, so retry a few times with increasing delays. If it keeps failing, the site may be blocking automated browsers or requests from cloud providers.
The page did not finish rendering within the time limitThe page is too slow or too heavy to render in time.Retry later. If it keeps failing, reduce what the page loads or split it into smaller documents.
Do not automatically retry 400, 401, or 403 responses without first correcting the request, credentials, or account limit. The exception is a 400 with errorCode 12 from a site that was unreachable or slow, as described above.

Limits

Each plan sets a monthly number of credits and how many conversions can run at the same time. Every option is available on every plan.

html2pdf.app plans and their limits
PlanCredits per monthParallel conversionsPDF size
Free1001Up to 1 MB
Startup1,0003No cap
Standard5,00010No cap
Scale10,00020No cap
Scale 2525,00025No cap
Scale 5050,00050No cap
Scale 7575,00075No cap
Scale 100100,000100No cap
  • Credits. Each 5 MB of a generated PDF uses one credit, so a PDF under 5 MB costs one credit. Credits reset on the 1st day of each month.
  • Request size. A request body can be up to 5 MB. For a larger page, pass its URL in html instead of the markup.
  • Waiting. waitFor can delay rendering by up to 10 seconds.
  • When a limit is reached. A request that goes beyond a plan limit gets a 403 response. See Errors.
  • Without an API key. The online converter allows 10 conversions per hour.

See Pricing for the price of each plan.

Asynchronous requests & callbacks

By default, the API keeps the request open while it generates the PDF and then returns the document as binary data. Provide a callBackUrl when the document should be generated in the background instead.

Submit an asynchronous job

Send the request to the normal generate endpoint and authenticate with the same X-API-Key header used for synchronous conversions. When authentication succeeds and the request is accepted for processing, the API responds with 202 Accepted. A 202 response is confirmation that the job was queued; its body is not the generated PDF.

curl --fail --show-error \
--request POST https://api.html2pdf.app/v1/generate \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <your-api-key>' \
--data '{
  "html": "https://www.example.com",
  "callBackUrl": "https://your-app.com/webhooks/pdf",
  "state": "order-123"
}'

Receive the generated PDF

When processing finishes, html2pdf.app sends a POST request to callBackUrl. The callback JSON contains the generated PDF as a base64-encoded document value. Decode that value before saving or serving the PDF.

{
  "document": "JVBERi0xLjQ...==",
  "state": "order-123"
}

The optional state value is returned unchanged, which lets you associate the callback with the original order, report, or job.

Callback delivery

  • Use a publicly reachable HTTPS endpoint that accepts POST requests.
  • Process callbacks idempotently because a delivery can be attempted more than once.
  • If your endpoint cannot be reached, html2pdf.app tries to deliver the callback up to three times in total. Any HTTP response from your endpoint, including an error status, counts as delivered and is not retried.
  • No callback is sent when a conversion fails or when every delivery attempt fails, so treat a job that has not called back within your own time limit as failed.

Single-page PDFs

Set singlePage to true to put the whole document on one page, with no page breaks. The page is as tall as the content, so nothing is split between pages and no blank space is left at the end.

It suits receipts and order confirmations whose length varies, long web pages kept as one document, and reports or dashboards that are read on screen rather than printed.

curl --fail --show-error --output page.pdf --request POST \
  --url https://api.html2pdf.app/v1/generate \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <your-api-key>' \
  --data '{
    "html": "https://example.com",
    "singlePage": true,
    "width": 1280
}'

How the page is sized

  • Width — width in pixels, or the width of format (its long side with landscape). height is ignored.
  • Height — the height of the content, plus marginTop and marginBottom.
  • Wide content — content up to 1.5 times wider than the page is scaled down to fit.
  • Page breaks — break rules such as break-after: page are ignored.
  • Headers and footers — headerTemplate and footerTemplate appear once, at the top and the bottom of the page.

Tips

  • For a URL whose images load on scroll, also set lazyLoad, so that they are loaded before the page is measured. See Lazy-loaded images.
  • Elements fixed to the window, such as a sticky header or a cookie banner, appear once: at the top of the page, or at the bottom if they stay at the bottom of the window.
  • A very long page opens in browsers and most PDF viewers, but Adobe Acrobat cannot open a page taller than 200 inches (19,200 pixels).

Lazy-loaded images

Many pages load images only when they scroll into view, with loading="lazy" or a JavaScript loader. A PDF is generated without scrolling, so by default images further down such a page can be missing or show a placeholder.

Set lazyLoad to true to scroll through the page before the PDF is generated, the way a visitor would. Images and content that load on scroll are loaded, and the page is scrolled back to the top before it is rendered.

curl --fail --show-error --output page.pdf --request POST \
  --url https://api.html2pdf.app/v1/generate \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <your-api-key>' \
  --data '{
    "html": "https://example.com",
    "lazyLoad": true
}'

How it works

  • Images marked loading="lazy" are loaded straight away, and the page is scrolled from top to bottom so that JavaScript loaders see every element.
  • After the last scroll, it waits for the images that started loading.
  • Scrolling stops at the bottom of the page once it stops growing, or after 12 seconds at most, so a page that keeps loading more content as you scroll still finishes.
  • It applies to HTML pages. A URL that returns plain text, JSON, XML or an image has no lazy content.

Things to know

  • It is off by default, because scrolling adds time on long pages.
  • Effects the page runs while scrolling can appear in the PDF, such as a header that appears on scroll, or an animation or a carousel caught part way.
  • For content that arrives later still, also set waitFor: the wait starts after the scroll.
  • With single-page PDFs of long pages, set lazyLoad too, so that every image is on the page.

Encryption

Set userPassword to encrypt the PDF and require a password when it is opened. Use permissions to control which actions are available after opening it. An optional ownerPassword grants full access to the document and its permission settings.

PDF encryption uses 128-bit RC4.

Encryption is enabled when userPassword is set. By default, permissions is ["print"], allowing a user who opens the document to print it.

ParameterTypeDescriptionDefault
userPasswordstringThe password that you want users to use for opening the PDF.null
ownerPasswordstringGrants full access to the PDF, including actions restricted by permissions and the ability to modify those permissions.null
permissionsarray
Field controlling what actions a user entering userPassword can perform.
print - allow printing.
modify - allow document modification, signing, and form-field changes.
copy - allow content copying and copying for accessibility.
edit - allow commenting.
fillform - allow filling of form fields.
extract - allow content copying for accessibility.
assemble - allow document assembly operations such as rotating, inserting, or deleting pages and managing bookmarks and thumbnails.
printbest - allow high-resolution printing when print is also allowed.
["print"]

cURL request example

curl --fail --show-error --output example.pdf --request POST \
  --url https://api.html2pdf.app/v1/generate \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <your-api-key>' \
  --data '{
    "html": "https://example.com",
    "userPassword": "user",
    "ownerPassword": "owner",
    "permissions": ["print", "modify", "copy"]
}'

Page breaks

Use CSS break rules to start each selected element on a new PDF page. Include the legacy page-break-* properties as a fallback.

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Paginated HTML</title>
    <style>
      .page {
        break-after: page;
        break-inside: avoid;
        page-break-after: always;
        page-break-inside: avoid;
      }

      .page:last-child {
        break-after: auto;
        page-break-after: auto;
      }
    </style>
  </head>
  <body>
    <section class="page">
      <h1>This is page 1</h1>
    </section>
    <section class="page">
      <h1>This is page 2</h1>
    </section>
    <section class="page">
      <h1>This is page 3</h1>
    </section>
  </body>
</html>

To put the whole document on one page with no breaks instead, see Single-page PDFs.

Fonts

The fonts listed below are available in the rendering environment.

You can also load Google Fonts or self-hosted web fonts with @font-face. Font stylesheets and files must be publicly reachable without authentication. If they load asynchronously, use waitFor to give the page time to finish rendering. No emoji font is installed, so load one as a web font if your document uses emoji.
Fonts available in the PDF rendering environment
Andale MonoComic Sans MSTahoma
ArialCourier NewTimes New Roman
Arial BlackGeorgiaTrebuchet MS
Arial NarrowImpactVerdana
Arial Unicode MSMicrosoft Sans SerifWebdings

Data handling

Generated PDFs are deleted from our servers immediately after they are delivered, in the API response or to your callback URL, and are never stored permanently. Raw HTML or text submitted in the html parameter is not stored in conversion logs. Selected request metadata and a source URL supplied in html may be retained in those logs.

See the Privacy Policy and Data Processing Agreement for processing, retention, and security details.