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.