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:
- Markdown to HTML using Python-Markdown with extensions:
tables-- pipe-delimited tablesfenced_code-- triple-backtick code blockscodehilite-- syntax highlightingtoc-- table of contents via[TOC]marker-
attr_list-- HTML attributes via{.class #id}syntax -
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)#
{
"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#
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#
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#
$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#
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.