---
seo_title: Markdown to HTML API for Rendering Markdown Files | EnConvert
meta_desc: Render Markdown to HTML via POST /v1/convert/markdown-to-html. Get a complete styled HTML page with dark mode, tables, code blocks, and TOC support.
keywords: markdown to html api, render markdown to html api, convert markdown to html, markdown to html converter api, markdown to html rest api, md to html api, markdown to html python requests, markdown rendering api
---

# Markdown to HTML API

The Markdown to HTML API converts a Markdown file to a fully styled HTML document with a single request to `POST /v1/convert/markdown-to-html`. Upload a `.md` or `.markdown` file via multipart form data and receive a complete, self-contained HTML page with embedded CSS, a responsive layout, and a built-in dark mode toggle. The result is ready to open in any browser. The HTML is returned directly by default, or set `direct_download=false` for JSON metadata with a presigned download URL.

---

## Endpoint

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

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

**Accepted input:** `.md` or `.markdown` files (UTF-8 encoded)

**Output format:** `.html` (`text/html`)

---

## 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 `.html` extension is added automatically. |
| `direct_download` | `boolean` | No | `true` | When `true`, returns the HTML document directly. When `false`, returns JSON metadata with a presigned download URL. |

---

## Conversion Rules

The converter uses Python-Markdown with the following extensions enabled:

| Extension | What It Does |
|-----------|-------------|
| `tables` | Pipe-delimited Markdown tables rendered as HTML `<table>` elements |
| `fenced_code` | Triple-backtick code blocks with language identifiers (` ```python `) |
| `codehilite` | Syntax highlighting CSS classes on code blocks (via Pygments) |
| `toc` | Table of contents generation via a `[TOC]` marker in your document |
| `attr_list` | Attach HTML attributes to elements using `{.class #id}` syntax |

### Example

**Input:**

~~~markdown
# My Document

[TOC]

## Introduction

This is a **bold** and *italic* example.

## Data Table

| Name  | Age | City   |
|-------|-----|--------|
| Alice | 30  | London |
| Bob   | 25  | Paris  |

## Code Sample

```python
def hello():
    print("Hello, world!")
```
~~~

**Output:** A complete `<!DOCTYPE html>` document with:

- The rendered Markdown inside a `<body>` with max-width 800px, centered layout
- Styled tables, code blocks, links, and headings
- A clickable table of contents generated from the headings
- A dark mode toggle button (top-right corner) that persists the user's preference to `localStorage`

### Output Styling

The generated HTML includes an embedded `<style>` block with:

- **Light and dark themes** via CSS custom properties, toggled by a `data-theme` attribute
- **System font stack** (`-apple-system, BlinkMacSystemFont, Segoe UI, Arial, sans-serif`)
- **Responsive layout** with 800px max-width and centered content
- **Line height** of 1.6 for readability
- **Styled elements**: code blocks (with background), tables (with borders and striped rows), links, and block quotes

<div class="alert alert-info">
<strong>Note on syntax highlighting:</strong> The <code>codehilite</code> extension adds Pygments CSS class names to code blocks, but no Pygments stylesheet is bundled in the output. Code blocks will have correct structure and basic styling, but language-specific color highlighting requires adding a <a href="https://pygments.org/styles/" target="_blank">Pygments CSS theme</a> to the page. The built-in styling provides background and font styling for all code blocks regardless.
</div>

---

## Response

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

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

Returns the full HTML document. Since the `Content-Disposition` is `inline`, browsers will render the page directly.

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

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

---

## Code Examples

### Python

```python
import requests

with open("README.md", "rb") as f:
    response = requests.post(
        "https://api.enconvert.com/v1/convert/markdown-to-html",
        headers={"X-API-Key": "sk_your_private_key"},
        files={"file": ("README.md", f, "text/markdown")}
    )

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

### Node.js

```javascript
const form = new FormData();
form.append("file", fs.createReadStream("README.md"));

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

const html = await response.text();
fs.writeFileSync("README.html", html);
```

### PHP

```php
$ch = curl_init("https://api.enconvert.com/v1/convert/markdown-to-html");
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")]
]);
$html = curl_exec($ch);
curl_close($ch);
file_put_contents("README.html", $html);
```

### 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.Close()

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

### JavaScript -- Browser (Public Key)

```javascript
const tokenRes = await fetch("https://api.enconvert.com/v1/auth/token", {
    method: "POST",
    headers: { "X-API-Key": "pk_your_public_key" }
});
const { token } = await tokenRes.json();

const form = new FormData();
form.append("file", fileInput.files[0]);

const response = await fetch("https://api.enconvert.com/v1/convert/markdown-to-html", {
    method: "POST",
    headers: { "Authorization": `Bearer ${token}` },
    body: form
});

const data = await response.json();
window.open(data.presigned_url, "_blank");
```

---

## 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 HTML conversion failed |
| `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` |
| Monthly conversions | Plan-dependent |

---

## Frequently asked questions

### How do I render Markdown to HTML with a REST API?

Send a `multipart/form-data` POST request to `POST /v1/convert/markdown-to-html` with your `.md` or `.markdown` file in the `file` field, authenticating with an `X-API-Key` header or a JWT `Authorization: Bearer` token. The response is a complete HTML document by default.

### Does the Markdown to HTML API support tables, code blocks, and a table of contents?

Yes. The converter uses Python-Markdown with the `tables`, `fenced_code`, `codehilite`, `toc`, and `attr_list` extensions enabled. Add a `[TOC]` marker in your document to generate a clickable table of contents from the headings.

### Why don't my code blocks have language-specific syntax highlighting colors?

The `codehilite` extension adds Pygments CSS class names to code blocks, but no Pygments stylesheet is bundled in the output. Add a Pygments CSS theme to the page for color highlighting; the built-in styling still provides background and font styling for all code blocks.

### Can I convert Markdown to HTML directly from the browser?

Yes. Exchange a public key (`pk_...`) for a JWT via `POST /v1/auth/token`, then call the endpoint with an `Authorization: Bearer` header and open the returned `presigned_url`.

### Does the generated HTML include dark mode?

Yes. The output includes light and dark themes via CSS custom properties, toggled by a `data-theme` attribute, with a dark mode toggle button that persists the user's preference to `localStorage`.
