TexToolsAI

Markdown to HTML in Python: Library or API?

By VeloForge Labs · · 2 min read

Python has several good ways to turn Markdown into HTML. This guide compares the library route with the API route and shows the safe way to do either.

Option 1: a Python library

The two most common libraries are Python-Markdown (installed as markdown) and markdown-it-py.

import markdown

html = markdown.markdown("# Hello\n\n**Bold** text", extensions=["tables", "fenced_code"])
print(html)

Extensions are how Python-Markdown gains features such as tables and fenced code blocks. markdown-it-py follows the CommonMark specification closely and has a plugin system.

The catch: sanitizing

These libraries convert Markdown. They do not make the output safe. Python-Markdown's documentation says so directly: raw HTML in the input passes through to the output. If you display user-written Markdown, run the result through a sanitizer.

The usual Python choice is nh3, a binding for the Rust library ammonia, or the older bleach library, which has been deprecated by its maintainers. A typical pipeline:

import markdown
import nh3

def render(user_markdown: str) -> str:
    raw_html = markdown.markdown(user_markdown, extensions=["tables", "fenced_code"])
    return nh3.clean(raw_html)

You now have two dependencies to keep updated and one policy to configure.

Option 2: an HTTP API

If you would rather have one request that does both steps, call an endpoint that parses and sanitizes:

import os
import requests

def render(user_markdown: str) -> str:
    response = requests.post(
        "https://texttoolsai.org/api/convert",
        json={"markdown": user_markdown, "options": {"gfm": True}},
        headers={"Authorization": "Bearer " + os.environ["TEXTOOLSAI_API_KEY"]},
        timeout=10,
    )
    response.raise_for_status()
    return response.json()["html"]

The response has html, which is already sanitized, and a meta block with latency information. Parameter details are in the API reference.

Comparing the options

Library plus sanitizer HTTP API
Dependencies Two or more One (requests or httpx)
Network required No Yes
Latency Microseconds to milliseconds A network round trip
Policy control Full Fixed allow-list
Same output across languages Only if you configure each one the same Yes

Choose the library when you need offline operation or fine control over the allow-list. Choose the API when several services in different languages need the same behavior, or when you want to avoid maintaining sanitizer configuration.

Handling failures

Network calls fail. Wrap the request, set a timeout, and decide what to do on a non-200 response. Authentication problems (401, 403) will not fix themselves on retry, while a 429 means you hit a limit. The details are in how to handle API errors and rate limits.

Cache the result

If the same Markdown is converted repeatedly, store the HTML. Key a cache on a hash of the Markdown string and you will never convert the same text twice.

Summary

For a script or an offline tool, Python-Markdown plus nh3 is hard to beat. For a service that renders user content and wants one safe call, use an API. In both cases, remember Markdown is not safe by default, and sanitize the output.