Image generation API
Design a template once in the editor, then generate 1200x630 images from it over HTTP by passing only the text fields. Signed image URLs for og:image automation, plus a synchronous REST endpoint. Renders in tens of milliseconds.
Free tier: 100 watermarked images/month to build with. API Pro: $9.00/month for 500 clean images.
1
Design a template
Build a card in the editor and save it
to your account. Its slug (for example blog-card) is the API identifier.
2
Call the API with fields
Pass the title, description, or other text fields. Colors, fonts, layout, and images stay locked to the template, so every render is on-brand.
3
Get the PNG back
The response is the image itself, rendered synchronously. No queues, no polling, no webhooks to wire up.
Authentication
Create a key under Account settings > API.
Keys look like bi_<key_id>_<secret>: the key_id
is public and appears in signed URLs; the secret authenticates REST calls and signs
URLs. A key only renders images against your templates and quota.
Authorization: Bearer bi_a1b2c3d4e5_XxYyZz...
Signed image URLs (og:image automation)
The URL itself returns the PNG, so it can go straight into a meta tag. Your page template builds one URL per post; every crawler and share preview gets a branded card with zero build steps.
GET https://betterimage.io/api/v1/i/<key_id>/<template_slug>.png
?title=Your+headline&description=One+support+line&s=<signature>
The signature is an HMAC-SHA256 (lowercase hex) computed with your key's secret over
the canonical string: <key_id>/<template_slug>?
followed by
the query parameters sorted alphabetically by name, joined as name=value
pairs with &, using raw (un-encoded)
values and excluding s
itself. Node.js example:
const crypto = require("crypto");
const keyId = "a1b2c3d4e5"; // public part of your API key
const secret = "XxYyZz..."; // secret part - keep server-side
const slug = "blog-card";
const fields = { title: "Your headline", description: "One support line" };
const canonical =
keyId + "/" + slug + "?" +
Object.keys(fields).sort().map((k) => k + "=" + fields[k]).join("&");
const s = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
const url =
"https://betterimage.io/api/v1/i/" + keyId + "/" + slug + ".png?" +
new URLSearchParams({ ...fields, s }).toString();
// <meta property="og:image" content={url} />
Responses are served with Cache-Control: public, max-age=86400, so
identical URLs are cached at the CDN edge and repeated crawler fetches don't count
against your quota.
REST API
For scripts, CI pipelines, and backends. The render is synchronous: the response body is the PNG.
curl -X POST https://betterimage.io/api/v1/images \
-H "Authorization: Bearer bi_a1b2c3d4e5_XxYyZz..." \
-H "Content-Type: application/json" \
-d '{"template": "blog-card", "fields": {"title": "Your headline"}}' \
--output card.png
Discovery and accounting endpoints:
GET /api/v1/templates -> your saved templates: slug, name, kind, fields
GET /api/v1/usage -> plan, period, used, limit, remaining, resets_on
Render responses carry x-bi-watermarked (whether the image has the
watermark) and x-bi-quota-remaining headers.
Fields you can override
Text fields only - the design stays locked to the template. Values beyond a field's length limit are truncated, and long titles auto-shrink to fit the card.
| Template kind | Fields |
|---|---|
| Basic |
title, description, tag, author,
footer
|
| Profile |
title (name), description (bio), footer
|
| Testimonial |
title
(role), description
(the quote), tag
(company), author
(person's name), footer, rating
(1-5)
|
| Minimal | title, description (tagline), footer |
Pricing and limits
- Free: 100 images/month with the "Made with betterimage.io" mark - enough to integrate and evaluate. No card required.
- API Pro ($9.00/month): 500 clean images/month, shared between API renders and clean downloads in the editor. Cancel anytime from the billing portal; the quota resets each calendar month.
- Rate limit: 60 requests/minute per key. Over-quota renders return 429.
- The one-time $4.99 clean export in the editor is unchanged.
Errors
| Status | Meaning |
|---|---|
400 |
Malformed request (missing template, bad fields object). |
401 |
Missing, invalid, or revoked API key. |
403 |
Signed URL signature doesn't match. |
404 |
Unknown key id or no template with that slug. |
429 |
Rate limit or monthly quota exceeded. |
Error bodies are JSON: {"error": "human-readable message"}.
Automate your og:images today
Design the template once, wire the URL into your layout, and every page you publish gets a branded card.
Get an API key