Skip to main content
Lesson 7 of 12 · Image Generation via API: GPT Image

Marketing: visuals and banners

Lesson 7 of 12 Updated November 2, 2026 Developer
On this page
  1. Marketing: visuals and banners
  2. Ad banners
  3. Social media visuals
  4. Blog article illustrations
  5. Marketing campaign kit
  6. Test before you decide
  7. Habits that make the difference

Updated November 2, 2026 — image models. gpt-image-1, which this course was originally written for, was discontinued on October 23, 2026, and gpt-image-1.5, gpt-image-1-mini, and chatgpt-image-latest will be retired on December 1, 2026. Examples now use gpt-image-2.5-sunburst, the most precise model for editing; gpt-image-2.5-flare is faster for standard generation. Both also support xhigh and max quality levels. GPT Image models always return images in base64: the response_format parameter from older DALL·E models is not accepted and has been removed from the code. Source: Image generation (OpenAI).

Marketing: visuals and banners

Marketing teams consume visuals continuously: ad banners, social posts, article illustrations, email headers. The bottleneck is almost never the idea—it’s production. GPT Image via the API shifts that bottleneck by letting you industrialize output without dropping quality, provided you encode in your prompts what an art director would demand.

Ad banners

A banner isn’t an illustration: it’s a message that must stay legible at small sizes, in a cluttered environment, in a fraction of a second. Your prompt must therefore carry the exact text in quotes and enforce readability conditions—bold sans-serif typography, high contrast between text and background, and a clear call to action. The function below locks in these constraints once and for all, varying only the message and the graphic mood, here a blue-violet gradient with light particles for a free trial offer.

from openai import OpenAI
import base64

client = OpenAI()

def banniere_pub(message: str, style: str, format_nom: str, output: str):
    """Generate an ad banner with embedded text."""
    prompt = (
        f"Professional ad banner with the text \"{message}\" "
        f"in large, legible letters, {style}, "
        "clean and modern design, clear call to action, "
        "bold sans-serif typography, high text/background contrast"
    )

    response = client.images.generate(
        model="gpt-image-2.5-sunburst",
        prompt=prompt,
        size="1536x1024",  # landscape format for banners
        quality="high",
    )

    data = base64.b64decode(response.data[0].b64_json)
    with open(output, "wb") as f:
        f.write(data)
    print(f"Banner {format_nom}: {output}")


banniere_pub(
    message="30-day free trial",
    style="blue-violet gradient, light particles, tech mood",
    format_nom="leaderboard",
    output="banniere_essai.png"
)

Social media visuals

Each platform has its own codes, and posting the same square everywhere shows immediately. A sober LinkedIn post in blues and grays won’t work on Instagram, where users expect vibrant colors and a centered composition; a vertical story requires text at the top and the visual at the bottom because the user’s thumb masks the lower part of the screen. The visuel_social function encodes these differences in a configuration dictionary that maps each platform to its native resolution and stylistic brief, then composes the prompt from the subject and text you provide.

from openai import OpenAI
import base64

client = OpenAI()

def visuel_social(plateforme: str, sujet: str, texte: str, output: str):
    """Generate a visual adapted to a social platform."""
    configs = {
        "instagram_post": {
            "size": "1024x1024",
            "style": "polished aesthetic, vibrant colors, centered composition"
        },
        "instagram_story": {
            "size": "1024x1536",
            "style": "vertical, text at top, impactful visual at bottom"
        },
        "linkedin": {
            "size": "1536x1024",
            "style": "professional and sober, blue and gray tones, corporate"
        },
        "twitter": {
            "size": "1536x1024",
            "style": "eye-catching, strong contrast, legible at small sizes"
        },
    }

    config = configs.get(plateforme, configs["instagram_post"])

    prompt = (
        f"Visual for {plateforme.replace('_', ' ')}, "
        f"subject: {sujet}, "
        f"displayed text: \"{texte}\", "
        f"{config['style']}, "
        "professional design, ready to publish"
    )

    response = client.images.generate(
        model="gpt-image-2.5-sunburst",
        prompt=prompt,
        size=config["size"],
        quality="high",
    )

    data = base64.b64decode(response.data[0].b64_json)
    with open(output, "wb") as f:
        f.write(data)
    print(f"Visual {plateforme}: {output}")


visuel_social("instagram_post", "AI product launch", "New: CorsenBot 2.0", "ig_post.png")
visuel_social("linkedin", "developer recruitment", "We're hiring!", "linkedin_post.png")
visuel_social("instagram_story", "summer promotion", "-30% this weekend", "ig_story.png")

Blog article illustrations

An article’s cover image follows a counterintuitive rule: it must contain no text. Your CMS will overlay the title, and an image that already carries letters turns into unreadable mush once the header is applied. The prompt therefore states this explicitly, in addition to requesting a panoramic composition and harmonious colors. The article list below also shows how a single function serves three very different registers—3D isometric blue for a customer service topic, colorful collage for marketing trends, dark tech with green neons for cybersecurity.

from openai import OpenAI
import base64

client = OpenAI()

def illustration_article(titre_article: str, style: str, output: str):
    """Generate a header illustration for a blog article."""
    prompt = (
        f"Conceptual illustration for an article titled \"{titre_article}\", "
        f"{style}, "
        "panoramic composition, no text in the image, "
        "harmonious colors, suitable as a blog cover image"
    )

    response = client.images.generate(
        model="gpt-image-2.5-sunburst",
        prompt=prompt,
        size="1536x1024",
        quality="high",
    )

    data = base64.b64decode(response.data[0].b64_json)
    with open(output, "wb") as f:
        f.write(data)
    print(f"Illustration: {output}")


articles = [
    ("How AI is transforming customer service", "3D isometric style, blue tones"),
    ("5 marketing trends for 2026", "modern collage, bright colors"),
    ("Cybersecurity guide for SMBs", "dark tech style, green neons"),
]

for i, (titre, style) in enumerate(articles):
    illustration_article(titre, style, f"article_header_{i+1}.png")

Marketing campaign kit

A campaign never runs on a single visual. You need the site’s hero banner, the social square, the vertical story, and the email header, all carrying the same message and palette. This is precisely where the API beats manual work: instead of re-exporting a source file four times, you describe the campaign once and let the loop produce each variant at its native resolution. The JSON manifest written at the end isn’t decoration—it’s what lets your publishing tool find the right file for the right slot.

from openai import OpenAI
import base64
import json

client = OpenAI()

def kit_campagne(
    nom_campagne: str,
    message_principal: str,
    palette: str,
    output_dir: str = "."
):
    """Generate a complete visual kit for a marketing campaign."""
    assets = [
        {
            "nom": "hero_banner",
            "size": "1536x1024",
            "prompt_extra": "large hero banner, centered text, maximum impact"
        },
        {
            "nom": "social_square",
            "size": "1024x1024",
            "prompt_extra": "square social post, centered composition"
        },
        {
            "nom": "story_vertical",
            "size": "1024x1536",
            "prompt_extra": "vertical story format, text at top, visual at bottom"
        },
        {
            "nom": "email_header",
            "size": "1536x1024",
            "prompt_extra": "email marketing header, sober and professional"
        },
    ]

    resultats = []

    for asset in assets:
        prompt = (
            f"Marketing visual for the \"{nom_campagne}\" campaign, "
            f"message: \"{message_principal}\", "
            f"palette: {palette}, "
            f"{asset['prompt_extra']}, "
            "premium design, modern typography"
        )

        response = client.images.generate(
            model="gpt-image-2.5-sunburst",
            prompt=prompt,
            size=asset["size"],
            quality="high",
        )

        path = f"{output_dir}/{nom_campagne}_{asset['nom']}.png"
        data = base64.b64decode(response.data[0].b64_json)
        with open(path, "wb") as f:
            f.write(data)

        resultats.append({"asset": asset["nom"], "path": path})
        print(f"  {asset['nom']} → {path}")

    # Save the manifest
    manifeste_path = f"{output_dir}/{nom_campagne}_manifeste.json"
    with open(manifeste_path, "w") as f:
        json.dump(resultats, f, indent=2, ensure_ascii=False)

    print(f"\nComplete kit: {len(resultats)} visuals generated")
    print(f"Manifest: {manifeste_path}")
    return resultats


kit_campagne(
    nom_campagne="rentree-2026",
    message_principal="Train in AI starting this September",
    palette="deep blue, copper orange, white"
)

Test before you decide

Marketing rarely decides by gut feel: it decides by numbers. But you need multiple versions to pit against each other, which used to cost a lot when you had to bring in a designer for each one. The A/B testing function generates as many variants as configurations you pass it, naming files by letter—A, B, C—so matching them to your campaign stats stays obvious. The example pits three treatments of the same ad for a meditation app against each other: a photographic nature-and-golden-light approach, a minimalist geometric illustration, an abstract gradient with floating particles. The concept and text don’t move; only the visual treatment is tested, which is the condition for the test result to mean anything.

from openai import OpenAI
import base64

client = OpenAI()

def ab_test_visuels(concept: str, variantes: list[dict], output_prefix: str):
    """Generate A/B variants of the same marketing concept."""
    for i, variante in enumerate(variantes):
        lettre = chr(65 + i)  # A, B, C...
        prompt = (
            f"{concept}, "
            f"style: {variante['style']}, "
            f"dominant color: {variante['couleur']}, "
            "professional design, ready to publish"
        )

        response = client.images.generate(
            model="gpt-image-2.5-sunburst",
            prompt=prompt,
            size="1024x1024",
            quality="high",
        )

        path = f"{output_prefix}_variante_{lettre}.png"
        data = base64.b64decode(response.data[0].b64_json)
        with open(path, "wb") as f:
            f.write(data)
        print(f"Variant {lettre} ({variante['style'][:30]}) → {path}")


ab_test_visuels(
    concept="Ad for a meditation app with the text 'Find your calm'",
    variantes=[
        {"style": "photographic, nature and golden light", "couleur": "green and gold"},
        {"style": "minimalist illustration, geometric shapes", "couleur": "pastel blue"},
        {"style": "abstract gradient, floating particles", "couleur": "purple and pink"},
    ],
    output_prefix="meditation_ad"
)

Habits that make the difference

Five habits separate industrialized production from a pile of mismatched visuals. Brand consistency lives in the prompt: systematically include your palette and style, or each generation starts from a default aesthetic and your feed becomes a patchwork. Text legibility imposes a length discipline—GPT Image handles typography well, but a five-to-seven-word message is the limit beyond which a banner becomes an unreadable paragraph.

Always generate at the native dimension of the medium rather than cropping afterward, because the model composes for the frame you give it and cropping shifts the subject out of its strength zone. Archive your prompts as you go: a library of proven formulations by visual type is worth far more after six months than any one-off speed gain. Finally, run every visual through a basic legal check, even just a glance, to verify it doesn’t evoke an existing trademark.

Put all this together on a fictitious product launch: a script that generates the hero banner, the Instagram post, the story, the LinkedIn visual, and two A/B variants of the main post, all accompanied by a JSON manifest listing every produced file.