Generating Dynamic Images at Scale¶
This tutorial shows how to generate Open Graph images, social cards, watermarked product photos, and branded placeholders — all dynamically via the Toolkit Image API with no headless browser required.
What you'll build¶
An image generation pipeline that: 1. Creates OG/social card images from HTML templates 2. Adds watermarks to product images 3. Generates placeholder images for prototypes 4. Converts all output to WebP for performance
Prerequisites¶
pip install httpx
Step 1: Generate social cards from templates¶
Create social card images using HTML+Liquid templates — ideal for blog posts, product pages, and landing pages:
import httpx
import base64
API_KEY = "YOUR_KEY"
BASE_URL = "https://image.toolkitapi.io/v1"
def generate_og_image(title: str, subtitle: str, output_path: str):
"""Generate an Open Graph social card image."""
template = f"""
<html>
<body style="background: linear-gradient(135deg, #0f172a, #1e293b);
color: #f8fafc; font-family: system-ui, sans-serif;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
width: 1200px; height: 630px; margin: 0; padding: 80px;">
<div style="font-size: 14px; color: #3b82f6; margin-bottom: 24px;
letter-spacing: 2px; text-transform: uppercase;">
Toolkit API
</div>
<h1 style="font-size: 56px; font-weight: 800; line-height: 1.2;
text-align: center; max-width: 900px; margin: 0 0 24px 0;">
{{{{ title }}}}
</h1>
<p style="font-size: 24px; color: #94a3b8; text-align: center;
max-width: 700px; margin: 0;">
{{{{ subtitle }}}}
</p>
</body>
</html>
"""
r = httpx.post(
f"{BASE_URL}/image/from-template",
headers={"X-API-Key": API_KEY},
json={
"template": template,
"variables": {"title": title, "subtitle": subtitle},
"width": 1200,
"height": 630,
"dark_mode": True,
"format": "png",
},
timeout=30.0,
)
r.raise_for_status()
data = r.json()
with open(output_path, "wb") as f:
f.write(base64.b64decode(data["image"]))
print(f"Social card saved to {output_path}")
# Generate social cards for multiple blog posts
posts = [
("Building a Domain Monitor", "Track DNS, WHOIS, and SSL changes with Toolkit API"),
("Bulk Email Validation at Scale", "Filter disposable addresses and check deliverability"),
]
for title, subtitle in posts:
slug = title.lower().replace(" ", "-")
generate_og_image(title, subtitle, f"og-{slug}.png")
const API_KEY = "YOUR_KEY";
const BASE_URL = "https://image.toolkitapi.io/v1";
async function generateOgImage(title, subtitle) {
const template = `
<html>
<body style="background: linear-gradient(135deg, #0f172a, #1e293b);
color: #f8fafc; font-family: system-ui, sans-serif;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
width: 1200px; height: 630px; margin: 0; padding: 80px;">
<div style="font-size: 14px; color: #3b82f6; margin-bottom: 24px;
letter-spacing: 2px; text-transform: uppercase;">
Toolkit API
</div>
<h1 style="font-size: 56px; font-weight: 800; text-align: center;
max-width: 900px; margin: 0 0 24px 0;">
{{ title }}
</h1>
<p style="font-size: 24px; color: #94a3b8; text-align: center;
max-width: 700px; margin: 0;">
{{ subtitle }}
</p>
</body>
</html>
`;
const r = await fetch(`${BASE_URL}/image/from-template`, {
method: "POST",
headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
template,
variables: { title, subtitle },
width: 1200,
height: 630,
dark_mode: true,
format: "png",
}),
});
const data = await r.json();
return Buffer.from(data.image, "base64");
}
Step 2: Watermark product images¶
Add a logo or text watermark to protect product images:
def watermark_product(image_url: str, output_path: str):
"""Add a branded watermark to a product image."""
r = httpx.post(
f"{BASE_URL}/image/watermark/image",
headers={"X-API-Key": API_KEY},
json={
"url": image_url,
"watermark_url": "https://toolkitapi.io/logo-white.png",
"position": "bottom-right",
"opacity": 0.4,
"scale": 0.12, # 12% of the base image width
"format": "webp",
},
)
r.raise_for_status()
data = r.json()
with open(output_path, "wb") as f:
f.write(base64.b64decode(data["image"]))
# Watermark a product catalog
watermark_product("https://cdn.example.com/product-1.jpg", "product-1-watermarked.webp")
watermark_product("https://cdn.example.com/product-2.jpg", "product-2-watermarked.webp")
Step 3: Generate branded placeholders¶
Create consistent placeholder images for mockups and prototypes:
def generate_placeholder(width: int, height: int, label: str,
bg_color: str, output_path: str):
"""Generate a branded placeholder image."""
r = httpx.post(
f"{BASE_URL}/image/placeholder",
headers={"X-API-Key": API_KEY},
json={
"width": width,
"height": height,
"text": label,
"bg_color": bg_color,
"text_color": "#ffffff",
"format": "webp",
},
)
r.raise_for_status()
data = r.json()
with open(output_path, "wb") as f:
f.write(base64.b64decode(data["image"]))
# Generate placeholders for a design system
placeholders = [
(800, 600, "Hero Banner", "#3B82F6", "hero.webp"),
(400, 300, "Card Image", "#10B981", "card.webp"),
(1200, 400, "Header", "#8B5CF6", "header.webp"),
]
for w, h, label, color, path in placeholders:
generate_placeholder(w, h, label, color, path)
Step 4: Convert everything to WebP¶
Optimize all images for web delivery:
def convert_to_webp(image_url: str, quality: int = 85) -> bytes:
"""Convert any image to optimized WebP format."""
r = httpx.post(
f"{BASE_URL}/image/convert",
headers={"X-API-Key": API_KEY},
json={
"url": image_url,
"format": "webp",
"quality": quality,
},
)
r.raise_for_status()
data = r.json()
return base64.b64decode(data["image"])
# Batch convert a directory of images
images = [
"https://cdn.example.com/hero.png",
"https://cdn.example.com/logo.jpg",
"https://cdn.example.com/banner.gif",
]
for url in images:
name = url.split("/")[-1].split(".")[0]
webp_bytes = convert_to_webp(url)
with open(f"{name}.webp", "wb") as f:
f.write(webp_bytes)
print(f"Converted {name} to WebP")
Complete pipeline¶
Here's a combined script that generates social cards, watermarks images, and optimizes everything:
import asyncio
import httpx
async def process_blog_post(client: httpx.AsyncClient, title: str,
subtitle: str, image_url: str, slug: str):
"""Generate all assets for a blog post."""
# 1. Generate social card
card_r = await client.post(
f"{BASE_URL}/image/from-template",
json={
"template": SOCIAL_CARD_TEMPLATE,
"variables": {"title": title, "subtitle": subtitle},
"width": 1200, "height": 630, "format": "png",
},
)
# 2. Watermark the hero image
watermark_r = await client.post(
f"{BASE_URL}/image/watermark/image",
json={
"url": image_url,
"watermark_url": "https://toolkitapi.io/logo.png",
"position": "bottom-right",
"scale": 0.1, "format": "webp",
},
)
# 3. Save results
card_data = card_r.json()
watermark_data = watermark_r.json()
with open(f"{slug}-og.png", "wb") as f:
f.write(base64.b64decode(card_data["image"]))
with open(f"{slug}-hero.webp", "wb") as f:
f.write(base64.b64decode(watermark_data["image"]))
print(f"✓ {slug}: social card + watermarked hero generated")
# Run for all blog posts
async def main():
async with httpx.AsyncClient(
headers={"X-API-Key": API_KEY},
timeout=30.0,
) as client:
tasks = [
process_blog_post(client, "Domain Monitoring Guide",
"Build an automated domain checker",
"https://cdn.example.com/monitor-hero.jpg",
"domain-monitor"),
process_blog_post(client, "Email Validation Pipeline",
"Clean your mailing lists at scale",
"https://cdn.example.com/email-hero.jpg",
"email-pipeline"),
]
await asyncio.gather(*tasks)
asyncio.run(main())
Endpoints used¶
| Endpoint | Purpose |
|---|---|
POST /v1/image/from-template |
Render HTML templates as images |
POST /v1/image/watermark/image |
Overlay logo watermarks |
POST /v1/image/watermark/text |
Add text watermarks |
POST /v1/image/placeholder |
Generate placeholder images |
POST /v1/image/convert |
Convert between formats |
POST /v1/image/compress |
Compress images for web |
Taking it further¶
- Blog automation — Hook into a CMS webhook to auto-generate social cards when a new post is published.
- E-commerce — Watermark all product images on upload, then serve the WebP versions via a CDN.
- Email campaigns — Generate personalized banner images by passing user names into HTML templates.
- A/B testing — Generate multiple social card variants with different colors and text, then track click-through rates.