One template, every size and every brand
Design a card once. The sizes endpoint lays it out for every place it goes, and a brand kit fills in whose card it is, so a SaaS can render one template in each customer's colours.
Every size in one call
curl -X POST https://api.imageapis.com/v1/images/sizes \
-H 'X-API-Key: YOUR_KEY' -H 'Content-Type: application/json' \
-d '{
"template": "tpl_4nq8vzk2hdyw7bxme3rc",
"variables": { "title": "Launch week starts Monday" },
"sizes": ["og", "instagram-post", "instagram-story", "x-header"]
}'
The answer is a list with one image record per size, each with its own url and a size field naming it. It costs one image per size, reserved before anything renders; a plan without that many left gets a 429 and nothing is charged.
A size is a preset name, "WIDTHxHEIGHT" such as "1080x1920", or an object. The presets:
| Preset | Size |
|---|---|
og, linkedin-post |
1200×630, 1200×627 |
x-post |
1600×900 |
instagram-post, instagram-portrait, instagram-story |
1080×1080, 1080×1350, 1080×1920 |
pinterest-pin |
1000×1500 |
youtube-thumbnail |
1280×720 |
x-header, linkedin-banner, email-header |
1500×500, 1584×396, 600×200 |
presentation |
1920×1080 |
a4, letter, certificate, poster, business-card |
794×1123, 816×1056, 1123×794, 1728×2304, 336×192 |
logo |
500×500 |
How a design is laid out at another size
Nothing is cropped and type keeps its proportions: everything scales by the factor that fits the original canvas inside the new one. Then:
- A layer that covers the canvas (a background photo, a full rectangle) covers the new canvas.
- A band, meaning a rectangle, shape or bar running edge to edge, stretches along its length, and a headline as wide as the canvas gets the new width.
- Everything else moves in groups. Layers within a small gap of each other move together: an avatar with its name and date, stars with their count, a label over its headline.
- A group within 15% of an edge stays that far from it, so a logo in a corner stays in the corner and a footer stays at the foot. Any other group keeps its centre at the same fraction of the canvas.
On the axis that grew, the extra room goes between groups, so a landscape card made into a story has its parts spread down the canvas, not stacked in a band across the middle. A big change of shape is the case to check by eye; when one size needs a nudge, give that size its own modifications:
"sizes": [
"og",
{ "preset": "instagram-story", "modifications": [{ "name": "title", "y": 600, "size": 96 }] }
]
The editor's Resize to menu does the same layout, so you can see a size, adjust it, and save it as a template of its own.
Brand kits
A brand keeps a customer's (or a client's, or your own product line's) colours, fonts, logo and any other values once:
curl -X POST https://api.imageapis.com/v1/brands \
-H 'X-API-Key: YOUR_KEY' -H 'Content-Type: application/json' \
-d '{
"name": "Acme",
"logo": "https://img.imageapis.com/u/upl_4kq8wz2mhxtd7vbn3rce.png",
"colors": { "primary": "#4f46e5", "background": "#0f172a", "text": "#ffffff" },
"fonts": { "heading": "Playfair Display", "body": "Inter" },
"values": { "website": "acme.com" }
}'
The template uses the brand through placeholders:
| Placeholder | From |
|---|---|
{{brand.name}} |
name |
{{brand.logo}} |
logo, for an image layer's src |
{{brand.color.primary}} |
colors.primary, for a fill, color or background |
{{brand.font.heading}} |
fonts.heading, for a text layer's font |
{{brand.image.hero}} |
images.hero |
{{brand.website}} |
values.website |
{ "name": "band", "type": "rect", "x": 0, "y": 0, "width": 1200, "height": 630, "fill": "{{brand.color.background}}" },
{ "name": "logo", "type": "image", "x": 72, "y": 64, "width": 160, "height": 48, "src": "{{brand.logo}}", "fit": "contain" },
{ "name": "title", "type": "text", "x": 72, "y": 200, "width": 1056, "height": 240,
"text": "{{title}}", "font": "{{brand.font.heading}}", "color": "{{brand.color.text}}" }
Then name the brand with the render:
curl -X POST https://api.imageapis.com/v1/images \
-H 'X-API-Key: YOUR_KEY' -H 'Content-Type: application/json' \
-d '{ "template": "tpl_4nq8vzk2hdyw7bxme3rc", "brand": "brand_6tq2mz8wkxhd4vbn7rce", "variables": { "title": "Hello from Acme" } }'
brand works on images, image sizes, PDFs, videos, collections, the template check, and signed URLs (&brand=brand_… in the signed query). A value you send in variables wins over the brand's, so one render can still override one colour. Mark one brand "default": true and it fills brand placeholders on renders that name no brand. GET /v1/brands lists them, each with the exact variables it fills.
Every size, in every brand
The two together, for a platform rendering share images for each of its customers:
const API = "https://api.imageapis.com";
const H = { "X-API-Key": process.env.IMAGEAPIS_KEY, "Content-Type": "application/json" };
for (const customer of customers) {
const r = await fetch(`${API}/v1/images/sizes`, {
method: "POST", headers: H,
body: JSON.stringify({
template: "tpl_4nq8vzk2hdyw7bxme3rc",
brand: customer.brandId,
variables: { title: customer.announcement },
sizes: ["og", "instagram-post", "instagram-story"],
}),
}).then((r) => r.json());
await saveImages(customer.id, r.images.map((i) => ({ size: i.size, url: i.url })));
}
Updating a brand changes every render from then on; signed URLs that name it render again on their next fetch.