# OG Image API

> Generate a social-card image from a title, subtitle and logo.

Renders an Open Graph or Twitter card image with your text on a clean template and returns the PNG, JPEG or WebP bytes. Cards are cached, so repeated requests for the same parameters cost nothing and answer with an `ETag`.

Because the image URL usually goes in a `<meta property="og:image">` tag where no header can be sent, the endpoint also accepts a **signed URL**: add `k=<key id>` and `sig=<signature>` instead of the Authorization header. The signature is the first 40 hex characters of `HMAC-SHA256(secret, canonical query)`, where the canonical query is every parameter except `sig`, sorted by name and RFC 3986 encoded, and the secret is the signing secret shown for the key in the console.

- **Endpoint:** `GET https://api.zactonz.com/og/`
- **Category:** Media
- **Version:** 1
- **Status:** stable
- **Authentication:** `Authorization: Bearer YOUR_API_KEY`
- **Reference page:** https://developers.zactonz.com/apis/og-image/

## Parameters

Send parameters in the query string.

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `title` | string | yes |  | Main text, up to 120 characters. |
| `subtitle` | string | no |  | Secondary line, up to 200 characters. |
| `site` | string | no |  | Short label shown at the bottom, for example your domain, up to 60 characters. |
| `logo` | string | no |  | Public URL of a PNG, JPEG, WebP, GIF or SVG logo, up to 512 KB. |
| `template` | enum | no | `card` | `card` (accent bar and logo), `minimal` (centred) or `split` (text beside an accent panel). Allowed values: `card`, `minimal`, `split`. |
| `theme` | enum | no | `light` | `light` or `dark`. Allowed values: `light`, `dark`. |
| `accent` | string | no | `ff6700` | Accent colour as six hex digits. |
| `bg` | string | no |  | Background colour as six hex digits, overriding the theme. |
| `size` | enum | no | `1200x630` | Pixel size. Allowed values: `1200x630`, `1200x600`, `1080x1080`, `1600x900`. |
| `font` | enum | no | `titillium` | `titillium`, `system` or `serif`. Allowed values: `titillium`, `system`, `serif`. |
| `format` | enum | no | `png` | `png` (default), `jpeg` or `webp`. Allowed values: `png`, `jpeg`, `webp`. |
| `resp` | enum | no | `image` | `image` streams the bytes. `json` returns a hosted link valid for seven days. Allowed values: `image`, `json`. |
| `k` | integer | no |  | Key id, only for signed URLs. |
| `sig` | string | no |  | Signature, only for signed URLs. |

## Example request

```bash
curl "https://api.zactonz.com/og/?title=Ship%20faster%20with%20the%20Zactonz%20APIs&subtitle=Screenshots%2C%20QR%20codes%2C%20link%20previews%20and%20more&site=developers.zactonz.com&template=card&theme=light&accent=ff6700&size=1200x630&font=titillium&format=png&resp=image" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

## Response

OK

| Field | Type | Description |
|---|---|---|
| `status` | integer | `200` on success. |
| `data` | object | The result. |
| `data.image` | string | Hosted link to the card. |
| `data.format` | string | Image format. |
| `data.size` | string | Pixel size. |
| `data.cached` | boolean | Whether the card came from the cache. |

```json
{
    "status": 200,
    "data": {
        "image": "https://api.zactonz.com/f/?p=og%2F20260917%2F1c0a…png&e=1790300000&s=9ab1…",
        "format": "png",
        "size": "1200x630",
        "cached": false
    }
}
```

## Status codes

The outcome is reported in the JSON `status` field. General rules: https://developers.zactonz.com/apis/errors.md

| status | Meaning | Description |
|---|---|---|
| `400` | Bad request | `title` is missing or a parameter is out of range. |
| `422` | Logo unavailable | The logo URL did not return an image. |
| `503` | Renderer busy | All render slots are in use. Retry after a few seconds. |
| `504` | Timeout | Rendering did not finish within the time limit. |
| `401` | Unauthorized | The Authorization header is missing or the key is not recognised. |
| `403` | Forbidden | The key is not licensed for this product. |
| `429` | Too many requests | The per-minute rate or the plan quota is exhausted. Retry after the seconds given in `Retry-After`. |

## Notes

Titles wrap to three lines and are scaled to fit. Text is always escaped; HTML is not interpreted. A logo is fetched once, embedded in the render and never hot-linked.
