BannerGen
Social media images from templates in well under a second. Base URL: https://api.bannergen.com. Machine-readable spec: /v1/openapi.json.
Quick start
Send your key as Authorization: Bearer big_…. One successful render costs 1 credit; failed renders are free.
curl -X POST https://api.bannergen.com/v1/renders \
-H "Authorization: Bearer $BANNERGEN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: post-2026-10-28-01" \
-d '{"template":"quote-card","format":"square","params":{"quote":"Simplicity is not the absence of clutter, it is the presence of clarity."}}'const res = await fetch("https://api.bannergen.com/v1/renders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BANNERGEN_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
"template": "quote-card",
"format": "square",
"params": {
"quote": "Simplicity is not the absence of clutter, it is the presence of clarity."
}
}),
});
const image = await res.json();
if (!res.ok) throw new Error(`${image.code}: ${image.message}`);
console.log(image.url);import os, uuid, requests
res = requests.post(
"https://api.bannergen.com/v1/renders",
headers={
"Authorization": f"Bearer {os.environ['BANNERGEN_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"template": "quote-card",
"format": "square",
"params": {
"quote": "Simplicity is not the absence of clutter, it is the presence of clarity."
}
},
timeout=30,
)
image = res.json()
res.raise_for_status()
print(image["url"])Response 201:
{
"id": "8f0c2a4e-3b51-4d0e-9a57-0d6a3f1b2c9e",
"url": "https://api.bannergen.com/i/8f0c2a4e-3b51-4d0e-9a57-0d6a3f1b2c9e.png",
"template": "quote-card",
"format": "square",
"width": 1080,
"height": 1080,
"output_format": "png",
"bytes": 184211,
"created_at": "2026-10-28T12:00:00.000Z"
}
The url is public and unguessable: hot-link it, post it, or download it.
Endpoints
| Method | Path | What it does |
|---|---|---|
| GET | /v1/templates | All templates with their formats. No key needed. |
| GET | /v1/templates/{id} | One template: JSON Schema of its params and an example. No key needed. |
| POST | /v1/renders | Render one image synchronously. Body: template, format (default square), params, output_format (png). |
| POST | /v1/batches | Start a batch of up to 1000 images. Answers 202 right away; see Batches. |
| GET | /v1/batches/{id} | Batch status and every image: url or error. |
| GET | /v1/account | Plan, credits left and when they reset. |
Batches
One template, many rows: rows is a list of params objects, defaults holds params shared by every row (a row's own value wins; a blank value — null, "" or missing — takes the default). Send format for one format or formats to render every row in each. Up to 1000 images per batch (rows × formats); 300 images take well under 3 minutes.
curl -X POST https://api.bannergen.com/v1/batches \
-H "Authorization: Bearer $BANNERGEN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: weekly-quotes-2026-44" \
-d '{"template":"quote-card","formats":["square","story"],"defaults":{"theme":"dark","handle":"@acme"},"rows":[{"quote":"Ship it.","author":"Ada"},{"quote":"Make it simple."}]}'
# Poll until "status" is "completed"
curl https://api.bannergen.com/v1/batches/$BATCH_ID -H "Authorization: Bearer $BANNERGEN_API_KEY"
Every row is checked up front. A row with bad params does not fail the batch: its images are marked failed right away with the reason, and cost nothing. The valid images reserve a credit each when the batch is created (402 if there are not enough); an image that fails while rendering gets its credit back. So a batch costs exactly the number of images that succeeded.
status goes queued → processing → completed. GET /v1/batches/{id} lists items in request order, each with row_index, format and either url or error:
{
"id": "3c9a7e10-5f2b-4c61-8d2e-91b0f4a6c7d3",
"status": "completed",
"template": "quote-card",
"formats": [
"square",
"story"
],
"total": 4,
"succeeded": 3,
"failed": 1,
"pending": 0,
"created_at": "2026-10-28T12:00:00.000Z",
"completed_at": "2026-10-28T12:00:04.000Z",
"items": [
{
"row_index": 0,
"format": "square",
"status": "succeeded",
"id": "5d1e…",
"url": "https://api.bannergen.com/i/5d1e….png",
"width": 1080,
"height": 1080,
"bytes": 171204
},
{
"row_index": 0,
"format": "story",
"status": "succeeded",
"id": "a07c…",
"url": "https://api.bannergen.com/i/a07c….png",
"width": 1080,
"height": 1920,
"bytes": 236118
},
{
"row_index": 1,
"format": "square",
"status": "succeeded",
"id": "e4b2…",
"url": "https://api.bannergen.com/i/e4b2….png",
"width": 1080,
"height": 1080,
"bytes": 158377
},
{
"row_index": 1,
"format": "story",
"status": "failed",
"error": {
"code": "image_load_failed",
"message": "Could not load image for \"logo_url\": HTTP 404",
"param": "logo_url"
}
}
]
}
| error.code | Meaning |
|---|---|
validation_failed | The row's params are invalid; errors lists each one. |
image_load_failed | An image URL of the row could not be fetched or decoded; param names it. |
internal_error | Something broke on our side. |
timeout | The image did not finish in time. |
For a row with bad params, error.errors lists each one; param is the param (column) name.
Formats
| id | Size |
|---|---|
square | 1080×1080 |
portrait | 1080×1350 |
story | 1080×1920 |
landscape | 1200×628 |
pin | 1000×1500 |
Output is PNG for now; JPEG and WebP are planned. Image URLs can be PNG, JPEG, GIF or SVG over https.
Retries and idempotency
Add an Idempotency-Key header (any unique string, up to 255 characters) to POST /v1/renders or POST /v1/batches to make retries safe. Repeating a request with the same key returns the first result with 200 and the header Idempotent-Replayed: true, and charges only once. The same key with a different body is rejected with idempotency_key_reused. If a render fails, the key is released and you can retry with it.
Rate limits
Up to 60 single renders and 10 batches per minute per key. Over the limit you get 429 with Retry-After in seconds.
Errors
Errors are JSON with a machine-readable code and a human message. Validation errors list every bad parameter:
{
"code": "validation_failed",
"message": "\"quote\" is required",
"errors": [
{
"param": "params.quote",
"code": "required",
"message": "\"quote\" is required"
}
]
}
| Status | code | Meaning |
|---|---|---|
401 | missing_api_key | No Authorization: Bearer <key> header. |
401 | invalid_api_key | The key is unknown or revoked. |
402 | insufficient_credits | Not enough credits for the render, or for the valid rows of a batch. |
404 | not_found | Unknown path, template or batch. |
409 | idempotency_in_progress | A request with this Idempotency-Key is still running. Retry shortly. |
400 | invalid_json | The body is not a JSON object. |
422 | validation_failed | Bad parameters; errors lists each one with param, code and message. |
422 | image_load_failed | An image URL could not be fetched or decoded; param names it. |
422 | idempotency_key_reused | This Idempotency-Key was already used with a different request body. |
429 | rate_limited | Too many renders or batches for this key. Wait Retry-After seconds. |
500 | internal_error | Something broke on our side. The credit is returned. |
Templates
The image shows only what you send. Leave a param out (or send null or "") and its element is not drawn: no placeholder text, no empty button. Only styling params (theme, font, toggles like trend or numbered) have defaults.
Every template also takes the brand params: background, primary_color, accent_color, font, logo_url, handle, theme.
quote-card — Quote card
A big quote with attribution. Good for thought leadership and customer voice.
| Param | Type | Description |
|---|---|---|
quote required | text ≤ 280 | The quote, without quotation marks. |
author | text ≤ 60 | Who said it. |
author_role | text ≤ 60 | Role or company. |
author_photo_url | image_url | Author photo, shown as a circle. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: playfair. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"quote": "Simplicity is not the absence of clutter, it is the presence of clarity.",
"author": "Maya Chen",
"author_role": "Head of Design, Northwind",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}blog-post — Blog post announcement
"New on the blog" card with title, excerpt and a call to action.
| Param | Type | Description |
|---|---|---|
label | text ≤ 30 | Small label above the title. |
title required | text ≤ 120 | Post title. |
excerpt | text ≤ 180 | One or two sentences under the title. |
cta | text ≤ 24 | Button text. |
image_url | image_url | Cover image shown next to or above the text. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: inter. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"label": "New on the blog",
"title": "How we cut our onboarding time in half with three tiny changes",
"excerpt": "What we measured, what we changed and what surprised us along the way.",
"cta": "Read the post",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}product-promo — Product promo
Product shot with name, price, old price and a discount badge.
| Param | Type | Description |
|---|---|---|
product_name required | text ≤ 60 | Product name. |
tagline | text ≤ 80 | Short benefit line. |
price | text ≤ 20 | Price as shown, with currency. |
old_price | text ≤ 20 | Crossed-out previous price. |
badge | text ≤ 14 | Discount badge text. |
cta | text ≤ 24 | Button text. |
product_image_url | image_url | Product photo, ideally on a plain or transparent background. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: poppins. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"product_name": "Aero Running Shoe",
"tagline": "Featherlight. Built for long runs.",
"price": "$89",
"old_price": "$129",
"badge": "-30%",
"cta": "Shop now",
"product_image_url": "https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=1200&q=80&fm=jpg",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}testimonial — Testimonial
Customer review with star rating, name and photo.
| Param | Type | Description |
|---|---|---|
review required | text ≤ 300 | What the customer said. |
name | text ≤ 60 | Customer name. |
role | text ≤ 60 | Role and company. |
photo_url | image_url | Customer photo, shown as a circle. |
rating | 1 | 2 | 3 | 4 | 5 | Star rating out of 5. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: inter. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"review": "We replaced three tools with this one. Setup took an afternoon and the team actually likes using it.",
"name": "Jordan Ellis",
"role": "Ops Lead at Brightside",
"rating": "5",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}carousel-slide — Carousel slide
One slide of a series: number, heading and body in a consistent style.
| Param | Type | Description |
|---|---|---|
slide_number | text ≤ 3 | Number of this slide. |
total_slides | text ≤ 3 | Total slides, for the progress bar. |
title required | text ≤ 90 | Slide heading. |
body | text ≤ 280 | Slide text. |
footer_hint | text ≤ 24 | Hint in the corner. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: space-grotesk. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"slide_number": "2",
"total_slides": "7",
"title": "Write the hook before the content",
"body": "People decide in the first second whether to keep swiping. Lead with the outcome, not the setup.",
"footer_hint": "Swipe →",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}event — Event / webinar
Event announcement with date, time and speaker.
| Param | Type | Description |
|---|---|---|
event_type | text ≤ 30 | Small label. |
title required | text ≤ 90 | Event title. |
date | text ≤ 40 | Date as you want it shown. |
time | text ≤ 40 | Time with timezone. |
speaker_name | text ≤ 60 | Speaker name. |
speaker_role | text ≤ 60 | Speaker role. |
speaker_photo_url | image_url | Speaker photo, shown as a circle. |
cta | text ≤ 24 | Button text. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: outfit. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"event_type": "Live webinar",
"title": "Scaling content without scaling the team",
"date": "Thu, Nov 14",
"time": "5:00 PM CET",
"speaker_name": "Priya Raman",
"speaker_role": "VP Marketing, Northwind",
"cta": "Register free",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}stat — Big number
One striking number with a short explanation.
| Param | Type | Description |
|---|---|---|
number required | text ≤ 12 | The number, with units. |
label | text ≤ 120 | What the number means. |
source | text ≤ 80 | Source line. |
trend | none | up | down | Optional trend arrow next to the number. Default: none. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: bricolage. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"number": "73%",
"label": "of buyers check a brand's social feed before their first purchase",
"source": "Source: Northwind Consumer Survey 2026",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}tips-list — Tips / list
Heading with 1–5 numbered or bulleted points.
| Param | Type | Description |
|---|---|---|
title required | text ≤ 80 | List heading. |
item_1 required | text ≤ 90 | First point. |
item_2 | text ≤ 90 | Second point. |
item_3 | text ≤ 90 | Third point. |
item_4 | text ≤ 90 | Fourth point. |
item_5 | text ≤ 90 | Fifth point. |
numbered | boolean | Numbers instead of bullets. Default: true. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: dm-sans. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"title": "5 ways to reuse one blog post",
"item_1": "Turn each subheading into a carousel slide",
"item_2": "Pull the best quote into a quote card",
"item_3": "Post the key stat as a big-number image",
"item_4": "Record a 30-second summary for Reels",
"item_5": "Send it to your newsletter with a new intro",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}hiring — We're hiring
Job announcement with role, location, type and salary.
| Param | Type | Description |
|---|---|---|
headline | text ≤ 30 | Top line. |
role required | text ≤ 60 | Job title. |
location | text ≤ 40 | Location or Remote. |
employment_type | text ≤ 30 | Full-time, contract… |
salary | text ≤ 40 | Salary range. |
cta | text ≤ 24 | Button text. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: montserrat. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"headline": "We're hiring",
"role": "Senior Product Designer",
"location": "Remote · EU",
"employment_type": "Full-time",
"salary": "€70–90k",
"cta": "Apply now",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}article-cover — Article cover / OG image
Cover for articles and link previews: title, category, author and date.
| Param | Type | Description |
|---|---|---|
category | text ≤ 30 | Category tag. |
title required | text ≤ 120 | Article title. |
author | text ≤ 60 | Author name. |
date | text ≤ 40 | Publication date or reading time. |
image_url | image_url | Optional photo; the card uses it as the background. |
background | background | Hex color, linear-gradient(<angle>deg, #hex, #hex) or an https image URL. Defaults depend on theme. |
primary_color | color | Main brand color (headings, key shapes). |
accent_color | color | Highlight color (badges, buttons, decorations). |
font | enum | Font family from the curated list. Default: fraunces. |
logo_url | image_url | Logo (PNG, JPEG, GIF or SVG over https). |
handle | text ≤ 40 | Signature such as @brand or brand.com. |
theme | light | dark | Quick light/dark switch for the default palette. Default: light. |
Example params:
{
"category": "Growth",
"title": "The quiet power of boring marketing",
"author": "Sam Okafor",
"date": "Oct 28, 2026 · 6 min read",
"logo_url": "https://avatars.githubusercontent.com/u/9919?s=256",
"handle": "@northwind"
}