USE CASE
SEO

OG Images That Build Themselves

You write a JSX template once. Every blog post, doc page, and landing page gets its own social preview. One API call per page.

OUTPUT PREVIEW
SCALE 1:1
Acme Engineering
@acme_eng

New on the blog: How we scaled to 1M users without adding a single server.

acme.dev/blogEngineering

How We Scaled to
1M Users

Jane Smith|Mar 15, 2026
How We Scaled to 1M Users — Acme Engineering Blog
acme.dev
Template + variables = image. That's the whole flow.RENDERED
SECTION 01

The OG Image Problem

Links without OG images get scrolled past. But making a unique image for every page means either a designer doing it by hand, or running Puppeteer on a server.

If you've tried Puppeteer, you know the deal: memory leaks, Chrome version mismatches, cold starts, zombie processes. It works until it doesn't, and then you're debugging headless Chrome at 2am.

SECTION 02

Write JSX. Get URLs.

Create a JSX template with variables for title, author, date, whatever you need. Upload it to HTMLPix. When you want an image, POST your variables and get back a signed URL.

The image renders on the first request and caches from there. The URL is valid for 5 years. You don't manage a server, update Chrome, or think about rendering.

SECTION 03

Example

FIG. 02
mint-og-url.sh
mint-og-url.sh
Shell
1# Step 1: Mint a signed image URL2curl -X POST https://api.htmlpix.com/v1/url \3  -H "Authorization: Bearer $HTMLPIX_KEY" \4  -H "Content-Type: application/json" \5  -d '{6    "templateId": "blog-post-template-id",7    "variables": {8      "title": "How We Scaled to 1M Users",9      "author": "Jane Smith",10      "date": "Mar 15, 2026",11      "category": "Engineering"12    },13    "width": 1200,14    "height": 63015  }'1617# Response:18# { "url": "https://image.htmlpix.com/v1/image?templateId=...&sig=...", "expiresAt": ... }19#20# Step 2: Use the URL directly in your <meta> tags21# <meta property="og:image" content="https://image.htmlpix.com/v1/image?..." />
Copy, paste, run. That's it.READY TO RUN
SECTION 04

Integration Snippets

app/blog/[slug]/page.tsx
TypeScript
import type { Metadata } from "next";

async function mintOgUrl(variables: Record<string, string>) {
  const res = await fetch("https://api.htmlpix.com/v1/url", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.HTMLPIX_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      templateId: process.env.BLOG_OG_TEMPLATE_ID,
      variables,
      width: 1200,
      height: 630,
    }),
  });
  const { url } = await res.json();
  return url;
}

export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await getPost(params.slug);
  const ogUrl = await mintOgUrl({
    title: post.title,
    author: post.author,
    date: post.date,
    category: post.category,
  });

  return {
    openGraph: { images: [{ url: ogUrl, width: 1200, height: 630 }] },
    twitter: { card: "summary_large_image", images: [ogUrl] },
  };
}
SECTION 05

Time & Cost Savings

Without HTMLPix

Set up Puppeteer or Playwright. Maintain a server. Debug Chrome memory leaks. Handle cold starts. Budget $50-200/mo for infra alone.

With HTMLPix

POST your variables, get a URL. First render takes 200-500ms, then it's cached. Starts at $8/month for 500 images.

Estimated Savings
No ops work
SECTION 06

Why HTMLPix

01

One Template, Every Page

The same template generates thousands of different images. Change the title, author, or date per page. The template does the layout.

02

No Servers to Run

You're not hosting Puppeteer. You POST to an API and get a URL back. That's the whole thing.

03

Cached After First Render

First render takes 200-500ms. Every request after that is served from edge cache. You pay to mint the URL, not to serve it.

04

Works With Any Stack

Next.js, Astro, Rails, Django, whatever. It's an HTTP POST. Put the URL in your og:image meta tag.

SECTION 07

Frequently Asked Questions

Try It

500 images per month on Starter. Your first OG image is one API call.

Get Your API Key