---
seo_title: Markdown to PDF API: Convert Markdown Files to PDF | EnConvert
meta_desc: Convert Markdown to PDF via REST API. POST /v1/convert/markdown-to-pdf turns .md files into styled PDFs with tables, code highlighting, headers, and footers.
keywords: markdown to pdf api, convert markdown to pdf api, md to pdf rest api, markdown to pdf converter api, readme to pdf api, markdown to pdf with syntax highlighting, markdown to pdf api python, markdown to pdf api node js
---

# Markdown to PDF API

The Markdown to PDF API converts a Markdown file into a styled PDF document via `POST /v1/convert/markdown-to-pdf`. Your `.md` or `.markdown` file is first rendered to HTML (with support for tables, fenced code blocks with syntax highlighting, and a table of contents), then converted to PDF using WeasyPrint. The response is raw PDF bytes by default, or JSON metadata with a presigned download URL when `direct_download=false`, and custom page sizes, margins, headers, footers, and grayscale output are available via `pdf_options`.

---

## Endpoint

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

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

**Accepted input:** `.md` or `.markdown` 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 `.md` or `.markdown` 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. |
| `page_width` | `float` | `null` | Custom page width in millimeters. Both width and 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. |
| `header` | `object` | `null` | Page header. Format: `{"content": "<text>", "height": 15}`. |
| `footer` | `object` | `null` | Page footer. Same format as header. |

**Supported page sizes:** `A0`-`A6`, `B0`-`B5`, `Letter`, `Legal`, `Tabloid`, `Ledger`

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

---

## Conversion Details

The conversion is a two-step process:

1. **Markdown to HTML** using Python-Markdown with extensions:
   - `tables` -- pipe-delimited tables
   - `fenced_code` -- triple-backtick code blocks
   - `codehilite` -- syntax highlighting
   - `toc` -- table of contents via `[TOC]` marker
   - `attr_list` -- HTML attributes via `{.class #id}` syntax

2. **HTML to PDF** using WeasyPrint with a built-in stylesheet that provides:
   - System font stack, 800px max-width centered layout
   - Styled code blocks, tables, blockquotes, and images
   - Responsive image sizing (`max-width: 100%`)

The `pdf_options` are injected as CSS `@page` rules before rendering.

---

## Response

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

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

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

```json
{
    "presigned_url": "https://spaces.example.com/...",
    "object_key": "env/files/{project_id}/markdown-to-pdf/readme_20260405_123456789.pdf",
    "filename": "readme_20260405_123456789.pdf",
    "file_size": 34567,
    "conversion_time_seconds": 0.8
}
```

---

## Code Examples

### Python

```python
import requests
import json

with open("README.md", "rb") as f:
    response = requests.post(
        "https://api.enconvert.com/v1/convert/markdown-to-pdf",
        headers={"X-API-Key": "sk_your_private_key"},
        files={"file": ("README.md", f, "text/markdown")},
        data={
            "pdf_options": json.dumps({
                "page_size": "Letter",
                "margins": {"top": 25, "bottom": 25, "left": 20, "right": 20},
                "footer": {"content": "Page {{page}} of {{total_pages}}", "height": 10}
            })
        }
    )

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

### Node.js

```javascript
const form = new FormData();
form.append("file", fs.createReadStream("README.md"));
form.append("pdf_options", JSON.stringify({
    page_size: "Letter",
    footer: { content: "Page {{page}} of {{total_pages}}", height: 10 }
}));

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

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

### PHP

```php
$ch = curl_init("https://api.enconvert.com/v1/convert/markdown-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("README.md", "text/markdown"),
        "pdf_options" => json_encode(["page_size" => "Letter", "grayscale" => true])
    ]
]);
$pdf = curl_exec($ch);
curl_close($ch);
file_put_contents("README.pdf", $pdf);
```

### Go

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

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

writer.WriteField("pdf_options", `{"page_size":"Letter","grayscale":true}`)
writer.Close()

req, _ := http.NewRequest("POST", "https://api.enconvert.com/v1/convert/markdown-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 `.md` or `.markdown` file |
| `400 Bad Request` | Invalid Markdown encoding (expected UTF-8) |
| `400 Bad Request` | Markdown 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 |
| Accepted extensions | `.md`, `.markdown` |
| Header/footer content | 2000 characters max |
| Monthly conversions | Plan-dependent |

## Frequently asked questions

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

Send a `multipart/form-data` request to `POST /v1/convert/markdown-to-pdf` with your `.md` or `.markdown` file (UTF-8 encoded) in the `file` field, authenticated via `X-API-Key` or a Bearer JWT. By default the raw PDF bytes are returned directly.

### Does Markdown to PDF conversion support tables and code syntax highlighting?

Yes. The Markdown is rendered with Python-Markdown extensions including `tables` for pipe-delimited tables, `fenced_code` for triple-backtick code blocks, and `codehilite` for syntax highlighting, then styled by a built-in stylesheet.

### Can I add a table of contents to the generated PDF?

Yes. Place a `[TOC]` marker in your Markdown, and the `toc` extension renders it as a table of contents in the output document.

### How do I add page numbers to a Markdown PDF?

Set a `footer` (or `header`) in the `pdf_options` JSON, e.g. `{"content": "Page {{page}} of {{total_pages}}", "height": 10}`. Supported template variables are `{{page}}`, `{{total_pages}}`, `{{date}}`, `{{title}}`, and `{{url}}`.

### Why does my request return 400 Bad Request?

Common causes: the file is not `.md` or `.markdown`, the content is not UTF-8 encoded, the `pdf_options` field is not valid JSON, or the conversion itself failed. Check the specific error message in the response body.
