Bannerbear Python Library
A Python wrapper for the Bannerbear API — an image generation and media processing service.
Documentation
Find the full API documentation here.
Requirements
Python 3.8 or higher.
Installation
pip install bannerbear
V5 API
The V5 API is the current generation of the Bannerbear API. V5 API keys are not compatible with V2 endpoints, and vice versa.
This library currently ships the V5 client only. V2 support may be added in a future release under
bannerbear.v2.
Table of Contents
- Authentication
- Account
- Image Templates
- Images
- Animation Templates
- Animations
- Workflows
- Workflow Runs
- Tools
- Assets
- Publications
- Batches
- Webhooks
- Instant URLs
Authentication
from bannerbear.v5 import Client
bb = Client("your V5 API key")
Or set BANNERBEAR_API_KEY and instantiate without arguments:
bb = Client()
Account
bb.account()
Image Templates
Templates can be created, updated, and deleted through the API — config holds the full canvas.
bb.list_image_templates(page=1)
bb.get_image_template("template uid")
bb.create_image_template({
"name": "My Template",
"description": "Created from the API",
"tags": ["portrait"],
"width": 1080,
"height": 1080,
"config": {
"objects": [
{"id": "bg", "type": "rectangle", "left": 0, "top": 0,
"width": 1080, "height": 1080, "background-color": "#0f172a"},
{"id": "headline", "type": "text", "left": 80, "top": 400, "width": 920,
"text": "Hello World!", "font-size": 72, "color": "#ffffff"},
]
},
})
bb.update_image_template("template uid", {
"name": "New Name",
"description": "...",
"tags": ["portrait"],
})
bb.delete_image_template("template uid")
Options for create_image_template / update_image_template
namerequired for createdescriptiontagswidth/height: canvas size in pixelsconfig: full canvas configuration,{"objects": [...]}. Passing it replaces the existing config in place
Deleting is a soft delete: images already rendered from the template stay intact, but the template no longer appears in list/get calls and cannot be used for new renders.
Images
V5's modifications is a dict with two sub-keys:
template— template-level changes (width, height, etc.)objects— list of per-layer changes
bb.create_image("template uid", {
"modifications": {
"template": {"width": 1080, "height": 1080},
"objects": [
{"name": "headline", "text": "Hello World!"},
{
"name": "photo",
"image_url": "https://images.unsplash.com/photo-1555400038-63f5ba517a47?w=1000&q=80",
},
],
},
})
Synchronous generation routes to sync.api.bannerbear.com/v5 (10s timeout). synchronous is a keyword-only flag — it's a transport switch and is not sent in the request body:
bb.create_image("template uid", {"modifications": {"objects": [...]}}, synchronous=True)
Options for create_image
modifications: V5 modifications dictformats: output formats, e.g.["jpg", "pdf"]scale: scale multiplier, 1–4dpi: DPI metadataquality: quality controlproxy: proxy and resize external images before rendering (bool)metadata: include any metadata to reference at a later pointversion: pin template versionsynchronous(keyword-only): route to the sync host (SDK-only, not sent to the API)
bb.get_image("image uid")
bb.list_images(page=1)
Animation Templates
Animation templates render video instead of a still image. They carry a frame_rate and a duration_seconds in place of the image template's static canvas.
bb.list_animation_templates(page=1)
bb.get_animation_template("template uid")
bb.create_animation_template({
"name": "My Animation",
"description": "Created from the API",
"tags": ["promo"],
"width": 1080,
"height": 1080,
"frame_rate": 30,
})
bb.update_animation_template("template uid", {"name": "New Name", "frame_rate": 60})
bb.delete_animation_template("template uid")
Options for create_animation_template / update_animation_template
namerequired for createdescriptiontagswidth/height: canvas size in pixels, 100–3000frame_rate:24,30, or60
Animations
Rendering an animation is always asynchronous — there is no sync host for animations. Poll get_animation until the status is "completed" or "failed", or subscribe to a webhook with the resource "animation".
animation = bb.create_animation("animation template uid", {
"modifications": {
"template": {"width": 1080, "height": 1080, "fps": 30},
"objects": [{"name": "headline", "text": "Hello World!"}],
},
"formats": ["mp4"],
})
animation = bb.get_animation(animation["uid"])
animation["status"] # "queued" | "rendering" | "completed" | "failed"
if animation["status"] == "completed":
print(animation["files"]["mp4"])
bb.list_animations(page=1)
Options for create_animation
modifications: V5 modifications dictformats:["mp4"]or["mov"]. Ignored whentransparentis set — that always yields MOVmetadata: include any metadata to reference at a later point
Template-level modification keys for animations: width, height, fps (24, 30, or 60), and transparent. Setting transparent renders on a transparent background and forces a MOV output, so the alpha channel survives.
Workflows
Workflows chain several steps into one named, re-runnable operation. They are read-only through the API — build them in the Bannerbear UI, then run them here.
bb.list_workflows(page=1)
workflow = bb.get_workflow("workflow uid")
workflow["inputs"] # the inputs this workflow declares
workflow["steps"]
Workflow Runs
A run is asynchronous. Poll get_workflow_run until the status is "completed" or "failed", or subscribe to a webhook with the resource "workflow_run".
run = bb.create_workflow_run("workflow uid", {
"inputs": {"headline": "Hello World!", "photo": "https://example.com/photo.jpg"},
})
run = bb.get_workflow_run(run["uid"])
run["status"] # "queued" | "running" | "completed" | "failed"
if run["status"] == "completed":
print(run["outputs"])
bb.list_workflow_runs(page=1)
Options for create_workflow_run
inputs: values for the workflow's declared inputs
Tools
Tools are standalone media operations that do not use a template. Every tool is asynchronous: the call returns a pending tool job. Poll get_tool_job until the status is "completed" or "failed", or subscribe to a webhook with the resource "tool_job".
job = bb.trim_video({"video_url": "https://example.com/clip.mp4", "start": 2.5, "end": 10.0})
job = bb.get_tool_job(job["uid"])
job["status"] # "pending" | "running" | "completed" | "failed"
if job["status"] == "completed":
print(job["outputs"]["video_url"])
bb.list_tool_jobs(page=1)
Every tool also accepts an optional metadata string.
| Method | Required | Optional | Output key |
|---|---|---|---|
remove_bg |
image_url |
— | image_url |
create_pdf |
urls |
— | pdf_url |
trim_video |
video_url, start, end |
— | video_url |
concat_videos |
video_urls |
width, height, fps |
video_url |
resize_video |
video_url, width, height |
fit |
video_url |
crop_video |
video_url, x, y, width, height |
— | video_url |
overlay_video |
base_video_url, overlay_video_url |
position, margin, x, y, scale, start |
video_url |
overlay_image |
video_url, image_url |
position, margin, x, y, opacity |
video_url |
subtitle_video |
video_url |
language, font, font_size, color, bold, italic, outline_color, outline_width, shadow_size, shadow_color, background_style, background_color, alignment |
video_url |
generate_voiceover |
text, voice |
— | audio_url |
add_audio |
video_url, audio_url, mode |
volume, loop, ducking |
video_url |
add_cover_art |
video_url, image_url |
— | video_url |
create_video_slideshow |
image_urls |
slide_duration, transition, transition_duration, width, height |
video_url |
apply_color_filter |
video_url, filter |
— | video_url |
soften_video |
video_url, strength |
— | video_url |
Place the two overlay tools with either position (plus an optional margin) or x/y — not both. position accepts top_left, top_center, top_right, center, bottom_left, bottom_center, and bottom_right. resize_video accepts a fit of cover (crops), contain (letterboxes), or blur (fills the bars with a blurred copy).
A few examples:
bb.remove_bg({"image_url": "https://example.com/product.png"})
# Corner placement, 40px in from each edge
bb.overlay_image({
"video_url": "https://example.com/clip.mp4",
"image_url": "https://example.com/logo.png",
"position": "bottom_right",
"margin": 40,
"opacity": 0.8,
})
bb.subtitle_video({
"video_url": "https://example.com/talk.mp4",
"font": "montserrat",
"font_size": 32,
"color": "#ffffff",
"background_style": "outline",
"alignment": "2",
})
bb.generate_voiceover({"text": "Welcome to Bannerbear.", "voice": "rachel"})
bb.create_video_slideshow({
"image_urls": ["https://example.com/1.jpg", "https://example.com/2.jpg"],
"slide_duration": 3,
"transition": "fade",
"width": 1280,
"height": 720,
})
create_tool_job calls any tool by name — the escape hatch for tools added after this release:
bb.create_tool_job("remove_bg", {"image_url": "https://example.com/product.png"})
Assets
Upload a file (max 5MB) and get back a durable CDN URL you can feed to image modifications or tools. Uploads are deduplicated per workspace by SHA-256, so re-uploading the same bytes returns the existing record instead of creating a duplicate.
with open("logo.png", "rb") as f:
asset = bb.upload_asset(f.read(), "image/png")
asset["url"]
bb.get_asset("asset uid")
bb.list_assets(page=1)
Accepted mime types: image/jpeg, image/png, image/webp, image/gif, video/mp4, video/webm, video/quicktime, audio/mpeg, audio/wav, audio/mp4, audio/webm, audio/ogg, application/pdf.
check_assets maps each SHA-256 content hash to its existing asset (or None), so a syncing client can skip the upload round-trip for content it already pushed. Max 100 hashes per call.
import hashlib
data = open("logo.png", "rb").read()
digest = hashlib.sha256(data).hexdigest()
if bb.check_assets([digest])[digest] is None:
bb.upload_asset(data, "image/png")
Publications
Publications are templates published to the public library. Installing one clones it into your workspace as a new image template.
bb.list_publications(page=1)
bb.get_publication("publication uid")
template = bb.install_publication("publication uid")
template["uid"]
Batches
Generate multiple images in one request (up to 100).
bb.create_batch({
"type": "images",
"items": [
{"template": "template uid 1", "modifications": {"objects": [...]}},
{"template": "template uid 2", "modifications": {"objects": [...]}},
],
})
bb.get_batch("batch uid")
bb.list_batches(page=1)
Webhooks
Webhooks are managed as a first-class resource in V5.
hook = bb.create_webhook({
"name": "my-webhook",
"url": "https://example.com/hook",
"resource": "image",
"event": "completed",
"status": "active",
})
# IMPORTANT: signing_key is ONLY returned in the create response. Store it now —
# subsequent get_webhook calls will not include it.
print(hook["signing_key"])
Options for create_webhook / update_webhook
namerequiredurlrequired — the URL that receives the eventsresource:"image","batch","tool_job","animation", or"workflow_run"event:"all_events","completed", or"failed"status:"active"or"disabled"
CRUD:
bb.get_webhook("webhook uid")
bb.update_webhook("webhook uid", {
"name": "renamed",
"url": "https://example.com/hook",
"resource": "image",
"event": "completed",
"status": "active",
})
bb.delete_webhook("webhook uid")
bb.list_webhooks(page=1)
Instant URLs
Instant URLs are URLs bound to a template that can be manipulated with query strings.
Create an Instant URL base
iurl = bb.create_instant_url({
"name": "my-instant-url",
"template": "template uid",
"mode": "encoded", # or "named_params"
"security": "signed", # or "open"
"status": "active",
"scale": 1, # 1, 2, 3, or 4
})
# IMPORTANT: signing_key is ONLY returned in the create response. Store it now.
print(iurl["signing_key"])
print(iurl["base_url"])
Options for create_instant_url / update_instant_url
namerequiredtemplaterequired — image template UIDmode:"encoded"or"named_params"security:"signed"or"open"status:"active"or"disabled"scale: 1, 2, 3, or 4rate_limit: enable per-IP rate limitingtemplate_version: pin template versionmax_renders: cap total rendersexpires_at: ISO 8601 expiry
CRUD:
bb.get_instant_url("uid")
bb.update_instant_url("uid", {"name": "...", "template": "...", ...})
bb.delete_instant_url("uid")
bb.list_instant_urls(page=1)
Build an Instant URL with modifications
build_instant_url is a pure local helper — no API call. It composes the URL from a base + modifications and, if a signing key is provided, appends the HMAC signature.
# Encoded mode, signed
bb.build_instant_url(
iurl["base_url"],
mode="encoded",
signing_key=iurl["signing_key"],
modifications={
"template": {"width": 1030, "height": 890},
"objects": [{"name": "title", "text": "Hello!", "color": "#ffffff"}],
},
)
# Named params mode, signed
bb.build_instant_url(
iurl["base_url"],
mode="named_params",
signing_key=iurl["signing_key"],
modifications={
"template": {"width": 1030, "height": 890},
"objects": [{"name": "title", "text": "Hello!"}],
},
)
# Open (unsigned): omit signing_key
bb.build_instant_url(
iurl["base_url"],
mode="encoded",
modifications={"objects": [{"name": "title", "text": "Hello!"}]},
)
Options for build_instant_url
base_url(positional, required) — from thecreate_instant_urlresponsemodifications(kwarg, required) — same shape ascreate_image's modificationsmode(kwarg) —"encoded"(default) or"named_params"signing_key(kwarg) — only needed when the instant URL was created withsecurity="signed"
License
MIT. See LICENSE.
Metadata
Release files for bannerbear 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| bannerbear-1.2.0.tar.gz | 9.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bannerbear-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 20.8 kB
Security for bannerbear 1.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.