---
seo_title: HTML to PDF API: Convert HTML Files to PDF | EnConvert
meta_desc: Convert HTML to PDF with a REST API. POST /v1/convert/html-to-pdf renders HTML files to PDF with WeasyPrint, using custom page sizes, margins, headers, and footers.
keywords: html to pdf api, convert html to pdf api, html file to pdf rest api, html to pdf conversion api, weasyprint html to pdf api, html to pdf api python, html to pdf api node js, generate pdf from html api
---

# HTML to PDF API

The HTML to PDF API converts an uploaded HTML file into a high-quality PDF using WeasyPrint rendering. Send a `.html` or `.htm` file to `POST /v1/convert/html-to-pdf` and the rendered PDF is returned synchronously: raw bytes by default, or JSON metadata with a presigned download URL when `direct_download=false`. Page size, margins, orientation, headers, footers, and grayscale output are all controlled via `pdf_options`.

---

## Endpoint

```
POST /v1/convert/html-to-pdf
```

**Content-Type:** `multipart/form-data`

**Accepted input:** `.html` or `.htm` files (UTF-8 encoded)

**Output format:** `.pdf` (`application/pdf`)

---

## Authentication

Requires either a private API key or a JWT token from a public key.

```
X-API-Key: sk_your_private_key
```

Or:

```
Authorization: Bearer <jwt_token>
```

---

## Request Parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `file` | file | Yes | -- | The `.html` or `.htm` file to convert. Must be UTF-8 encoded. |
| `output_filename` | `string` | No | Input filename | Custom output filename. The `.pdf` extension is added automatically. |
| `direct_download` | `boolean` | No | `true` | When `true`, returns raw PDF bytes. When `false`, returns JSON metadata with a presigned download URL. |
| `pdf_options` | `string` | No | `null` | JSON string containing PDF configuration options. See below. |

### PDF Options

Pass as a JSON string in the `pdf_options` form field. All fields are optional.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page_size` | `string` | `"A4"` | Named page size. Ignored when both `page_width` and `page_height` are set. |
| `page_width` | `float` | `null` | Custom page width in millimeters. Both `page_width` and `page_height` must be set together. |
| `page_height` | `float` | `null` | Custom page height in millimeters. |
| `orientation` | `string` | `"portrait"` | `"portrait"` or `"landscape"`. |
| `margins` | `object` | `{"top": 10, "bottom": 10, "left": 10, "right": 10}` | Page margins in millimeters. |
| `grayscale` | `boolean` | `false` | Convert the output to grayscale via Ghostscript post-processing. |
| `header` | `object` | `null` | Page header. Format: `{"content": "<text>", "height": 15}`. Supports template variables. |
| `footer` | `object` | `null` | Page footer. Same format as header. |

**Supported page sizes:** `A0`, `A1`, `A2`, `A3`, `A4`, `A5`, `A6`, `B0`, `B1`, `B2`, `B3`, `B4`, `B5`, `Letter`, `Legal`, `Tabloid`, `Ledger`

**Header/footer template variables:** `{{page}}`, `{{total_pages}}`, `{{date}}`, `{{title}}`, `{{url}}`

---

## Conversion Details

- Uses **WeasyPrint** for CSS-based PDF rendering
- The `pdf_options` are translated into CSS `@page` rules injected into the HTML before rendering
- WeasyPrint respects the document's own CSS styles in addition to the injected page rules
- All rendering is synchronous and server-side

<div class="alert alert-info">
<strong>Note:</strong> External resources referenced by URL in the HTML (images, stylesheets, fonts) may not resolve. For best results, use inline CSS and base64-encoded images, or ensure all resources are publicly accessible.
</div>

---

## Response

### Direct Download (`direct_download=true`, default)

```
HTTP 200 OK
Content-Type: application/pdf
Content-Disposition: inline; filename="document_20260405_123456789.pdf"
```

Returns raw PDF bytes.

### Metadata Response (`direct_download=false`)

```json
{
    "presigned_url": "https://spaces.example.com/...",
    "object_key": "env/files/{project_id}/html-to-pdf/document_20260405_123456789.pdf",
    "filename": "document_20260405_123456789.pdf",
    "file_size": 45678,
    "conversion_time_seconds": 1.2
}
```

---

## Code Examples

### Python

```python
import requests
import json

with open("report.html", "rb") as f:
    response = requests.post(
        "https://api.enconvert.com/v1/convert/html-to-pdf",
        headers={"X-API-Key": "sk_your_private_key"},
        files={"file": ("report.html", f, "text/html")},
        data={
            "pdf_options": json.dumps({
                "page_size": "A4",
                "orientation": "portrait",
                "margins": {"top": 20, "bottom": 20, "left": 15, "right": 15}
            })
        }
    )

with open("report.pdf", "wb") as out:
    out.write(response.content)
```

### Node.js

```javascript
const form = new FormData();
form.append("file", fs.createReadStream("report.html"));
form.append("pdf_options", JSON.stringify({
    page_size: "A4",
    orientation: "portrait",
    margins: { top: 20, bottom: 20, left: 15, right: 15 }
}));

const response = await fetch("https://api.enconvert.com/v1/convert/html-to-pdf", {
    method: "POST",
    headers: { "X-API-Key": "sk_your_private_key" },
    body: form
});

fs.writeFileSync("report.pdf", Buffer.from(await response.arrayBuffer()));
```

### PHP

```php
$ch = curl_init("https://api.enconvert.com/v1/convert/html-to-pdf");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["X-API-Key: sk_your_private_key"],
    CURLOPT_POSTFIELDS => [
        "file" => new CURLFile("report.html", "text/html"),
        "pdf_options" => json_encode([
            "page_size" => "A4",
            "margins" => ["top" => 20, "bottom" => 20, "left" => 15, "right" => 15]
        ])
    ]
]);
$pdf = curl_exec($ch);
curl_close($ch);
file_put_contents("report.pdf", $pdf);
```

### Go

```go
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)

part, _ := writer.CreateFormFile("file", "report.html")
file, _ := os.Open("report.html")
io.Copy(part, file)

writer.WriteField("pdf_options", `{"page_size":"A4","margins":{"top":20,"bottom":20,"left":15,"right":15}}`)
writer.Close()

req, _ := http.NewRequest("POST", "https://api.enconvert.com/v1/convert/html-to-pdf", body)
req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("X-API-Key", "sk_your_private_key")
resp, _ := http.DefaultClient.Do(req)
```

---

## Error Responses

| Status | Condition |
|--------|-----------|
| `400 Bad Request` | File is not a `.html` or `.htm` file |
| `400 Bad Request` | Invalid HTML encoding (expected UTF-8) |
| `400 Bad Request` | HTML to PDF conversion failed |
| `400 Bad Request` | Invalid `pdf_options` JSON |
| `401 Unauthorized` | Missing or invalid API key / JWT token |
| `402 Payment Required` | Monthly ops allowance exhausted |
| `402 Payment Required` | Storage limit reached |
| `413 Payload Too Large` | File exceeds plan's maximum file size |

---

## Limits

| Limit | Value |
|-------|-------|
| Max file size | Plan-dependent (Founding: 5 MB) |
| Input encoding | UTF-8 only |
| Header/footer content | 2000 characters max |
| Scale range | 0.1 -- 2.0 |
| Monthly conversions | Plan-dependent |

## Frequently asked questions

### How do I convert HTML to PDF with a REST API?

Send a `multipart/form-data` request to `POST /v1/convert/html-to-pdf` with your `.html` or `.htm` file in the `file` field, authenticated via `X-API-Key` or a Bearer JWT. By default the response is the raw PDF bytes with `Content-Type: application/pdf`.

### Can I set a custom page size, margins, or orientation for the PDF?

Yes. Pass a JSON string in the `pdf_options` form field with `page_size` (e.g. `A4`, `Letter`, `Legal`), `margins` in millimeters, and `orientation` (`portrait` or `landscape`). For non-standard dimensions, set `page_width` and `page_height` together in millimeters.

### How do I add page numbers or headers and footers to the generated PDF?

Set `header` or `footer` in `pdf_options` as `{"content": "<text>", "height": 15}`. The content supports the template variables `{{page}}`, `{{total_pages}}`, `{{date}}`, `{{title}}`, and `{{url}}`, and is limited to 2000 characters.

### Why are images, fonts, or stylesheets missing from my PDF output?

External resources referenced by URL in the HTML may not resolve during server-side rendering. Use inline CSS and base64-encoded images, or make sure all referenced resources are publicly accessible.

### Can I get a download URL instead of raw PDF bytes?

Yes. Set `direct_download=false` and the endpoint returns JSON metadata including a `presigned_url`, `object_key`, `filename`, `file_size`, and `conversion_time_seconds` instead of the PDF body.
