Turning HTML into an image sounds simple until you try it. Client-side libraries like html2canvas re-implement the browser's renderer and get CSS wrong in small ways: missing shadows, broken fonts, images that fail CORS. Running headless Chrome yourself renders correctly but means operating a browser: memory limits, cold starts, fonts installed on the server, and a queue when traffic spikes. Here, one request renders your HTML in a current Chrome and returns the image.
Render HTML to PNG
Send the markup as html. The viewport size is the image size:
curl -X POST "https://shotkit.net/api/take" \
-H "X-Access-Key: YOUR_ACCESS_KEY" \
-H "Content-Type: application/json" \
-d '{"html":"<div style=\"display:grid;place-items:center;width:800px;height:400px;background:#fef3c7;font:700 56px Georgia,serif;color:#78350f\">Hello from HTML</div>","viewport_width":800,"viewport_height":400,"format":"png"}' \
--fail-with-body -o shot.pngUse POST with a JSON body for real templates. A long query string runs into URL length limits.
Get exactly the element you want
Rather than matching the viewport to your design, capture one element with selector. The image is cropped to that element's box. Add omit_background for a transparent PNG, and device_scale_factor for retina output:
curl -X POST "https://shotkit.net/api/take" \
-H "X-Access-Key: YOUR_ACCESS_KEY" \
-H "Content-Type: application/json" \
-d '{"html":"<div class=\"badge\" style=\"display:inline-flex;gap:8px;align-items:center;padding:10px 18px;border-radius:999px;background:#111827;color:#fff;font:600 20px system-ui\">✓ All checks passed</div>","selector":".badge","format":"png","omit_background":true,"device_scale_factor":3}' \
--fail-with-body -o shot.pngPick the format
| Format | Use it for |
|---|---|
png |
Text, UI, flat colors, transparency. Lossless. |
jpg |
Photos and gradients, where a small file matters more than perfect edges. |
webp |
The web: smaller than PNG and JPG at the same quality, supported by every current browser. |
avif |
The smallest files, for modern browsers. |
tiff |
Print and imaging pipelines that require it. |
image_quality (0–100) tunes the lossy formats, and image_width or image_height resize the result while keeping its ratio.
Things to know
- Absolute URLs. The HTML has no origin, so
src="/logo.png"can't resolve. Use full URLs or inline images asdata:URLs. - Fonts. Google Fonts and
@font-facewith absolute URLs load normally. Add"wait_until": ["networkidle0"]so the capture waits for them. - JavaScript runs. Load Tailwind's CDN build, a charting library or your own script; the capture happens after the page settles. For content that appears later, use
wait_for_selector. - Size limits. The viewport goes up to 7680 px on each side and
device_scale_factorup to 5. For tall content,full_page: truecaptures the whole document.
Frequently asked questions
Can I use Tailwind CSS in the HTML?
Yes. Either inline your compiled CSS in a <style> tag, which is fastest, or include <script src="https://cdn.tailwindcss.com"></script> and let it build the styles in the page.
Does it render emoji and non-Latin scripts?
Yes. Chrome renders emoji and scripts such as Chinese, Japanese, Arabic and Cyrillic. For a specific look, load the font you want instead of relying on system fonts.
How is this different from html2canvas?
html2canvas runs in your user's browser and redraws the DOM onto a canvas with its own approximation of CSS, so complex layouts often come out differently. This renders the HTML in Chrome itself and takes a real screenshot, so the image matches what a browser shows.
Can I get a PDF instead of an image?
Yes, set format to pdf. See HTML to PDF for page sizes, margins and page breaks.