Skip to content

Generating Images

The /render endpoint is the heart of OG Engine. Send a JSON payload describing what you want, receive a binary image — PNG by default, WebP on paid plans. This guide walks through what happens under the hood, the minimal and full request shapes, and how to interpret the response headers.

When your request hits the API, OG Engine follows a four-step pipeline:

  1. Text measurement — Pretext’s prepareWithSegments() analyzes your title and description text, handling Unicode grapheme clusters, CJK characters, Arabic, emoji, and bidirectional text with full accuracy.
  2. LayoutlayoutWithLines() breaks each text block into lines given the available width, font metrics, and line height. Overflow is detected and an ellipsis is appended if the text exceeds maxLines.
  3. Canvas draw — The selected template composites the background gradient (or image), decorative elements, and each line of text onto an @napi-rs/canvas surface.
  4. PNG encode — The canvas is serialized to a binary buffer and streamed back as the HTTP response body.

Total time from request to response: typically ~22ms for a standard OG image (benchmarked). PNG encoding dominates; text layout + canvas draw take <1ms.

The only required fields are format and title:

Terminal window
curl -X POST https://og-engine.com/render \
-H "Authorization: Bearer oge_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"format": "og", "title": "Hello, World"}' \
--output image.png

This uses all defaults: the default template, Outfit font at 48px, the void gradient, and left-aligned layout.

A fully specified request gives you control over every visual parameter:

{
"format": "og",
"template": "default",
"title": "Server-Side Text Layout Without a Browser",
"description": "Pure JS text measurement replaces Puppeteer. ~22ms renders, zero browser dependencies.",
"author": "Pretext Engine",
"tag": "Open Source",
"style": {
"accent": "#38ef7d",
"font": "Outfit",
"titleSize": 48,
"descSize": 22,
"layout": "left",
"gradient": "void",
"overlayOpacity": 0.65
},
"output": {
"format": "png",
"quality": 90
}
}

Any field you omit falls back to its default. You never need to send the full object.

Every successful /render response includes diagnostic headers alongside the image bytes:

HeaderExampleMeaning
Content-Typeimage/pngOutput format
X-Render-Time-Ms2.34Total server-side render time in milliseconds
X-Title-Lines2Number of lines the title was broken into
X-Desc-Lines3Number of lines the description was broken into
X-Layout-OverflowfalseWhether any text was truncated with ellipsis
X-RateLimit-Limit500Your plan’s monthly render limit
X-RateLimit-Remaining483Renders remaining this month
X-RateLimit-Reset1735689600Unix timestamp when quota resets

You can use X-Layout-Overflow: true as a signal to call /validate with a smaller font size and retry — or simply display a warning in your UI.

Terminal window
# Inspect headers without saving the image body
curl -I -X POST https://og-engine.com/render \
-H "Authorization: Bearer oge_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"format": "og", "title": "Checking overflow"}'

PNG is the default. It is lossless, supports transparency, and is universally accepted by social platforms. Most OG images are 1200×630 at approximately 80–200KB depending on complexity.

{ "output": { "format": "png" } }

WebP delivers 25–35% smaller files than PNG at equivalent visual quality. Ideal for high-volume scenarios or performance-sensitive applications.

{ "output": { "format": "webp", "quality": 85 } }

The quality field (1–100, default 90) controls the WebP lossy compression level. Values between 80–90 are recommended for social cards. Attempting to request WebP on the Free plan returns a plan_required error.

import { OGEngine } from '@atypical-consulting/og-engine-sdk'
const og = new OGEngine(process.env.OG_ENGINE_KEY!)
const imageBuffer = await og.render({
format: 'og',
title: 'Server-Side Text Layout Without a Browser',
description: 'Pure JS text measurement replaces Puppeteer.',
tag: 'Open Source',
style: { accent: '#38ef7d', font: 'Outfit' },
})
// Node.js / Bun
await Bun.write('output.png', imageBuffer)

Edit the title and description below to see how rendering works in real time:

0.0ms|8500x