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:
  2. tables -- pipe-delimited tables
  3. fenced_code -- triple-backtick code blocks
  4. codehilite -- syntax highlighting
  5. toc -- table of contents via [TOC] marker
  6. attr_list -- HTML attributes via {.class #id} syntax

  7. HTML to PDF using WeasyPrint with a built-in stylesheet that provides:

  8. System font stack, 800px max-width centered layout
  9. Styled code blocks, tables, blockquotes, and images
  10. 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.