API OpenAI Image Generation en Python : exemple pas à pas

Envoyez un prompt depuis Python, récupérez une image en base64 et enregistrez-la sur le disque. Ce guide utilise les modèles GPT Image actuels et montre comment la taille, la qualité et le format modifient le résultat. Il ajoute ensuite les modifications par masque, les aperçus en streaming, les lots asynchrones et un suivi des coûts.

API OpenAI Image Generation en Python : exemple pas à pas
Cristian Da Conceicao
Fondateur de Picasso IA

La plupart des tutoriels sur le point de terminaison d’image d’OpenAI s’arrêtent à « voici une URL ». Cela fonctionnait avec DALL-E. Ce n’est plus le cas avec les modèles GPT Image, qui renvoient des données base64 et rien d’autre, si bien que le script copié en premier plante souvent sur result.data[0].url. Ce guide part d’un script qui fonctionne, puis l’enrichit avec les tailles, les niveaux de qualité, les modifications par masque, les aperçus en streaming, les lots asynchrones et un petit suivi des coûts.

Les paramètres, noms de modèles et tarifs ci-dessous proviennent de la documentation actuelle d’OpenAI sur la génération d’images et de sa page de tarification, consultées le 2026-10-06. Lorsqu’un chiffre est susceptible de changer, le texte le signale.

Avant d’écrire le moindre code

Trois éléments doivent être en place avant que la première requête aboutisse : un nom de modèle, le SDK installé et une organisation OpenAI vérifiée. Ce dernier point piège la plupart des comptes neufs, car les modèles GPT Image renvoient une erreur d’accès tant que la vérification n’est pas terminée dans les paramètres de la console développeur.

Mains d’un développeur tapant sur un ordinateur portable posé sur un bureau en chêne, dans la lumière douce du matin

Choisir un modèle

Voici les modèles GPT Image qu’OpenAI tarifie aujourd’hui. Les cinq sont aussi disponibles sur PicassoIA, ce qui est pratique pour tester un prompt avant d’écrire la moindre ligne de code.

ModèleIdéal pourPrix de sortie image (par 1M de tokens)
gpt-image-2.5-flareGénération quotidienne rapide et de haute qualité$30.00
gpt-image-2.5-sunburstModifications précises et inpainting$30.00
gpt-image-2Version antérieure, mêmes tarifs en tokens$30.00
gpt-image-1Premier modèle GPT Image$40.00
gpt-image-1-miniCoût le plus bas$8.00

Sur PicassoIA, vous pouvez essayer côte à côte GPT Image 2.5 Flare, GPT Image 2.5 Sunburst, GPT Image 2, GPT Image 1 et GPT Image 1 Mini. Un choix raisonnable par défaut : Flare pour générer, Sunburst lorsqu’il s’agit d’une modification.

Installer et s’authentifier

Installez le SDK officiel :

pip install --upgrade openai pillow

Créez un secret de projet dans le tableau de bord OpenAI, puis exposez-le sous forme de variable d’environnement. Le SDK le lit automatiquement, si bien que le secret n’apparaît jamais dans votre fichier source.

# macOS / Linux
export OPENAI_API_KEY="sk-..."

# Windows PowerShell
$env:OPENAI_API_KEY = "sk-..."

💡 Astuce : gardez le secret hors des notebooks que vous comptez partager et hors de l’historique git. S’il fuite, révoquez-le dans le tableau de bord et créez-en un nouveau.

Votre première image en Python

Le script minimal

Voici le script complet. Exécutez-le et un PNG apparaît à côté du script.

import base64
from pathlib import Path
from openai import OpenAI

client = OpenAI()

result = client.images.generate(
    model="gpt-image-2.5-flare",
    prompt="A ceramic bowl of ripe peaches on a linen cloth, soft window light, 50mm photograph",
    size="1536x1024",
    quality="medium",
)

image_bytes = base64.b64decode(result.data[0].b64_json)
Path("peaches.png").write_bytes(image_bytes)

Quatre arguments font le travail. model choisit le moteur, prompt décrit l’image, size fixe les dimensions en pixels, et quality arbitre entre vitesse, coût et détail. Tout le reste a une valeur par défaut raisonnable, et le format de sortie est PNG, sauf si vous en demandez un autre.

Développeur relisant un premier script Python sur un ordinateur portable au crépuscule

Rédiger des prompts solides

Un prompt qui fonctionne dans une démo peut s’effondrer au bout de cinquante images générées en boucle. Quatre habitudes gardent les résultats stables :

  • Commencez par le sujet, puis le décor, la lumière et l’objectif. « Un bol en céramique de pêches mûres posé sur un linge en lin, lumière douce venant de la fenêtre à gauche, photographie 50 mm » fait mieux qu’une liste d’adjectifs.
  • Mettez entre guillemets tout texte qui doit apparaître sur l’image, et limitez-le à un ou deux mots.
  • Dites ce qu’il faut éviter en termes positifs. « Mur blanc et nu » fonctionne mieux que « pas d’encombrement ».
  • Changez une seule chose à la fois. Si vous modifiez en même temps le sujet, la lumière et la taille, vous ne saurez pas quel changement a aidé.

Pourquoi la réponse est en base64

Les modèles GPT Image renvoient toujours du base64. L’option response_format="url" acceptée par DALL-E n’est pas prise en charge, donc result.data[0].url n’existe pas, et l’image arrive directement dans b64_json. Trois habitudes en découlent :

  • Décodez une fois, écrivez sur le disque. base64.b64decode vous donne des octets bruts que vous pouvez enregistrer, importer ou confier à Pillow.
  • Hébergez le fichier vous-même. Si un blog ou une application a besoin d’un lien public, transférez les octets vers votre propre stockage (S3, R2, un CDN) et conservez cette URL.
  • Évitez le disque pour les aperçus rapides. Construisez une data URI avec f"data:image/png;base64,{b64}" et insérez-la dans une balise <img>.

💡 Vous migrez un ancien code ? Rechercher .url et response_format dans votre projet permet de trouver presque toutes les lignes à modifier.

Tirages photo étalés en éventail sur un bureau en noyer

Taille, qualité et format de sortie

Ces paramètres déterminent ce que vous obtenez et ce que cela coûte. Voici la liste complète publiée par la documentation :

ParamètreValeurs acceptéesRemarques
size1024x1024, 1536x1024, 1024x1536, ou WIDTHxHEIGHT personnaliséLes côtés personnalisés doivent être des multiples de 16, le format compris entre 1:3 et 3:1, le plus long côté jusqu’à 3 840 px, et le nombre total de pixels entre 655 360 et 8 294 400
qualitylow, medium, high, autoLes modèles 2.5 listent aussi xhigh et max
output_formatpng (par défaut), jpeg, webpChoisissez webp ou jpeg pour des fichiers plus légers
output_compression0 à 100JPEG et WebP uniquement
backgroundtransparent, opaque, autoLa transparence nécessite un format avec canal alpha, donc utilisez PNG ou WebP
nEntierPlusieurs images pour une même requête
moderationauto (par défaut), lowlow applique un filtrage plus léger
stream, partial_imagesBooléen, 0 à 3Images d’aperçu pendant le rendu de l’image finale

Quelques règles empiriques :

  • Paysage et portrait. 1536x1024 et 1024x1536 conviennent à la plupart des formats de blog et de réseaux sociaux. Pour un vrai 16:9, demandez 2048x1152 : les deux côtés sont des multiples de 16 et le nombre de pixels reste bien dans la plage autorisée.
  • Itérez à petit prix, finalisez en qualité. Testez vos prompts à quality="low", puis relancez le meilleur résultat à high. Vous payez les tokens de sortie, et une qualité supérieure en produit davantage.
  • Choisissez le format selon la destination. Gardez PNG pour la retouche et la transparence, passez à webp avec output_compression=85 pour les pages qui doivent se charger vite.

Trois tirages encadrés aux formats carré, paysage et portrait sur un mur de briques

Modifier des images existantes avec des masques

Modifier une image

images.edit prend un fichier source et un prompt décrivant la modification. Passez une liste de fichiers lorsque vous voulez combiner plusieurs références.

with open("living-room.png", "rb") as photo:
    edited = client.images.edit(
        model="gpt-image-2.5-sunburst",
        image=photo,
        prompt="Swap the grey sofa for a green velvet armchair, keep the window light unchanged",
    )

Path("living-room-edit.png").write_bytes(base64.b64decode(edited.data[0].b64_json))

Décrivez aussi clairement ce qui doit rester identique que ce qui doit changer. Les modèles dérivent lorsque le prompt ne nomme que l’élément nouveau.

Ajouter un masque

Un masque limite la modification à une seule zone. C’est un PNG avec canal alpha, aux mêmes dimensions que la source. Les pixels entièrement transparents marquent la zone à repeindre ; tout ce qui est opaque est protégé. Pillow en crée un en quelques lignes :

from PIL import Image, ImageDraw

base = Image.open("living-room.png").convert("RGBA")
mask = Image.new("RGBA", base.size, (0, 0, 0, 255))          # opaque: keep
ImageDraw.Draw(mask).rectangle((620, 380, 1180, 900), fill=(0, 0, 0, 0))  # transparent: repaint
mask.save("mask.png")

with open("living-room.png", "rb") as photo, open("mask.png", "rb") as hole:
    edited = client.images.edit(
        model="gpt-image-2.5-sunburst",
        image=photo,
        mask=hole,
        prompt="A green velvet armchair with a wooden side table, matching the room's light",
    )

Le masque est une indication, pas une découpe nette. Les bords peuvent déborder légèrement, prévoyez donc une petite marge autour de l’objet à remplacer.

Main d’un retoucheur tenant un stylet sur une tablette graphique, à côté d’un portrait imprimé

Aperçus en streaming et lots

Images partielles pendant l’attente

Les rendus en haute qualité peuvent prendre du temps. Avec stream=True et partial_images, l’API envoie des images d’aperçu avant l’image finale, si bien qu’une interface peut afficher une progression au lieu d’un simple spinner.

stream = client.images.generate(
    model="gpt-image-2.5-flare",
    prompt="A vintage red bicycle leaning on a brick wall, golden hour, 35mm photograph",
    size="1536x1024",
    quality="high",
    stream=True,
    partial_images=2,
)

for event in stream:
    if event.type == "image_generation.partial_image":
        Path(f"preview-{event.partial_image_index}.png").write_bytes(base64.b64decode(event.b64_json))
    else:
        Path("final.png").write_bytes(base64.b64decode(event.b64_json))

Les aperçus peuvent augmenter le nombre de tokens, donc comparez usage avec et sans streaming avant de l’activer pour chaque requête.

Lots asynchrones sans erreurs de limite

Pour une liste de prompts, AsyncOpenAI associé à un sémaphore maintient sous contrôle le nombre de requêtes en cours :

import asyncio
from openai import AsyncOpenAI

client = AsyncOpenAI(max_retries=4, timeout=180)
gate = asyncio.Semaphore(3)

async def render(index: int, prompt: str) -> Path:
    async with gate:
        result = await client.images.generate(
            model="gpt-image-2.5-flare", prompt=prompt, size="1536x1024", quality="low",
        )
    path = Path(f"batch-{index:02d}.png")
    path.write_bytes(base64.b64decode(result.data[0].b64_json))
    return path

async def main(prompts: list[str]) -> list[Path]:
    return await asyncio.gather(*(render(i, p) for i, p in enumerate(prompts)))

Un sémaphore limite la concurrence, pas le nombre d’images par minute. Avec les niveaux d’utilisation bas, la limite par minute est atteinte en premier, donc augmentez max_retries et laissez le SDK temporiser. Lorsque personne n’attend le résultat, le point de terminaison Batch est pris en charge par ces modèles et facture les tokens de sortie à moitié prix.

Coûts, limites et erreurs

Tarifs en tokens par modèle

OpenAI tarifie ces modèles au token, et non à l’image, et ne publie pas de tableau par image pour les modèles actuels. Tarifs par 1M de tokens :

ModèleEntrée texteEntrée imageSortie imageSortie image (Batch)
gpt-image-2.5-flare$5.00$8.00$30.00$15.00
gpt-image-2.5-sunburst$5.00$8.00$30.00$15.00
gpt-image-2$5.00$8.00$30.00$15.00
gpt-image-1$5.00$10.00$40.00$20.00
gpt-image-1-mini$2.00$2.50$8.00$4.00

La qualité et la taille modifient le nombre de tokens de sortie qu’une image utilise, ce qui explique qu’un même prompt puisse coûter des montants très différents à low et à high.

Carnet, calculatrice et épreuves imprimées sur le bureau d’un freelance

Suivre les dépenses dans le code

La réponse contient un objet usage avec les décomptes de tokens. Convertissez-le en dollars après chaque appel et vous ne serez jamais surpris par une facture :

RATES = {"text_in": 5.00, "image_in": 8.00, "image_out": 30.00}  # USD per 1M tokens

def cost_usd(usage) -> float:
    details = usage.input_tokens_details
    return (
        details.text_tokens * RATES["text_in"]
        + details.image_tokens * RATES["image_in"]
        + usage.output_tokens * RATES["image_out"]
    ) / 1_000_000

print(f"Last image: ${cost_usd(result.usage):.4f}")

Les limites de débit dépendent de votre niveau d’utilisation. Pour gpt-image-2.5-flare, la page du modèle indique ces limites au moment de la rédaction :

NiveauTokens par minuteImages par minute
Niveau 1100K5
Niveau 2250K20
Niveau 3800K50
Niveau 43M150
Niveau 58M250

Erreurs que vous rencontrerez

Développeur se massant la nuque en lisant une erreur sur son ordinateur portable, la nuit

SymptômeCause probableCorrection
Erreur d’accès au premier appelOrganisation non vérifiéeTerminez la vérification dans la console développeur
AttributeError sur .urlLes modèles GPT Image renvoient uniquement du base64Lisez plutôt b64_json
« Bad request » mentionnant la modérationLe prompt ou l’image d’entrée a déclenché un filtre de sécuritéReformulez le prompt et évitez les sujets sensibles
« Bad request » sur sizeCôté non multiple de 16, ratio supérieur à 3:1, ou nombre de pixels hors plageAjustez les dimensions
429 (limite de débit)Limite du niveau atteinteRéduisez la concurrence, augmentez max_retries ou utilisez Batch
TimeoutLa haute qualité peut prendre du tempsAugmentez timeout côté client

Interceptez les trois erreurs qui comptent en production :

import openai

try:
    result = client.images.generate(model="gpt-image-2.5-flare", prompt=prompt, size="1536x1024")
except openai.BadRequestError as err:
    print("Rejected:", err.message)
except openai.RateLimitError:
    print("Image limit reached, slow down")
except openai.APIConnectionError:
    print("Network problem, retry later")

Comment utiliser GPT Image 2 sur PicassoIA

Avant de dépenser votre budget API pour tester des prompts, lancez-les dans un navigateur. GPT Image 2 sur PicassoIA rend du texte lisible dans les images, prend en charge les fonds transparents, accepte des images de référence et produit jusqu’à 10 variantes par exécution.

Prototyper des prompts sans code

  1. Ouvrez la page du modèle et rédigez votre prompt. Mettez entre guillemets tout texte qui doit apparaître dans l’image.
  2. Réglez la qualité sur low pour les brouillons. Passez à high pour le rendu final.
  3. Choisissez un format : 3:2 ou 2:3 correspondent à 1536x1024 et 1024x1536, et 16:9 donne un cadre panoramique.
  4. Choisissez le format de sortie. WebP est le format par défaut, PNG conserve proprement la transparence.
  5. Réglez le nombre d’images entre 1 et 10, puis lancez la génération.
  6. Importez des images de référence si vous voulez modifier plutôt que créer à partir de zéro.

Les champs du formulaire correspondent à l’appel Python, si bien qu’une recette qui fonctionne dans le navigateur passe directement dans le code :

Champ PicassoIAArgument Python
aspect_ratiosize
qualityquality
output_formatoutput_format
output_compressionoutput_compression
backgroundbackground
moderationmoderation
number_of_imagesn
input_imagesimage (dans images.edit)

Un champ facultatif accepte votre propre clé OpenAI ; laissez-le vide et le proxy de PicassoIA gère la requête.

Designer faisant défiler une photo de portrait sur une tablette dans un studio lumineux

Deux autres habitudes en valent la peine. D’abord, rédigez le prompt avec un grand modèle de langage : collez une idée brute dans GPT 5 ou Claude Sonnet 4.6 et demandez trois variantes photographiques avec objectif, lumière et cadrage. Ensuite, passez le même prompt dans PicassoIA Image et Seedream 4.5 pour voir quel style convient à votre projet. Pour les modifications qui ne demandent aucun code, PicassoIA Image Editor Pro traite directement les retouches photo dans le navigateur.

L’API développeur de PicassoIA

Si votre pipeline nécessite un autre fournisseur, PicassoIA dispose de sa propre API développeur, au format similaire à Replicate :

  • URL de base : https://api.picassoia.com/v1
  • Authentification : un jeton Bearer qui commence par pia_sk_
  • Déroulement : créez une prédiction avec POST /v1/models/{owner}/{name}/predictions, interrogez GET /v1/predictions/{id}, puis lisez le résultat
  • Limite : 5 prédictions simultanées par compte

Les tâches sont asynchrones, donc la boucle création puis interrogation remplace l’appel bloquant unique que vous avez écrit plus haut. Vérifiez les exigences du forfait sur la page de l’API PicassoIA avant de construire dessus.

Créez vos propres images dès aujourd’hui

Vous disposez maintenant d’un pipeline fonctionnel : installer le SDK, vérifier l’organisation, demander une image, décoder le base64, régler size et quality, modifier avec un masque, diffuser des aperçus, regrouper en lots avec des limites et suivre les dépenses. Le moyen le plus rapide d’améliorer les résultats reste le volume. Rédigez dix prompts, lancez-les à low, gardez les deux meilleurs et refaites le rendu de ceux-ci à high.

Professionnelle de la création face à un mur de studio couvert de photographies imprimées

Si vous voulez tester des prompts avant de toucher à l’API, ouvrez GPT Image 2 sur PicassoIA, générez quelques variantes et comparez-les avec d’autres modèles dans la bibliothèque de modèles PicassoIA. Une fois que vous avez des images fixes qui vous plaisent, la collection d’effets de PicassoIA peut y ajouter du mouvement et du style. Choisissez un sujet qui vous tient à cœur, rédigez un prompt détaillé et voyez ce qui en sort.

Partager cet article

Choisissez votre langue