An HTML to image API lays your markup and CSS out in a browser and returns a picture of it. With Shareglint you save the HTML and CSS once as a template; after that every image is one HTTP request — POST /v1/images with the template name and the text that changes — and the answer is the URL of a PNG, JPEG, WebP or PDF file, usually in about a second.
It is real Chrome, not a re-implementation of CSS: grid, flexbox, gradients, shadows, emoji and right-to-left text render as they do in your browser. Try it below without an account.
Edit the HTML and CSS, then render. You get a 1200×630 PNG from the same Chrome that renders API images.
Start free — 100 images a month
The playground allows 5 renders an hour from one address and adds the Free plan watermark. Scripts, forms, iframes and SVG are removed, and so is every external URL — images and fonts from other servers do not load (data: URIs do). The same rules apply to saved templates.
Two calls to start, one call per image after that.
While you design, POST /v1/templates/preview renders a draft without saving it and uses no quota.
Each sample saves the template and renders one PNG. Replace sk_live_YOUR_KEY with your key from the dashboard.
curl
# 1. Save the template once
curl -X POST "https://shareglint.com/v1/templates" \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Article card",
"width": 1200,
"height": 630,
"fields": [
"kicker",
"title",
"author"
],
"html": "<div class=\"card\"><p class=\"kicker\">{{ kicker }}</p><div class=\"fitbox\"><h1 class=\"fit\" data-autofit>{{ title }}</h1></div><p class=\"by\">{{ author }}</p></div>",
"css": ".card{height:100%;padding:72px;display:grid;grid-template-rows:auto 1fr auto;gap:24px;background:#0f172a;color:#f8fafc;font-family:Roboto,sans-serif} .kicker{color:#fbbf24;font-size:26px;letter-spacing:.1em;text-transform:uppercase} h1{font-size:76px;line-height:1.08} .by{font-size:28px;color:#94a3b8}"
}'
# → {"id": "tpl_…", "slug": "article-card", …}
# 2. One call per image (use the slug from step 1)
curl -X POST "https://shareglint.com/v1/images?wait=true" \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "article-card",
"format": "png",
"modifications": {
"kicker": "Engineering",
"title": "How we cut our build time in half",
"author": "Ada Lovelace"
}
}'
JavaScript
const API = 'https://shareglint.com/v1';
const headers = {
Authorization: 'Bearer sk_live_YOUR_KEY',
'Content-Type': 'application/json',
};
// 1. Save the template once
const template = await fetch(`${API}/templates`, {
method: 'POST',
headers,
body: JSON.stringify({
name: "Article card",
width: 1200,
height: 630,
fields: ['kicker', 'title', 'author'],
html: "<div class=\"card\"><p class=\"kicker\">{{ kicker }}</p><div class=\"fitbox\"><h1 class=\"fit\" data-autofit>{{ title }}</h1></div><p class=\"by\">{{ author }}</p></div>",
css: ".card{height:100%;padding:72px;display:grid;grid-template-rows:auto 1fr auto;gap:24px;background:#0f172a;color:#f8fafc;font-family:Roboto,sans-serif} .kicker{color:#fbbf24;font-size:26px;letter-spacing:.1em;text-transform:uppercase} h1{font-size:76px;line-height:1.08} .by{font-size:28px;color:#94a3b8}",
}),
}).then((r) => r.json());
// 2. One call per image
const image = await fetch(`${API}/images?wait=true`, {
method: 'POST',
headers,
body: JSON.stringify({
template: template.slug,
format: 'png',
modifications: {
kicker: "Engineering",
title: "How we cut our build time in half",
author: "Ada Lovelace",
},
}),
}).then((r) => r.json());
console.log(image.url);
PHP (Laravel)
use Illuminate\Support\Facades\Http;
$api = Http::withToken('sk_live_YOUR_KEY')->baseUrl('https://shareglint.com/v1');
// 1. Save the template once
$template = $api->post('/templates', [
'name' => 'Article card',
'width' => 1200,
'height' => 630,
'fields' => ['kicker', 'title', 'author'],
'html' => '<div class="card"><p class="kicker">{{ kicker }}</p><div class="fitbox"><h1 class="fit" data-autofit>{{ title }}</h1></div><p class="by">{{ author }}</p></div>',
'css' => '.card{height:100%;padding:72px;display:grid;grid-template-rows:auto 1fr auto;gap:24px;background:#0f172a;color:#f8fafc;font-family:Roboto,sans-serif} .kicker{color:#fbbf24;font-size:26px;letter-spacing:.1em;text-transform:uppercase} h1{font-size:76px;line-height:1.08} .by{font-size:28px;color:#94a3b8}',
])->json();
// 2. One call per image
$image = $api->post('/images?wait=true', [
'template' => $template['slug'],
'format' => 'png',
'modifications' => [
'kicker' => 'Engineering',
'title' => 'How we cut our build time in half',
'author' => 'Ada Lovelace',
],
])->json();
echo $image['url'];
Python
import requests
API = "https://shareglint.com/v1"
HEADERS = {"Authorization": "Bearer sk_live_YOUR_KEY"}
# 1. Save the template once
template = requests.post(f"{API}/templates", headers=HEADERS, json={
"name": "Article card",
"width": 1200,
"height": 630,
"fields": ["kicker", "title", "author"],
"html": "<div class=\"card\"><p class=\"kicker\">{{ kicker }}</p><div class=\"fitbox\"><h1 class=\"fit\" data-autofit>{{ title }}</h1></div><p class=\"by\">{{ author }}</p></div>",
"css": ".card{height:100%;padding:72px;display:grid;grid-template-rows:auto 1fr auto;gap:24px;background:#0f172a;color:#f8fafc;font-family:Roboto,sans-serif} .kicker{color:#fbbf24;font-size:26px;letter-spacing:.1em;text-transform:uppercase} h1{font-size:76px;line-height:1.08} .by{font-size:28px;color:#94a3b8}",
}).json()
# 2. One call per image
image = requests.post(f"{API}/images?wait=true", headers=HEADERS, json={
"template": template["slug"],
"format": "png",
"modifications": {
"kicker": "Engineering",
"title": "How we cut our build time in half",
"author": "Ada Lovelace",
},
}).json()
print(image["url"])
{
"id": "img_01m08s3ez1zcv28h335g9923bw",
"kind": "image",
"status": "completed",
"format": "png",
"size": "og",
"width": 1200,
"height": 630,
"url": "https://cdn.shareglint.com/renders/img_01m08s3ez1zcv28h335g9923bw.png",
"duration_ms": 912
}
| format | What you get |
|---|---|
png |
Lossless. scale 2 (the default) doubles the pixels: a 1200×630 template gives a 2400×1260 file; scale 1 gives exactly 1200×630. |
jpeg |
Smaller files for photos; quality from 1 to 100, 85 by default. |
webp |
Smaller still, same quality setting as JPEG. |
pdf |
One page exactly the template size, backgrounds printed, text kept as vector and selectable. |
gif, mp4 |
Animated: records the template's CSS animations, up to 5 seconds; 5 renders of quota each. |
One template renders in every size preset too (og 1200×630, square 1080×1080, portrait 1080×1350, story 1080×1920, pinterest 1000×1500, youtube 1280×720) — which size each platform wants.
There are three common ways to turn HTML into an image yourself. Each works; each has a cost that shows up later.
| Option | CSS it renders | What you take on |
|---|---|---|
| Satori / @vercel/og | A subset: JSX with flexbox layout, no CSS grid | Write the card in JSX, load font files yourself, run it in your Next.js or edge function |
| wkhtmltoimage | An old Qt WebKit engine: no CSS grid, modern CSS unreliable | A tool whose project was archived in 2023, installed on your servers |
| Puppeteer or Playwright on your servers | Everything Chrome renders | Chrome's memory and crashes, fonts on every machine, a queue, and isolating any HTML you did not write |
| Shareglint | Everything Chrome renders | One HTTP request; Chrome, fonts, sandboxing, cache and storage are ours |
If your site is on Next.js and the card fits in flexbox, @vercel/og is free and runs next to your pages — use it. Running Chrome yourself is the right call when you already operate it at scale. An API is worth it when you would rather not do either.
Free: 100 images a month, no card, with a small watermark. Starter: $9 a month for 2,000 images ($4.50 per thousand), no watermark. A render you already have — same template, same fields, same format — is served from cache and uses no quota.
A web service that takes HTML and CSS, lays it out in a browser engine and returns an image of the result — PNG, JPEG, WebP or a PDF. You use it when the picture should be built from data: social cards, certificates, receipts, charts with text, email banners.
With Shareglint: save the HTML and CSS once with POST /v1/templates, then call POST /v1/images?wait=true with the template slug, the field values and "format": "png". The response holds the URL of the PNG.
Yes. "format": "pdf" returns one page exactly the size of the template, with backgrounds printed and text kept as vector, so it stays sharp and selectable.
Grid, flexbox and the rest of modern CSS work, because the renderer is Chrome. Fonts come from the families installed in the renderer (Roboto, Open Sans, Carlito, DejaVu Sans, DejaVu Serif, DejaVu Sans Mono, Ubuntu, Noto Sans CJK JP, Noto Sans CJK SC, Noto Color Emoji); uploading your own font files is not supported.
No. Templates are sanitised on save and scripts are removed, which is what makes it safe to render HTML from anyone. To capture a page that needs its scripts, use the Screenshot API with the page's URL.
The Free plan renders 100 images a month with no card, with a small watermark. The playground on this page needs no account at all.
HTMLCSStoImage takes the full HTML in every request. Shareglint stores the template once and takes only the changing fields, adds text auto-fitting and a cache that uses no quota, and comes with 153 ready-made templates. If your HTML is different on every request, their model fits better.