HTML to image in Python

Three ways to turn an HTML string or file into a PNG from Python, all run on the same 600×315 social card. Tested with html2image 2.0.7 and Playwright for Python on Chromium.

Quick answer

OptionPick it when
html2image
Drives your installed Chrome
Quick scripts on a machine that already has Chrome. Fixed-size captures only: no element cropping, no waiting for JavaScript.
Playwright
Bundled Chromium
You need control: crop to an element, retina scale, wait for fonts or charts, transparent backgrounds.
An API
Chromium on someone else's server
Servers, Lambda or CI where you don't want a browser in your image.

imgkit isn't on the list: it wraps wkhtmltoimage, which was archived in 2023 and renders with a 2012-era WebKit (no flexbox gaps, no grid, no modern CSS). See the wkhtmltopdf alternative guide. Pillow can't render HTML at all, it only draws text and shapes you position yourself.

The test card

Save this as card.html. It's a typical generated image: a fixed-size box with a gradient and flexbox layout.

<!doctype html>
<html><head><meta charset="utf-8"><style>
  body { margin: 0; font-family: sans-serif; }
  .card { width: 600px; height: 315px; box-sizing: border-box; padding: 40px;
          display: flex; flex-direction: column; justify-content: space-between;
          background: linear-gradient(135deg, #4f46e5, #0ea5e9); color: #fff; }
  h1 { margin: 0; font-size: 40px; }
</style></head><body>
  <div class="card"><h1>Release 2.4 is out</h1><p>Faster builds, smaller bundles.</p></div>
</body></html>

1. html2image

pip install html2image

It finds an installed Chrome, Chromium or Edge and calls its headless --screenshot mode, so there's no browser download, but you do need one on the machine.

from html2image import Html2Image

hti = Html2Image(size=(600, 315), output_path=".")
hti.screenshot(html_file="card.html", save_as="card-h2i.png")

# From strings instead of a file:
hti.screenshot(html_str="<h1>Hello</h1>", css_str="h1 { color: red; }", save_as="hello.png")

Result: a 600×315 PNG of the card. Things to know:

2. Playwright

pip install playwright
playwright install chromium
from pathlib import Path
from playwright.sync_api import sync_playwright

html = Path("card.html").read_text(encoding="utf-8")
with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(device_scale_factor=2)
    page.set_content(html, wait_until="networkidle")
    page.locator(".card").screenshot(path="card-pw.png")
    browser.close()

Result: a 1200×630 PNG (600×315 at 2×), cropped exactly to the card whatever the window size, so it's ready to use as an og:image. Variations:

3. An API (no browser to install)

Shotmint renders in the same Chromium, so you get the same pixels, but your code only makes an HTTP request. The selector option crops to an element like Playwright's locator:

import os, requests

html = open("card.html", encoding="utf-8").read()
r = requests.post("https://shotmint.creasework.com/v1/screenshot",
                  headers={"X-API-Key": os.environ["SHOTMINT_KEY"]},
                  json={"html": html, "selector": ".card", "device_scale_factor": 2},
                  timeout=60)
r.raise_for_status()
open("card-api.png", "wb").write(r.content)

Result: the same 1200×630 PNG. Other options: format (png, jpeg, webp), quality, width/height for the viewport, full_page, omit_background for transparency, delay in milliseconds to wait for scripts, and "url": "https://..." to capture a live page instead of HTML. Images and fonts must be reachable from the internet or inlined. See the Python API guide for error handling.

Side by side

FeatureSupport
Crop to an elementPlaywright yes, API yes (selector)
html2image: no, fixed window size
Retina (2×)Playwright yes, API yes
html2image: only via a Chrome flag
Wait for JavaScriptPlaywright yes, API yes (delay)
html2image: captures right after load
InstallAPI: requests only
html2image: plus a Chrome; Playwright: plus its Chromium

Which one?

For a one-off script on your laptop, html2image is the least setup. For anything generated in bulk or shown publicly (og:images, certificates, charts), use Playwright for the element crop and 2× output, or the API if you'd rather not run Chromium on your server. No code at all? Paste your HTML into the free HTML to image converter. Need social cards from a template? See dynamic og:images. Node.js instead: HTML to image in Node.js. PDFs: HTML to PDF in Python.

Try it with 100 free renders

No signup, no card. Get a key in one click, then buy credits only if you need more (from €5 for 2,000, never expire).

Get a free API key Full API reference