Génération d'images Compatible OpenAI Intégration API Persistance des médias Sécurité de facturation

Intégration d'une API de génération d'images : modèles compatibles OpenAI

Intégrez la génération d'images via des endpoint compatibles OpenAI, gérez les éditions, bornez n et la taille, et persistez correctement les médias temporaires avec gpt-image-2.

AveMujica API 10 min de lecture

L'intégration d'une API de génération d'images consiste à appeler depuis le code produit un endpoint stable — généralement POST /v1/images/generations et souvent POST /v1/images/edits — tout en sortant de chaque client le choix du modèle, les identifiants, les quotas et le stockage des médias. Avec une passerelle compatible OpenAI, les applications envoient un prompt et, éventuellement, model, n, size, quality ainsi que des champs GPT Image comme output_format, puis décodent les charges utiles en base64. Les réponses URL historiques ne doivent être gérées que lorsqu'un modèle les prend encore en charge. Ce guide présente un chemin d'intégration adapté à la production à partir des routes déjà exposées par cette passerelle, sans inventer de prix fournisseur ni de notes de qualité.

Forme minimale correcte pour GPT Image

Utilisez une seule URL de base authentifiée, un Bearer token émis par votre passerelle et le contrat GPT Image actuel d'OpenAI. La documentation officielle présente gpt-image-2 comme le modèle d'image principal actuel ; les générations GPT Image renvoient data[].b64_json. Le paramètre response_format concerne les anciens modèles DALL·E, pas GPT Image.

curl -sS https://YOUR_GATEWAY/v1/images/generations \
  -H "Authorization: Bearer $GATEWAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "A product photo of a ceramic mug on a wooden table, soft window light",
    "n": 1,
    "size": "1024x1024",
    "output_format": "png"
  }' | jq -r '.data[0].b64_json' | base64 --decode > mug.png

Traitez la réponse comme un média éphémère accompagné de métadonnées de facturation, et non comme une ressource produit durable. Si le produit doit conserver les images après l'expiration d'une URL temporaire du fournisseur, dans le cas de modèles historiques ou d'adaptateurs non OpenAI, téléchargez-les immédiatement dans un object storage que vous contrôlez, ou utilisez le chemin de persistance de l'atelier image personnel, autorisé selon la propriété utilisateur.

Revérifiez toujours les champs propres au modèle dans le guide OpenAI de génération d'images et la référence de l'API Images. Google documente séparément la génération d'images native de Gemini sur ai.google.dev. Une passerelle ne remplace pas ces documentations : elle normalise l'accès.

Pourquoi intégrer via une passerelle plutôt que plusieurs SDK

Les équipes produit échouent généralement pour des raisons opérationnelles, pas à cause de la qualité du modèle :

  • chaque service détient une clé fournisseur différente
  • les expériences de prompt contournent les budgets
  • des URL CDN temporaires sont stockées en base puis cessent de fonctionner
  • le nombre d'images n ou la résolution multiplie le coût sans validation
  • les environnements personnels et les projets d'équipe partagent une même clé, ce qui détruit l'attribution des coûts

Le modèle de passerelle multi-fournisseurs, présenté plus largement dans passerelle LLM multi-fournisseurs et passerelle API compatible OpenAI, stabilise les clients tandis que les opérateurs associent des alias stables aux canaux, imposent les quotas et écrivent les journaux d'usage. Pour les médias, le même plan de contrôle doit aussi répondre à la question : « qui pourra télécharger cette ressource plus tard ? »

Endpoint et champs de requête importants

Générations

POST /v1/images/generations est le point d'entrée text-to-image. Les champs courants du DTO de requête image de ce dépôt sont les suivants :

ChampRôle avec GPT Image / la passerelle
modelPréférez l'alias stable gpt-image-2, ou votre alias associé ; les anciens ID gpt-image-1* suivent le calendrier de dépréciation d'OpenAI vers gpt-image-2
promptinstruction textuelle obligatoire
nnombre facultatif ; le type pointeur préserve la présence JSON, mais cette passerelle normalise n=0, ou l'absence de n, à 1 avant la facturation
sizerésolution comme 1024x1024, 1536x1024, 1024x1536 ; GPT Image 2 documente aussi des règles WIDTHxHEIGHT plus larges
qualityjeton de qualité propre au fournisseur lorsqu'il est pris en charge
output_formatpng / jpeg / webp pour les familles GPT Image
output_compressioncompression pour les formats qui la prennent en charge
response_formatDALL·E historique uniquement (url ou b64_json) ; non pris en charge par GPT Image, qui renvoie toujours du base64
background, moderation, streamextensions propres à la famille de modèles lorsque l'adaptateur les prend en charge
userétiquette d'utilisateur final pour le suivi des abus

Ne supposez pas que tous les champs fonctionnent avec tous les modèles. Validez les modèles réellement activés dans votre environnement et la documentation actuelle du fournisseur.

Éditions

POST /v1/images/edits est la surface d'édition et d'inpainting. Les intégrations envoient généralement des champs multipart pour l'image source, un masque facultatif et le prompt. Le code produit doit :

  1. accepter les uploads utilisateur dans un stockage temporaire avec limite de taille
  2. normaliser les types MIME avant l'appel à la passerelle
  3. conserver la même authentification et le même propriétaire de facturation que pour les générations
  4. ne jamais journaliser les octets bruts des images dans les logs applicatifs

Si le produit a seulement besoin de « régénérer avec un prompt plus précis », les générations peuvent suffire. Ajoutez les éditions lorsque les utilisateurs doivent préserver la composition ou les ressources de marque.

La sécurité de facturation fait partie de l'intégration

La génération d'images comporte de nombreux multiplicateurs. Un client hostile ou défectueux qui fixe n à une valeur absurde peut transformer une requête HTTP en catastrophe comptable. Dans ce code :

  • la validation refuse un n supérieur à MaxImageN (128) avant le calcul du quota
  • un n absent ou égal à 0 est normalisé à 1 : préserver la présence du pointeur ne signifie pas accepter zéro image
  • les chemins de pré-consommation et de règlement doivent toujours refuser les charges négatives ou saturées
  • les ratios de qualité et de taille ne deviennent des multiplicateurs qu'après validation

Règles produit :

  • fixez la valeur UI par défaut de n à 1
  • n'acceptez jamais un n fourni par l'utilisateur sans limite supérieure inférieure au maximum de la plateforme
  • affichez le coût estimé avant une génération de plusieurs variantes
  • si la pré-consommation échoue, faites échouer l'UX ; ne placez pas la tâche en file de manière optimiste sur un autre portefeuille

TypeScript : générer, décoder, persister

type ImageGenResponse = {
  data: Array<{ b64_json?: string; url?: string; revised_prompt?: string }>;
};

async function generatePng(prompt: string, model = "gpt-image-2") {
  const res = await fetch(`${process.env.GATEWAY_BASE}/v1/images/generations`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GATEWAY_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model,
      prompt,
      n: 1,
      size: "1024x1024",
      output_format: "png",
    }),
  });
  if (!res.ok) throw new Error(await res.text());
  const body = (await res.json()) as ImageGenResponse;
  const b64 = body.data?.[0]?.b64_json;
  if (!b64) {
    // Legacy/provider-adapter path only: some non-GPT models may still return url.
    if (body.data?.[0]?.url) {
      return { kind: "url" as const, url: body.data[0].url, revised: body.data[0].revised_prompt };
    }
    throw new Error("no image payload");
  }
  const bytes = Buffer.from(b64, "base64");
  // upload bytes to object storage under the billing owner's key prefix
  return { kind: "bytes" as const, bytes, revised: body.data[0].revised_prompt };
}

Python : même contrat avec décodage explicite

import base64
import os
from pathlib import Path
import requests

def generate_and_store(prompt: str, model: str = "gpt-image-2") -> Path:
    r = requests.post(
        f"{os.environ['GATEWAY_BASE']}/v1/images/generations",
        headers={"Authorization": f"Bearer {os.environ['GATEWAY_KEY']}"},
        json={
            "model": model,
            "prompt": prompt,
            "n": 1,
            "size": "1024x1024",
            "output_format": "webp",
            "output_compression": 80,
        },
        timeout=120,
    )
    r.raise_for_status()
    b64 = r.json()["data"][0]["b64_json"]
    out = Path("/tmp/out.webp")
    out.write_bytes(base64.b64decode(b64))
    return out

Préférez b64_json, valeur par défaut de GPT Image, lorsque vous contrôlez le stockage et souhaitez un seul aller-retour. Ne mentionnez url que comme comportement d'un modèle historique ou d'un adaptateur fournisseur. Ne supposez jamais qu'une URL reste valide indéfiniment.

Atelier image personnel et atelier vidéo d'équipe

Ce dépôt expose un atelier image limité à la portée personnelle / session / token :

  • UI : atelier image
  • API utilisateur sous /api/user/image-workbench/* pour presets, modèles, exécutions, ressources, génération et édition
  • variantes par clé API sous /api/image-workbench/* pour générer, éditer et télécharger
  • outils d'administration des modèles sous /api/image-workbench/admin/*
  • les clés d'objet et l'historique utilisent la propriété utilisateur

L'arborescence actuelle ne contient aucune route /api/teams/:team_id/image-workbench limitée à une équipe. N'affirmez pas qu'il existe un historique image d'équipe ou des ressources image limitées à un projet, sauf si votre fork les ajoute.

En revanche, l'atelier vidéo inclut des routes équipe/projet sous /api/teams/:team_id/video-workbench. Si les responsables produit demandent « un historique image partagé comme pour la vidéo », il s'agit d'une fonctionnalité manquante, et non d'une capacité déjà fournie par l'atelier image. La facturation multi-locataires des appels bruts /v1/images/* suit toujours le propriétaire associé au token ; consultez l'architecture de passerelle LLM multi-locataires.

Utilisez l'atelier lorsque les opérateurs ou designers ont besoin d'un historique interactif et de presets. Utilisez les appels bruts /v1/images/* lorsque des services backend génèrent des ressources par programme. Les deux chemins doivent partager les mêmes alias de modèles et champs de journaux d'usage pour l'attribution ultérieure.

Liste de contrôle du cycle de vie des médias

  1. Soumettre avec une clé de passerelle limitée, pas une clé fournisseur.
  2. Valider n, en se rappelant qu'ici 0 devient 1, la taille et la longueur du prompt avant l'envoi.
  3. Enregistrer l'ID de requête ou de journal d'usage pour le support.
  4. Décoder b64_json, ou récupérer une fois une URL historique, puis persister les clés d'object storage selon votre politique de rétention.
  5. Autoriser les téléchargements ultérieurs par propriétaire : utilisateur pour les ressources de l'atelier image, propriétaire de facturation pour les tâches API que vous stockez vous-même.
  6. Faire expirer ou modérer à nouveau les ressources lorsque la politique produit l'exige.
  7. Observer les taux d'échec et les coûts par modèle dans votre stack de monitoring.

Si l'étape 4 est ignorée, les défauts produit apparaissent plusieurs semaines plus tard comme des miniatures cassées, pas comme des erreurs API.

Notes d'ingénierie des prompts et du produit

  • Séparez l'intention système, comme les règles de marque, du prompt utilisateur dans la couche applicative ; l'API Images utilise généralement une chaîne prompt unique, composez-la avec soin.
  • Stockez le prompt final envoyé, pas uniquement le brouillon de l'UI, car les fournisseurs peuvent renvoyer revised_prompt.
  • Limitez les tâches image simultanées par utilisateur ; les retries peuvent facilement déclencher un fan-out accidentel.
  • Gérez les HTTP 429/5xx avec des retries bornés et des clés clientes idempotentes lorsque la stack les prend en charge (retries et idempotence des API LLM).
  • Pour les expériences multi-modèles, changez seulement l'alias model après avoir vérifié la prise en charge de la taille et de la qualité ; ne dupliquez pas tout le client.
  • Préférez gpt-image-2 dans les exemples par défaut ; associez explicitement les anciens alias si un canal les expose encore.

Échecs d'intégration fréquents

SymptômeCause probableCorrection
Images vides après un « succès »Le client cherche url avec GPT ImageLire b64_json et le décoder
Consommation soudaine du quotan non borné ou tempête de retriesBorner n, dédupliquer les retries
n=0 devait signifier « simulation »La passerelle normalise 0 → 1Ne pas effectuer l'appel au lieu d'envoyer zéro
Fonctionne en compte personnel, échoue avec une clé d'équipeLa clé n'a pas l'autorisation projet ou utilise le mauvais groupe de modèlesCorriger les droits du token d'équipe ; ne pas supposer que l'atelier image est limité à l'équipe
Endpoint d'édition en 400Noms de champs multipart ou taille de masque incorrectsS'aligner sur le contrat de formulaire fournisseur/passerelle
Modèle introuvableAlias non associé sur le canalAssocier l'alias stable à l'ID upstream dans la configuration du canal

Lecture associée

Sources

Questions sur les échecs

Faut-il appeler OpenAI, Gemini et les autres avec trois SDK ?

Non si le produit n'a besoin que d'un seul contrat client. Préférez le chemin compatible OpenAI de la passerelle pour les générations et éditions, et réservez les SDK natifs aux fonctionnalités que la passerelle ne mappe pas encore.

response_format=url est-il sûr pour une base de production ?

C'est un mauvais choix par défaut pour GPT Image, où il n'est pas pris en charge et où la réponse est toujours en base64. Pour les anciens modèles qui renvoient encore des URL, considérez-les uniquement comme des pointeurs temporaires et réhébergez les images avant de les afficher sur des pages durables.

Quelle est une valeur sûre par défaut pour n ?

Commencez à 1 dans l'UX de production. Augmentez uniquement lorsque le produit exige explicitement plusieurs variantes, et gardez un maximum produit strict inférieur au maximum de la plateforme, 128 dans ce code. N'envoyez pas n=0 en espérant une opération vide.

Les requêtes image ont-elles besoin du streaming ?

Généralement non. Activez-le uniquement si l'adaptateur et l'UI profitent des aperçus partiels ; sinon, conservez le chemin simple requête/réponse.

Comment partager un historique d'images entre équipes sans fuite de ressources ?

Avec la passerelle actuelle, l'historique de l'atelier image est personnel. Pour une bibliothèque partagée, construisez un partage applicatif au-dessus des octets que vous stockez, ou utilisez les clés projet d'équipe uniquement pour facturer /v1/images/* tout en appliquant votre propre ACL aux objets persistés.

Premier raccordement en production

Raccordez un job backend à POST /v1/images/generations avec model=gpt-image-2, n=1, output_format=png, décodage base64 et upload immédiat dans l'object storage. Confirmez que le journal d'usage indique le propriétaire de facturation attendu, puis ajoutez un deuxième alias de modèle seulement après avoir vérifié la compatibilité taille/qualité dans la configuration du canal.