How to Generate Ad and Social Media Banners with an API
Generate branded ad and social banners from templates with JSON, async jobs, validation, polling, webhooks, and production safeguards.
Use a template-rendering API to generate ad and social media image variations automatically. Define reusable text, image, color, and layout layers; send a JSON request with a template ID and substitutions; then poll the queued job or receive a webhook before downloading the completed PNG or JPG. Validate dimensions, copy length, image URLs, safe areas, and brand rules before submitting each render.
What an API banner workflow does
A production workflow separates design from data. Designers create a layout family once, while your application supplies campaign-specific values for each placement.
- List placements and exact pixel dimensions, such as feed square, story vertical, and landscape display.
- Create one template for each layout family with named text, image, color, and background layers.
- Store brand colors, fonts, legal copy, destination URLs, and template versions as structured data.
- Validate every substitution before sending it to the renderer.
- Submit one render job per variant or use a documented collection or batch feature.
- Handle
pending,completed, andfailedstates with polling or webhooks. - Download the output, deliver it to your ad or content pipeline, and retain template and input metadata for auditability.
Choose the rendering API
| Service | Best fit | Documented capabilities | Checks before production |
|---|---|---|---|
| Bannerbear V5 | Server-side image and video automation | Editable template layers, text and image modifications, queued jobs, polling, webhooks, PNG/JPG output, PDF when requested, workflows, instant URLs, asset uploads, SDKs, and an AI-image generation tool. | Use the V5 reference and verify current quotas, limits, model availability, and licensing. |
| Creatomate | Campaigns that need static and animated or video variants | API workflows, pre-made templates, templates built from scratch, image banners, and video banners. | Confirm dimensions, rendering latency, API limits, and advertising rights in your account. |
| Canva REST API | Design and asset integration or an editor handoff | Create and sync assets and designs, collaboration, and export to another platform. | Preview APIs may have unannounced breaking changes and are not recommended for production public apps. The cited documentation does not establish a dedicated bulk banner-render endpoint. |
| Adobe Express Embed SDK | Embedded creation experiences | Embedded Express creation with templates and assets for social content plus AI-powered image-generation features. | Verify that the current embedded capabilities meet your server-side rendering requirements. |
For any provider, compare template-layer control, output formats, aspect-ratio presets, asynchronous delivery, webhooks, SDK languages, asset hosting, AI-image options, rate limits, quotas, API stability, and paid-ad licensing.
Design a template that scales
Name every editable layer
Use stable names such as headline, subheadline, hero_image, price, badge, background_color, and cta. Avoid positional indexes: inserting a layer should not silently change which element your code edits.
Keep layout families separate
A square feed image, vertical story, and landscape display placement usually need different line breaks and safe areas. Create one template per layout family instead of forcing one canvas to handle every ratio.
Make brand constraints data
Store approved colors, font choices, maximum character counts, legal disclaimers, logo rules, and destination URLs in configuration. Version this configuration with the template ID so a later redesign can be reproduced.
Bannerbear V5: complete request pattern
Bannerbear documents bearer-token authentication and a queued image-generation flow. A request applies modifications to a template, then your worker polls the image UID or receives a webhook.
curl -X POST "https://api.bannerbear.com/v5/images" \
-H "Authorization: Bearer API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "TEMPLATE_UID",
"modifications": [
{"name": "headline", "text": "Summer sale: 25% off"},
{"name": "subheadline", "text": "Ends Sunday"},
{"name": "hero_image", "image_url": "https://cdn.example.com/product.jpg"},
{"name": "background_color", "color": "#102A43"}
],
"webhook_url": "https://example.com/banner-webhook"
}'
The response is queued. Persist its UID and treat the render as incomplete until the status is completed.
Poll for completion with cURL
curl "https://api.bannerbear.com/v5/images/IMAGE_UID" \
-H "Authorization: Bearer API_KEY"
When the status is completed, read the returned PNG or JPG URL and download it. If the status is failed, record the provider error and input payload for diagnosis.
Python worker with validation and backoff
import os
import time
import requests
API_KEY = os.environ["BANNERBEAR_API_KEY"]
BASE = "https://api.bannerbear.com/v5"
variant = {
"headline": "Summer sale: 25% off",
"subheadline": "Ends Sunday",
"hero_image": "https://cdn.example.com/product.jpg",
"background_color": "#102A43",
}
if len(variant["headline"]) > 42:
raise ValueError("headline exceeds the template limit")
if not variant["hero_image"].startswith(("https://", "http://")):
raise ValueError("hero_image must be an absolute URL")
payload = {
"template": "TEMPLATE_UID",
"modifications": [
{"name": "headline", "text": variant["headline"]},
{"name": "subheadline", "text": variant["subheadline"]},
{"name": "hero_image", "image_url": variant["hero_image"]},
{"name": "background_color", "color": variant["background_color"]},
],
}
headers = {"Authorization": f"Bearer {API_KEY}"}
r = requests.post(f"{BASE}/images", json=payload, headers=headers, timeout=30)
r.raise_for_status()
uid = r.json()["uid"]
for attempt in range(8):
time.sleep(min(2 ** attempt, 30))
status = requests.get(f"{BASE}/images/{uid}", headers=headers, timeout=30)
status.raise_for_status()
data = status.json()
if data.get("status") == "completed":
image_url = data["image_url"]
image = requests.get(image_url, timeout=60)
image.raise_for_status()
open("banner.jpg", "wb").write(image.content)
break
if data.get("status") == "failed":
raise RuntimeError(data)
else:
raise TimeoutError("render did not complete within the polling window")
Node.js submission
const apiKey = process.env.BANNERBEAR_API_KEY;
const payload = {
template: 'TEMPLATE_UID',
modifications: [
{ name: 'headline', text: 'Summer sale: 25% off' },
{ name: 'subheadline', text: 'Ends Sunday' },
{ name: 'hero_image', image_url: 'https://cdn.example.com/product.jpg' },
{ name: 'background_color', color: '#102A43' }
]
};
const created = await fetch('https://api.bannerbear.com/v5/images', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!created.ok) throw new Error(`create failed: ${created.status}`);
const { uid } = await created.json();
for (let attempt = 0; attempt < 8; attempt++) {
await new Promise(r => setTimeout(r, Math.min(2 ** attempt * 1000, 30000)));
const check = await fetch(`https://api.bannerbear.com/v5/images/${uid}`, {
headers: { Authorization: `Bearer ${apiKey}` }
});
const data = await check.json();
if (data.status === 'completed') {
const file = await fetch(data.image_url);
const bytes = Buffer.from(await file.arrayBuffer());
require('fs').writeFileSync('banner.jpg', bytes);
break;
}
if (data.status === 'failed') throw new Error(JSON.stringify(data));
}
Generate many variants safely
Represent a campaign as a list of validated records. Include a deterministic variant key made from campaign ID, template version, placement, and input hash. Use that key as your idempotency record so a timeout does not create duplicate renders.
[
{
"variant_key": "spring-2026-v3-story-en",
"template": "story-template-v3",
"placement": "story",
"headline": "Spring collection",
"image_url": "https://cdn.example.com/spring.jpg",
"locale": "en"
}
]
Submit one job per variant unless the provider documents a collection or batch request. Limit concurrency to the provider’s documented rate limit, persist every provider UID, and retry only transient transport or server errors.
Validation checklist before submission
- Dimensions match the ad placement and the template’s expected canvas.
- Text length fits the named layer at the target font and language.
- URLs are absolute HTTPS URLs, publicly fetchable by the renderer, and return the intended image type.
- Images meet file-size, aspect-ratio, and licensing requirements.
- Logo clear space, contrast, legal copy, and safe areas pass brand rules.
- Destination URLs use the correct campaign parameters.
- Template ID and version are approved for the campaign.
- AI-generated assets have human brand and legal review before publication.
AI-assisted imagery
Keep image generation separate from layout rendering. Generate an image with a dedicated endpoint, store the resulting asset, validate it, and pass its URL to the banner template. Bannerbear V5 documents POST /v5/tools/generate_ai_image with a prompt, model, aspect ratio, and optional reference image. Treat the AI result as an input that still needs brand, legal, and quality review.
Webhooks, polling, and reliability
Webhooks
Use a webhook for normal production delivery. Verify the request according to the provider’s documented method, acknowledge quickly, and process the event asynchronously. Make the handler idempotent because delivery can be retried.
Polling
Polling is useful for scripts and local jobs. Use exponential backoff with a maximum interval, a total deadline, and a final status check. Do not poll every second indefinitely.
Retries
Retry connection resets, rate-limit responses after the provider’s retry delay, and temporary 5xx responses. Do not blindly retry invalid templates, inaccessible assets, authentication failures, or failed validation. Record the request ID, template version, variant key, status, and provider error.
Performance and cost controls
- Reuse templates and keep source assets close to the renderer when the provider supports reliable asset hosting.
- Generate only the placements required by the campaign.
- Use caching keyed by template version plus normalized substitutions.
- Queue work and cap concurrency instead of sending an unbounded burst.
- Measure queue wait, render duration, download duration, failure rate, and retry count.
- Keep completed files in durable storage if provider URLs are temporary.
- Check each provider’s current quotas, rate limits, output pricing, storage rules, and advertising license terms before launch.
Or skip the browser setup
If your banner workflow needs screenshots of the finished campaign page, preview links, or landing pages, ScreenshotNeo provides a one-call website screenshot API. It accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.
See the ScreenshotNeo API documentation for the full option set.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Use its full-page capture, CSS selector capture, custom CSS and JavaScript, waits, headers, cookies, device presets, PDF output, caching, signed links, async jobs, webhooks, bulk capture, and usage API when your review pipeline needs them. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account to start.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 401 or 403 response | Missing, malformed, or revoked API key | Send the documented bearer header, load the key from a secret store, and rotate it if revoked. |
| Template or layer not found | Wrong UID, deleted template, or renamed layer | Use the production template version and compare every modification name with the template definition. |
| Render fails while fetching an image | Private URL, redirect chain, unsupported type, or timeout | Use a public HTTPS asset URL, test it without browser cookies, and validate status, content type, and size before submission. |
| Text is clipped | Copy exceeds the layer or a translation is longer | Validate per locale, shorten copy, enlarge the layer, or create a layout-specific template. |
| Job stays pending | Queue delay or polling logic ended too early | Use a bounded exponential backoff, webhook fallback, and a clear operational deadline. |
| Duplicate images | Client retried after an unknown response | Use a deterministic variant key and provider-supported idempotency or a database uniqueness constraint. |
| Webhook processed twice | Provider retry or network acknowledgement failure | Store the event or render UID before side effects and return success quickly. |
FAQ
Can one template generate every social placement?
It can, but separate layout families usually produce better line breaks, safe areas, and image crops. Keep shared brand data while allowing placement-specific templates.
Should rendering be synchronous?
Assume asynchronous rendering for production. A queued job with polling or a webhook lets you handle spikes and retries without holding an HTTP request open.
Can I use AI to create the whole banner?
Use AI for optional imagery, then render text and brand elements through a deterministic template. This keeps copy, legal text, and layout reviewable.
How do I choose between image and video automation?
Choose an image-focused renderer for static placements. Choose a service that documents video templates when the campaign also needs animation, and verify output limits and licensing.
Is Canva a bulk banner-render API?
The reviewed Canva REST documentation covers assets, designs, collaboration, and export. It does not document a dedicated bulk banner-render endpoint, so confirm the current API before designing around that assumption.


