AI certificate background via API: text-free art, names added in code

Generate a landscape certificate border at 4:3 or 3:2, with no text, then print names and dates with code so nothing is misspelled. Sume rows and cost.

5 min readSume
All posts

For certificates, diplomas or award cards, generate the decorative background and border with the Sume image API and print the names yourself. Names and dates are exactly the text that must not contain a typo, and image models sometimes alter letters. Splitting the job means one generation (about four cents) and one deterministic text overlay for any number of recipients.

Landscape ratios on the catalog (read 2026-10-03): 4:3 and 3:2 are listed by most rows. Seedream 4.0, 4.5 and 5.0 Lite, Qwen Image and Max, Flux 2 Pro and Flex all list 3:2; the Imagen rows list 4:3 but not 3:2.

Generate the border

Say that the center must stay empty and that there is no text. A single n: 4 call gives four designs for four times the price: $0.15 on Flux 2 Pro. A call that is still running after the 30-second wait returns 202 with a job envelope instead of 200, so check the status code before reading data; see Jobs and results.

import os
import requests

resp = requests.post(
    "https://api.sume.com/v1/images",
    headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
    json={
        "model": "black-forest-labs/flux.2-pro",
        "prompt": "elegant certificate border, thin gold line ornaments in the corners, cream paper texture, large empty center, no text, no letters",
        "aspect_ratio": "3:2",
        "n": 4,
    },
    timeout=60,
)
resp.raise_for_status()
if resp.status_code == 202:
    print("still running:", resp.json()["data"]["status_url"])
else:
    for image in resp.json()["data"]:
        print(image["url"])

Print the names

Read a CSV and write one PNG per person. Use a font file you have the licence for; the call below falls back to Pillow's bundled font.

import csv
from PIL import Image, ImageDraw, ImageFont

bg = Image.open("border.png").convert("RGB")
font = ImageFont.load_default(size=bg.width // 18)
with open("names.csv", newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        img = bg.copy()
        d = ImageDraw.Draw(img)
        w = d.textlength(row["name"], font=font)
        d.text(((img.width - w) / 2, img.height * 0.45), row["name"], font=font, fill="black")
        img.save(f"certificate-{row['name']}.png")

Why not let the model write the names

Text-in-image is a use for which you can test Ideogram, Flux 2 Flex and others, and Sume publishes no accuracy rate. For a list of 200 different names, any per-image error rate becomes a proofreading job. Deterministic text takes a few lines.

Sources

Related posts

More in Use cases

All Use cases posts

Written by Sume