Menu
Sign up
HTML to image API

HTML to image: your HTML and CSS, rendered to PNG in real Chrome

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.

Try it: HTML and CSS to PNG, no account

Edit the HTML and CSS, then render. You get a 1200×630 PNG from the same Chrome that renders API images.

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.

How it works in the API

Two calls to start, one call per image after that.

  1. Save the template once. POST /v1/templates with your HTML, CSS, size and the names of the fields that change. Fields go into the HTML as {{ name }}; the HTML is sanitised on save.
  2. Render an image. POST /v1/images with the template slug, the field values and a format. With ?wait=true the URL of the finished file comes back on the same connection.
  3. Use the URL. In og:image, an email, a feed or a download. The same fields again return the existing file and use no quota.

While you design, POST /v1/templates/preview renders a draft without saving it and uses no quota.

Code: curl, JavaScript, PHP and Python

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"])

What comes back

{
    "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
}

Output formats

formatWhat 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.

Why real Chrome, and not a library

There are three common ways to turn HTML into an image yourself. Each works; each has a cost that shows up later.

OptionCSS it rendersWhat 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.

Limits, plainly

What it costs

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.

Questions

What is an HTML to image API?

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.

How do I convert HTML to PNG with an API?

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.

Can I turn HTML into a PDF instead?

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.

Does it support CSS grid, flexbox and custom fonts?

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.

Can the HTML run JavaScript?

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.

Is there a free HTML to image API?

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.

How is this different from HTMLCSStoImage?

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.