Skip to main content

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