Every few months I find myself needing to generate images from HTML — OG cards for a blog, email template previews, dashboard exports, report cover pages. And every time, the path is the same: set up Puppeteer, configure it for headless, handle the Docker sandbox issues, write the render logic, deal with a crash at 2am.
There's a simpler way.
What the render endpoint does
SnapAPI's /v1/render endpoint accepts raw HTML and returns a pixel-perfect PNG, JPEG, or WebP. One POST, one image. No browser to install, no sandbox flags, no cold-start.
curl -X POST 'https://snapapi.tech/v1/render' \
-H 'x-api-key: YOUR_KEY' \
-H 'Content-Type: application/json' \
-d '{
"html": "<div style=\"background:#0f172a;color:#22c55e;padding:80px;font-size:48px;font-family:sans-serif\">Hello, world</div>",
"width": 1200,
"height": 630,
"format": "png"
}' \
--output card.png
That's the complete workflow.
Three real use cases
1. Dynamic OG card generation
Instead of uploading a static image for every blog post, generate one at publish time using the post's title and description:
const SnapAPI = require('snapapi-sdk');
const fs = require('fs');
const client = new SnapAPI(); // reads SNAPAPI_KEY from env
async function generateOGCard(post) {
const html = `
<!DOCTYPE html><html><head><style>
* { margin:0; padding:0; box-sizing:border-box; }
body {
width:1200px; height:630px;
background:#0f172a; color:#f1f5f9;
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
display:flex; flex-direction:column;
justify-content:center; padding:80px;
}
h1 { font-size:52px; font-weight:800; margin-bottom:20px; line-height:1.15; }
p { font-size:24px; color:#94a3b8; }
.brand { position:absolute; bottom:40px; right:60px; font-size:16px; color:#475569; }
</style></head>
<body>
<h1>${post.title}</h1>
<p>${post.description.slice(0, 120)}</p>
<span class="brand">yourblog.com</span>
</body></html>`;
const png = await client.render(html, { width: 1200, height: 630 });
fs.writeFileSync(`og/${post.slug}.png`, png);
return `https://yourblog.com/og/${post.slug}.png`;
}
Run this at publish time. Every post gets a correctly sized, on-brand OG image without touching Figma or Canva.
2. Email template preview rendering
Before sending a campaign, render each template variant to PNG and review them as images:
async function previewEmailTemplate(htmlTemplate) {
// Render at 600px wide (standard email width)
const png = await client.render(htmlTemplate, { width: 600, height: 400 });
fs.writeFileSync('email-preview.png', png);
console.log('Preview saved — open email-preview.png to review');
}
No email client setup. No browser. Just an image you can review or attach to a Slack message for approval.
3. Dashboard export for reports
Render a data visualization as an image for a PDF report or Slack digest:
const chartHtml = `
<div style="background:white;padding:40px;width:800px;font-family:sans-serif">
<h2 style="margin-bottom:20px">Weekly Signups</h2>
<div style="display:flex;align-items:flex-end;gap:8px;height:200px">
${[42, 58, 35, 71, 89, 63, 94].map(v =>
`<div style="background:#22c55e;width:40px;height:${v * 2}px;border-radius:4px 4px 0 0"></div>`
).join('')}
</div>
<div style="display:flex;gap:8px;margin-top:8px;font-size:12px;color:#64748b">
${['Mon','Tue','Wed','Thu','Fri','Sat','Sun'].map(d =>
`<div style="width:40px;text-align:center">${d}</div>`
).join('')}
</div>
</div>`;
const chartPng = await client.render(chartHtml, { width: 800, height: 320 });
fs.writeFileSync('weekly-chart.png', chartPng);
The image drops straight into a PDF, email, or Slack attachment.
The three OG card gotchas
If you've tried this before and got a broken image, it's usually one of:
-
Web fonts don't load —
@import url(...)won't work in a server-render context. Use system fonts or base64-encode your font directly in the HTML. -
Relative paths fail — any
src="/images/..."will 404. All URLs must be absolute. -
Dimensions in the HTML must match the API call — if you request
width:1200,height:630but the root element in your HTML is only 800px wide, you'll get padding or cropping. Set the root element dimensions explicitly in CSS.
Install
npm install snapapi-sdk
const SnapAPI = require('snapapi-sdk');
const client = new SnapAPI(); // SNAPAPI_KEY from env
const png = await client.render(html, { width: 1200, height: 630 });
Full reference: snapapi.tech/render-api
Free API key
100 renders/month · No credit card · Active in 30 seconds
Top comments (0)