---
title: GET /og Reference
description: Full API reference for the signed OG image endpoint. Templates, parameters, caching, and Satori CSS constraints.
slug: og
---

# GET /og — OG Image API

Generate Open Graph images from a signed URL. Public endpoint, no API key required.

## Endpoint

```
GET https://api.renderog.app/og
```

## Request Parameters

| Parameter | Type | Default | Limits | Notes |
|---|---|---|---|---|
| `template` | string | — | `basic`, `gradient`, `blog`, `product` | Required |
| `title` | string | — | ≤200 chars | Required, the main text |
| `subtitle` | string | — | optional | Secondary text (template-dependent) |
| `description` | string | — | optional | Longer text, used by some templates |
| `theme` | string | `dark` | `light`, `dark` | Color scheme |
| `w` | int | `1200` | 100–2400 | Width in pixels |
| `h` | int | `630` | 100–1260 | Height in pixels |
| `format` | string | `png` | `png`, `webp`, `avif`, `svg` | Output format |
| `expires` | int | — | unix seconds | Optional; if set, signature must include this and must be in the future |
| `sig` | string | — | hex, 64 chars | HMAC-SHA256 signature (required) |

## Response

**Status 200 (success):**
- `Content-Type`: `image/png`, `image/webp`, `image/avif`, or `image/svg+xml`
- `Content-Length`: size in bytes
- `Cache-Control: public, s-maxage=31536000, immutable` — cached at edge for 1 year
- `X-Render-Ms`: milliseconds to render (≤100ms cached, ≤500ms cold)
- `X-Cache`: `HIT` or `MISS`

**Status 400 (invalid params):**
```json
{ "error": { "code": "invalid_params", "message": "..." } }
```
Not billed. Check `title` length, dimensions, template name.

**Status 403 (signature invalid/expired):**
```json
{ "error": { "code": "invalid_signature", "message": "..." } }
```
Not billed. Verify signature calculation and that `expires` (if set) is a future unix timestamp.

**Status 422 (render failed):**
```json
{ "error": { "code": "render_failed", "message": "..." } }
```
Not billed. Usually invalid CSS or template HTML. Test with a simple template first.

**Status 429 (rate limited):**
```json
{ "error": { "code": "rate_limited", "message": "..." } }
```
Not billed. Includes `Retry-After` header. Free: 60/min, Paid: 600/min.

## Signing

All `/og` requests must be signed with HMAC-SHA256:

1. Collect all parameters **except** `sig`
2. Sort alphabetically by key
3. URL-encode each value
4. Join with `&`: `key1=value1&key2=value2...`
5. Compute `sig = HMAC-SHA256(SIGNING_SECRET, canonical)` and encode as hex
6. Append to URL: `...&sig=<sig>`

**Example (canonical):**
```
format=png&h=630&template=basic&theme=dark&title=Hello&w=1200
```
(Note: `sig` is excluded, everything else sorted alphabetically)

If `expires` is set, it must be included in the signature:
```
expires=1700000000&format=png&h=630&template=basic&theme=dark&title=Hello&w=1200
```

## Templates

### `basic`

Clean, minimal layout. Left-aligned text with accent color.

**Example:**
```
https://api.renderog.app/og?template=basic&title=My%20Startup&subtitle=Build%20fast&theme=dark&sig=...
```

- `title`: Required, large heading
- `subtitle`: Optional, smaller secondary text
- Colors: White on dark background (default), or dark on light (if `theme=light`)

### `gradient`

Modern gradient background with centered text.

**Example:**
```
https://api.renderog.app/og?template=gradient&title=Ship%20It&description=Fast%20deployments&theme=dark&sig=...
```

- `title`: Required
- `description`: Optional, line-wrapped body text
- `theme`: Affects gradient colors and text contrast

### `blog`

Article-style layout with byline and date placeholders.

**Example:**
```
https://api.renderog.app/og?template=blog&title=Next.js%2015%20is%20Here&subtitle=What%27s%20New&theme=dark&sig=...
```

- `title`: Article headline
- `subtitle`: Byline or publication name
- `description`: Summary text (optional)

### `product`

Product-card layout with icon placeholder and feature list.

**Example:**
```
https://api.renderog.app/og?template=product&title=RenderOG&subtitle=OG%20Images%20at%20the%20Edge&theme=light&sig=...
```

- `title`: Product name
- `subtitle`: Tagline
- `description`: Bullet-point features (optional)

## Caching Behavior

Every OG image is cached at Cloudflare's edge for **1 year** (`s-maxage=31536000`). The cache key is the full URL including query parameters.

**First render (MISS):** 50–100ms depending on image complexity and font subsetting.
**Cached render (HIT):** <5ms.

Cache hits do not count toward your quota and are never billed.

## Satori & CSS Constraints

OG images are rendered with **Satori** (React JSX → SVG → PNG). Templates are written as HTML/CSS strings, not JSX.

### Supported CSS

- **Flexbox** layout (`display: flex`, `flex-direction`, `justify-content`, `align-items`, `gap`, `flex-wrap`, etc.)
- **Sizing**: `width`, `height`, `min-width`, `max-width`, `padding`, `margin`, `border-radius`
- **Colors**: `color`, `background-color`, `border` (solid only)
- **Text**: `font-size`, `font-weight` (numerical: 400–900), `letter-spacing`, `line-height`, `text-align`, `text-transform`
- **Positioning**: `absolute`, `relative` (with `top`, `left`, `right`, `bottom`)

### Not Supported

- **Grid** (`display: grid` does not work)
- **`calc()`** — use fixed dimensions
- **`var()` or custom properties** — inline values only
- **System fonts** — must pass font data explicitly (Inter subset is bundled)
- **Remote image URLs** — Satori's internal fetch fails on Workers; fetch images in the route handler and inline as base64 data URIs
- **Transforms** (`transform: rotate()`, `skew`, etc.)
- **Filters** (`filter: blur()`, etc.)
- **Animations** — static renders only

### Fonts

Built-in: **Inter** (latin + cyrillic subset, weights 400–700, `@font-face` included).

For other fonts, pass base64-encoded TTF/OTF data at template render time (API key route). Standard web fonts (Google Fonts, etc.) are accessible via `@font-face` if you inline the `url()` as a base64 data URI.

## Examples

### Minimal OG (curl)

```bash
curl "https://api.renderog.app/og?template=basic&title=Hello%20World&w=1200&h=630&sig=abc123def456..."
```

### With Expiry

Signature includes `expires` (unix seconds, ~1 hour from now):

```
https://api.renderog.app/og?expires=1723000000&format=png&h=630&template=basic&title=Hello&w=1200&sig=...
```

If current time > `expires`, the API returns `403 expired`.

### Different Format

```
https://api.renderog.app/og?template=blog&title=Article&format=webp&sig=...
```

### Light Theme

```
https://api.renderog.app/og?template=product&title=RenderOG&theme=light&w=1200&h=630&sig=...
```

## Performance Notes

- **Cached renders:** <5ms, served from Cloudflare's edge in your region
- **Cold renders:** 50–150ms depending on CSS complexity
- **Max dimensions:** 2400×1260 px (larger images take longer)
- **Wasm initialization:** Happens once per Worker instance, not per-request
- **Font subsetting:** Inter is pre-subset and bundled; custom fonts add ~10–30ms

## Debugging

**Not rendering?** Use the [Playground](https://renderog.app/playground) to preview before signing. Common issues:

- **Empty or wrong text:** Check `title` is provided and URL-encoded
- **Layout broken:** Satori only supports flexbox. No grid, no `calc()`.
- **Image not showing:** Satori can't fetch remote images. Fetch and inline as data URI instead.
- **Wrong colors:** Double-check `theme` value and ensure CSS color values are valid hex/rgb.

See [Error Codes](./errors.md) for all status codes and meanings.
