Features Full Page Screenshot Wait for Selector & Delay Block Cookie Banners Custom Viewport & Device Website to PDF HTML to Image Pricing Docs Blog Log In Sign Up

HTML to Image API — Render HTML to PNG, WebP, or JPEG

Every SaaS product eventually needs to generate images from dynamic data. OG cards for social sharing, PDF invoices, course certificates, personalized email headers. The content changes per user or per page, so static assets don't work. You need a template engine that outputs pixels.

Puppeteer or Playwright is the obvious path: spin up headless Chrome, load your HTML, call page.screenshot(). It works on your laptop. In production, it means managing Chromium processes, handling memory leaks, installing fonts on every server, and dealing with a 300 MB binary that doesn't fit in AWS Lambda's deployment limit.

With the html parameter, you send any HTML string and get an image back. A full Chromium instance renders it on our end. No browser to manage, no CSS restrictions, no deployment headaches.

Rendering HTML to an image with one API call

Pass your HTML via the html parameter instead of a url:

curl -X POST https://screenshotrun.com/api/v1/screenshots \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "

Hello World

Rendered in real Chromium.

", "width": 600, "height": 400, "format": "png" }'

The API queues a render job, loads your HTML in Chromium, waits for network activity to settle, and captures the viewport. The response includes a download URL for the generated image. The HTML renders exactly as it would in a Chrome browser tab, because that's literally what it is.

You can use any image format the API supports: PNG for lossless output, WebP for smaller file sizes, JPEG for photographs, or even PDF if you need a paginated document from the same template.

Full Chromium rendering, no CSS restrictions

This is where the API differs from tools like Satori and Vercel OG. Those tools convert a JSX component to SVG, not a real browser render. Satori supports a subset of CSS: flexbox works, but display: grid doesn't. position: absolute works, but calc() behaves unpredictably. Custom fonts must be registered manually and fit within a 500 KB total bundle limit.

The screenshotrun API has none of these restrictions. It runs a real Chromium instance, so everything Chrome supports is available:

<div style="display: grid; grid-template-columns: 1fr 2fr; gap: 20px; padding: 40px; font-family: 'Inter', sans-serif;">
  <div style="background: linear-gradient(135deg, #667eea, #764ba2); border-radius: 12px; padding: 30px; color: white;">
    <h2 style="margin: 0;">Monthly Report</h2>
    <p style="opacity: 0.8;">June 2026</p>
  </div>
  <div style="display: flex; flex-direction: column; gap: 12px;">
    <div style="background: #f8f9fa; border-radius: 8px; padding: 16px;">Revenue: $42,300</div>
    <div style="background: #f8f9fa; border-radius: 8px; padding: 16px;">Users: 1,847</div>
    <div style="background: #f8f9fa; border-radius: 8px; padding: 16px;">Churn: 2.1%</div>
  </div>
</div>

CSS grid, gradients, border-radius, nested flexbox, Google Fonts via @import or <link> tags in the HTML head. All of it renders because the engine is the same Chrome you use for development. Satori would silently drop the grid layout and fall back to block stacking.

Markdown to image in one call

Not every image starts from HTML. README files, changelogs, release notes, documentation snippets. These live in Markdown and converting them to HTML before sending to a screenshot API adds a build step most teams don't want.

Send Markdown directly via the markdown parameter and skip the conversion step entirely:

{
  "markdown": "# Release v2.4.0\n\n## What's new\n\n- **Batch API** for processing up to 100 URLs in one request\n- **S3 export** sends screenshots directly to your bucket\n- Dark mode support via `dark_mode` parameter\n\n## Bug fixes\n\n- Fixed timeout handling for pages over 30 seconds\n- Resolved race condition in webhook delivery",
  "width": 800,
  "height": 600,
  "format": "webp"
}

Internally, the Markdown is converted to styled HTML with sensible defaults (typography, code block highlighting, spacing), rendered in Chromium, and returned as an image. No external Markdown-to-HTML library needed on your end. Most screenshot APIs don't accept Markdown directly, so this saves a build step. One caveat: the default styling covers common cases, but if you need pixel-perfect control over layout and typography, send pre-styled HTML instead.

OG images and social cards from HTML templates

Generating Open Graph cards is the most common use case for HTML-to-image. Every page on a SaaS site needs a unique og:image, and designing them by hand doesn't scale past ten pages.

Build one HTML template with CSS, inject dynamic data (title, author, date, logo), and send it to the API at 1200x630 (the standard OG image size):

{
  "html": "<div style='width:1200px;height:630px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#0f0c29,#302b63,#24243e);color:white;font-family:system-ui;padding:60px'><div><h1 style='font-size:48px;margin:0'>How to Cache Screenshots for Faster Page Loads</h1><p style='font-size:24px;opacity:0.7;margin-top:16px'>screenshotrun.com</p></div></div>",
  "width": 1200,
  "height": 630,
  "format": "png"
}

That's a minimal example. In production, most teams build the template as a standalone HTML file with external CSS, inject variables server-side (Blade, Jinja, Handlebars, string interpolation), and send the rendered string to the API. The template can be as complex as you want: embedded logos via base64, custom fonts, charts rendered as inline SVG.

The same approach works for certificates (swap name, course title, date), email header images (swap subject line and preview text), and dynamic banners (swap product image and price). One template, thousands of unique outputs.

What Puppeteer costs you in production

Here's the equivalent Puppeteer code for rendering HTML to an image:

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1200, height: 630 });
await page.setContent(htmlString, { waitUntil: 'networkidle0' });
const screenshot = await page.screenshot({ type: 'png' });
await browser.close();
fs.writeFileSync('output.png', screenshot);

Seven lines. Looks simple. But every line hides a production problem. puppeteer.launch() starts a 300 MB Chromium process that takes 1-3 seconds cold. page.setContent() loads web fonts over the network, and networkidle0 doesn't guarantee they've rendered. browser.close() sometimes hangs, leaking the process. Run this in a loop and memory climbs until the container gets OOM-killed.

I've seen teams spend more engineering hours maintaining their Puppeteer rendering pipeline than building the templates it renders. The build-vs-buy comparison breaks down the full operational cost. For most teams generating under 50,000 images per month, the API is cheaper than the engineering time alone.

Combining html with the rest of the toolkit

The html parameter isn't a separate product. It works with every other API feature, which is something standalone tools like htmlcsstoimage.com can't match.

Set a custom viewport to control the exact output dimensions. Use full_page to capture HTML that scrolls beyond the viewport. Inject additional css to override template styles without modifying the HTML. Add a delay if your template loads external resources that need time to render.

A practical example: generating a full-page invoice as an image with wait_for_selector to ensure a chart component finishes rendering:

{
  "html": "<html><head><link rel='stylesheet' href='https://fonts.googleapis.com/css2?family=Inter:wght@400;600'></head><body>...invoice HTML with chart...</body></html>",
  "width": 800,
  "full_page": true,
  "wait_for_selector": ".chart-rendered",
  "format": "png"
}

The invoice guide covers HTML template patterns for invoices and receipts in detail, including page-break strategies when you need PDF output instead of an image.

Where htmlcsstoimage.com and wkhtmltoimage fall short

htmlcsstoimage.com (hcti.io) does one thing: HTML to image. It starts at $14/month for 200 renders and scales to $1,650/month at 500K. The rendering is real Chrome, so the output quality is comparable. But it's a single-purpose tool with no URL screenshots, no full_page capture, no cookie blocking, no viewport control, and no Markdown parameter.

wkhtmltoimage uses QtWebKit, not Chromium. That means no flexbox, no CSS grid, broken @font-face handling, and rendering artifacts on anything built after 2015. The project is effectively unmaintained. I still see tutorials recommending it, but any HTML template designed with modern CSS will render incorrectly.

Client-side libraries like html2canvas and dom-to-image re-implement CSS rendering in JavaScript Canvas. They're useful for browser-only use cases (letting users save a page section), but they're not pixel-perfect, can't handle cross-origin fonts or images, and don't run server-side. They're not a replacement for a real rendering engine.

HTML and Markdown parameter reference

Full parameter list in the API reference. Here are the HTML/Markdown-specific options:

ParameterTypeDefaultLimitPlan
htmlstring500,000 charsStarter+
markdownstring500,000 charsPro+
formatstringpngpng, jpeg, webp, avif, tiff, pdfAll (PDF: Starter+)
qualityinteger801–100All
widthinteger1280320–3840All
heightinteger800200–2160All

Every rendering option works with html: viewport dimensions, full-page capture, css and js injection, retina for 2x output, and delay or wait_for_selector for async content. Pick url, html, or markdown as the source, then layer on whatever the template needs.

Turn HTML templates into pixel-perfect images

Get your API key — free tier available

Frequently asked questions

The API uses a full Chromium engine, so it supports the same CSS features as Chrome: flexbox, CSS grid, custom properties, web fonts via @font-face, media queries, calc(), and modern layout techniques. There are no CSS subset restrictions like you would find with Satori or Vercel OG.
Yes. Use the markdown parameter instead of html. The API converts your Markdown to styled HTML internally, renders it in Chromium, and returns a screenshot. Useful for generating social cards from README files, changelogs, or documentation snippets.
The html parameter accepts up to 500,000 characters. That is enough for complex pages with inline CSS, embedded SVGs, and data tables. For anything larger, host the HTML and capture it via the url parameter instead.
Design your OG image as an HTML template with inline CSS (1200x630px is the standard size), then send it to the API with html, width: 1200, and height: 630. The API returns a production-ready PNG or WebP you can reference in your og:image meta tag.
The html and markdown parameters are available on Starter plans and above. The free tier supports URL-based screenshots in all image formats but does not include HTML or Markdown rendering.