Build against HTMLPress without opening the repository.
This page mirrors the repository reference for the authenticated PDF and image APIs. Start with a verified account, create a bearer token in the dashboard, and send requests only to final HTTP 200 targets.
- 1. Register and verify your email address to start the 3-day API trial.
- 2. Create an API token in the dashboard. The plaintext token is shown exactly once.
- 3. Send Authorization: Bearer htmlpress_… on every GET or POST request to /api/pdf and /api/image.
- 4. Revoke tokens from the dashboard at any time if one is exposed or no longer needed.
Go from signup to your first API call.
Register, verify your email, log in to the dashboard, create a token, and use that token for every API request. The trial clock starts at verification time, not at registration time.
HTMLPress follows redirects, but conversion succeeds only when the final response is HTTP 200. Private, local, and internal targets are blocked unless you explicitly disable the SSRF guard in a self-hosted environment.
The curl examples below are runnable as-is against https://htmlpress.tech once you substitute your own bearer token.
Every public conversion request is bearer-authenticated.
Send Authorization: Bearer htmlpress_... on every GET and POST request to /api/pdf and /api/image.
401 UNAUTHORIZED means the header is missing, malformed, unknown, or revoked. 403 EMAIL_NOT_VERIFIED means the account has not verified its email yet. 403 TRIAL_EXPIRED means the verified account used up its 3-day trial.
Tokens are shown only once when created. Store them immediately in your own secret manager, and revoke them from the dashboard if they are ever exposed.
Authorization: Bearer htmlpress_your-token-hereRender a public webpage into a PDF.
POST JSON is the primary contract. GET remains supported with the same option names as query parameters for backwards compatibility.
Successful responses stream PDF bytes with Content-Type: application/pdf and Content-Disposition: inline; filename="<filename>".
| Name | Type | Default | Allowed values | Notes |
|---|---|---|---|---|
| url | string | required | Valid http:// or https:// URL | Missing or invalid URLs return 400 INVALID_URL. |
| waitUntil | string | networkidle2 | load, domcontentloaded, networkidle0, networkidle2 | Shared with the image endpoint. |
| timeout | integer ms | 30000 | 1000 to 60000 | Shared with the image endpoint. |
| delay | integer ms | 0 | 0 to 10000 | Extra wait after a successful page load. |
| filename | string | page.pdf | Sanitized filename token | Used in Content-Disposition. Quotes, separators, and control chars are stripped. |
| ignoreHttpsErrors | boolean | false | true, false, 1, 0 | Only bypasses certificate validation. It does not bypass the SSRF guard. |
| format | string | A4 | A0, A1, A2, A3, A4, A5, A6, Letter, Legal, Tabloid, Ledger | Case-insensitive. Ignored when width or height is provided. |
| width | string or number | unset | CSS length using mm, cm, in, or px | Bare numbers are treated as millimeters and override format when set. |
| height | string or number | unset | CSS length using mm, cm, in, or px | Bare numbers are treated as millimeters and override format when set. |
| landscape | boolean | false | true, false, 1, 0 | Preferred over the legacy orientation alias. |
| margin | string or number | unset | CSS length using mm, cm, in, or px | Applies to all sides unless overridden. |
| marginTop | string or number | unset | CSS length using mm, cm, in, or px | Overrides margin for the top side. |
| marginRight | string or number | unset | CSS length using mm, cm, in, or px | Overrides margin for the right side. |
| marginBottom | string or number | unset | CSS length using mm, cm, in, or px | Overrides margin for the bottom side. |
| marginLeft | string or number | unset | CSS length using mm, cm, in, or px | Overrides margin for the left side. |
| scale | number | 1 | 0.1 to 2 | Matches Puppeteer’s supported range. |
| pageRanges | string | unset | Examples: 1, 1-3, 1-3, 5, 7-9 | Invalid patterns return 400 INVALID_OPTION. |
| printBackground | boolean | true | true, false, 1, 0 | Preserves background colors and images. |
| preferCSSPageSize | boolean | false | true, false, 1, 0 | Passes through to Puppeteer. |
| omitBackground | boolean | false | true, false, 1, 0 | Passes through to Puppeteer. |
| displayHeaderFooter | boolean | false | true, false, 1, 0 | Enables headerTemplate and footerTemplate. |
| headerTemplate | string | unset | Maximum 10240 bytes | HTML string used only when displayHeaderFooter=true. |
| footerTemplate | string | unset | Maximum 10240 bytes | HTML string used only when displayHeaderFooter=true. |
Runnable curl example
curl -X POST https://htmlpress.tech/api/pdf \
-H "Authorization: Bearer htmlpress_your-token-here" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","format":"A5","landscape":true,"margin":10}' \
-o page.pdfCapture a public webpage as PNG, JPEG, or WebP.
POST JSON is the primary contract. GET remains supported with the same option names as query parameters for backwards compatibility.
Binary responses use image/png, image/jpeg, or image/webp. When encoding=base64, the endpoint returns a plain-text base64 string instead.
| Name | Type | Default | Allowed values | Notes |
|---|---|---|---|---|
| url | string | required | Valid http:// or https:// URL | Missing or invalid URLs return 400 INVALID_URL. |
| waitUntil | string | networkidle2 | load, domcontentloaded, networkidle0, networkidle2 | Shared with the PDF endpoint. |
| timeout | integer ms | 30000 | 1000 to 60000 | Shared with the PDF endpoint. |
| delay | integer ms | 0 | 0 to 10000 | Extra wait after a successful page load. |
| filename | string | screenshot.png, screenshot.jpg, or screenshot.webp | Sanitized filename token | Default follows the selected output format. |
| ignoreHttpsErrors | boolean | false | true, false, 1, 0 | Only bypasses certificate validation. It does not bypass the SSRF guard. |
| format | string | png | png, jpeg, webp | Preferred over the legacy type alias. |
| quality | integer | unset | 0 to 100 | Only valid with jpeg or webp. |
| width | integer | 1122 | 16 to 5000 | Viewport width in CSS pixels. |
| height | integer | 1587 | 16 to 5000 | Viewport height in CSS pixels. |
| deviceScaleFactor | number | 1 | 1 to 3 | Multiplies final pixel density. |
| fullPage | boolean | false | true, false, 1, 0 | Captures the full scroll height. |
| clip | object | unset | { x, y, width, height } | JSON-only nested object form. Mutually exclusive with fullPage. |
| clipX | number | unset | Greater than or equal to 0 | GET-compatible clip alias. Send together with clipY, clipWidth, and clipHeight. |
| clipY | number | unset | Greater than or equal to 0 | GET-compatible clip alias. Send together with clipX, clipWidth, and clipHeight. |
| clipWidth | number | unset | Greater than 0 | GET-compatible clip alias. Send together with clipX, clipY, and clipHeight. |
| clipHeight | number | unset | Greater than 0 | GET-compatible clip alias. Send together with clipX, clipY, and clipWidth. |
| omitBackground | boolean | false | true, false, 1, 0 | Passes through to Puppeteer. |
| encoding | string | binary | binary, base64 | The older implicit base64 default is gone. |
Runnable curl example
curl -X POST https://htmlpress.tech/api/image \
-H "Authorization: Bearer htmlpress_your-token-here" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","format":"jpeg","quality":80,"width":1280,"height":800,"fullPage":true}' \
-o screenshot.jpgAll failures use one consistent envelope.
The public conversion endpoints always return JSON in a single error envelope. The table below excludes dashboard-only and demo-internal codes such as INVALID_CREDENTIALS and RATE_LIMITED.
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable English explanation.",
"details": {
"optional": "metadata"
}
}
}| Code | HTTP status | When it happens |
|---|---|---|
| INVALID_URL | 400 | Missing URL, malformed URL, or unsupported protocol. |
| INVALID_OPTION | 400 | A known option has an invalid type, enum, range, or combination. |
| TARGET_NOT_ALLOWED | 400 | The SSRF guard rejected a private, local, link-local, or internal target. |
| UNAUTHORIZED | 401 | The bearer token is missing, malformed, unknown, or revoked. |
| EMAIL_NOT_VERIFIED | 403 | The bearer token belongs to an account whose email is not verified. |
| TRIAL_EXPIRED | 403 | The bearer token belongs to an account whose 3-day trial has expired. |
| METHOD_NOT_ALLOWED | 405 | The request method is not GET or POST. |
| SSL_ERROR | 422 | The target failed TLS certificate validation. |
| DNS_ERROR | 422 | DNS resolution failed during navigation. |
| CONNECTION_FAILED | 422 | Connection was refused, reset, timed out, or was otherwise unreachable. |
| TARGET_HTTP_ERROR | 422 | The final response after redirects was not HTTP 200. |
| TIMEOUT | 504 | Navigation exceeded the configured timeout. |
| RENDER_FAILED | 500 | Page load succeeded, but PDF or image rendering failed. |
| INTERNAL_ERROR | 500 | An unexpected internal error interrupted the conversion. |
400 INVALID_OPTION
{
"error": {
"code": "INVALID_OPTION",
"message": "Invalid value for \"width\". Expected an integer between 16 and 5000.",
"details": {
"parameter": "width"
}
}
}401 UNAUTHORIZED
{
"error": {
"code": "UNAUTHORIZED",
"message": "You must be signed in to access this resource."
}
}403 TRIAL_EXPIRED
{
"error": {
"code": "TRIAL_EXPIRED",
"message": "Your 3-day HTMLPress trial has expired. Email info@bespokeweb.nl to continue."
}
}405 METHOD_NOT_ALLOWED
{
"error": {
"code": "METHOD_NOT_ALLOWED",
"message": "Only GET and POST requests are supported for this endpoint."
}
}422 TARGET_HTTP_ERROR
{
"error": {
"code": "TARGET_HTTP_ERROR",
"message": "The target page responded with status 404 (Not Found). Only pages that respond with status 200 can be converted.",
"details": {
"targetStatus": 404,
"url": "https://example.com/nope"
}
}
}500 RENDER_FAILED
{
"error": {
"code": "RENDER_FAILED",
"message": "Failed to render the page as a PDF."
}
}504 TIMEOUT
{
"error": {
"code": "TIMEOUT",
"message": "The target page did not finish loading within the 30000 ms timeout."
}
}