Guide · 2026-10-12

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.

Get a free key: 150 images a month API reference