Guides

Générer et retoucher des images produit par l'API (Node, Python)

TchaviPar Gael — fondateur de Tchavi

Publié le · Mis à jour le · 11 min de lecture

Retour au blog

Sommaire

Pour une boutique sur Jumia, Afrikrea ou Instagram, deux appels suffisent : l'un génère la photo du produit à partir d'un texte, l'autre la retouche à partir de cette photo (fond blanc, étiquette de prix). Sur mes essais, une génération a coûté 11 crédits et une retouche entre 17 et 96 crédits, en 8 à 9 secondes.

J'ai fait les trois appels pour de vrai, en octobre 2026, à travers Tchavi, avec un sac en wax imaginaire. Voici le code en Node.js et en Python, ce que ça a coûté, et ce que chaque modèle a bien ou mal fait.

En bref

  • Tchavi, une passerelle d'API IA compatible OpenAI que tu paies en mobile money, génère et retouche des images avec la même clé que le texte. La route est POST /v1/images/generations, et POST /v1/images/edits pour les modèles GPT Image.
  • Génération mesurée : gpt-image-1-mini en qualité low, 1024x1024, a coûté 11 crédits (environ 3,7 F) en 8,5 secondes.
  • Retouche mesurée : 17 crédits avec gpt-image-1-mini (fond remplacé, mais le texte du prix était faux), 96 crédits avec nano-banana-2 en 0.5K (fond remplacé, sac fidèle, prix lisible).
  • Les images de référence sont envoyées en base64 dans le champ images. GPT Image en accepte 16, Nano Banana 14.
  • Une retouche GPT Image passe par /images/edits. Avec gpt-image-1-mini, la même requête envoyée à /images/generations m'a renvoyé un 502 sans rien débiter.

Quel modèle pour quoi

Tous les modèles ne prennent pas une image de référence. Sur ceux que j'ai regardés :

ModèleGénérationImage de référenceRéglages de prix
gpt-image-1-miniouioui (jusqu'à 16)quality : low, medium, high
nano-banana-2ouioui (jusqu'à 14)resolution : 0.5K, 1K, 2K, 4K
flux-2-proouinontarif unique par image

Pour flux-2-pro, j'ai envoyé une image de référence et l'API a répondu 400 bad_request : l'entrée image est réservée à certaines familles de modèles. La documentation des images donne la liste des familles, et l'onglet API de chaque modèle donne ses paramètres exacts.

Les prix en crédits pour une image carrée, d'après le catalogue (3 crédits = 1 F) :

ModèleRéglageCréditsEn F
gpt-image-1-minilow11environ 3,7 F
gpt-image-1-minimedium248 F
gpt-image-1-minihigh77environ 25,7 F
nano-banana-20.5K101,28environ 33,8 F
nano-banana-21K142environ 47,3 F
nano-banana-22K214environ 71,3 F

Pages des modèles : gpt-image-1-mini et nano-banana-2. Le conseil que je te donne : commence au réglage le moins cher, regarde le résultat, et ne monte que si l'image ne tient pas la route.

Le format de l'image change aussi le prix. Un size non carré est facturé 1,5 fois le prix carré sur gpt-image-1-mini : j'ai mesuré 16,5 crédits pour un 1024x1536 en low, contre 11 en carré. Pour un visuel vertical d'Instagram, c'est le prix à prévoir.

Étape 1 : générer la photo produit (Node.js)

import { writeFileSync } from 'node:fs';

const BASE = 'https://tchavi.com/api/v1';
const headers = {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${process.env.TCHAVI_API_KEY}`,
};

const res = await fetch(`${BASE}/images/generations`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    model: 'gpt-image-1-mini',
    quality: 'low',
    size: '1024x1024',
    prompt:
      'Photo produit pour une boutique en ligne : un sac à main en tissu wax coloré (motifs jaune, bleu et rouge), posé sur une table en bois, lumière douce de fenêtre, fond de salon flou, rendu réaliste',
  }),
});
const json = await res.json();
if (!res.ok) throw new Error(`${json.error} : ${json.message}`);

const image = json.data[0];
const ext = image.media_type.split('/')[1]; // png, jpeg ou webp
writeFileSync(`sac.${ext}`, Buffer.from(image.b64_json, 'base64'));
console.log('crédits :', res.headers.get('x-credits-used'));

L'image est dans data[0].b64_json, son type dans data[0].media_type. Lis le type au lieu de supposer du PNG : selon le modèle, tu peux recevoir du PNG, du JPEG ou du WebP.

Résultat de mon appel : statut 200, 8,5 secondes, 11 crédits, un PNG de 1,5 Mo. Le sac est lisible, avec des motifs wax plausibles, mais la matière ressemble plus à du tissu matelassé qu'à du wax. C'est le niveau de détail d'un réglage low.

Étape 2 : retoucher depuis une image de référence

Voilà la partie utile pour un vendeur : garder le produit et changer le décor. La requête est la même, avec un champ images qui contient tes images de référence en base64.

Avec GPT Image : la route /images/edits

import { readFileSync, writeFileSync } from 'node:fs';

const reference = readFileSync('sac.png').toString('base64');

const res = await fetch(`${BASE}/images/edits`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    model: 'gpt-image-1-mini',
    quality: 'low',
    size: '1024x1024',
    images: [reference],
    prompt:
      'Garde exactement le même sac. Remplace le fond par un fond blanc uni de studio, et ajoute une étiquette de prix ronde jaune avec le texte « 12 500 F » en haut à droite.',
  }),
});
const json = await res.json();
if (!res.ok) throw new Error(`${json.error} : ${json.message}`);
writeFileSync('sac-retouche.png', Buffer.from(json.data[0].b64_json, 'base64'));

Mesuré : 200, 8,9 secondes, 17 crédits (environ 5,7 F). Ce sont les 11 crédits de la génération, plus 6 crédits pour l'image de référence : sur gpt-image-1-mini, chaque image de référence est facturée en plus du prix de l'image.

Je n'ai testé que gpt-image-1-mini : je ne dis donc rien des autres modèles GPT Image, ni de /images/edits avec Nano Banana.

Le fond est bien devenu blanc et l'étiquette jaune est là. Deux défauts : le motif du sac a été redessiné (ce n'est plus exactement le même sac), et l'étiquette affiche « 12300F » au lieu de « 12 500 F ». Un texte exact dans une image est le point faible des réglages bas. Pour une étiquette de prix, je te conseille de l'ajouter toi-même en code (une image SVG ou un canevas) plutôt que de le demander au modèle.

Avec Nano Banana 2 : la route /images/generations

Pour Nano Banana, la retouche passe par la route de génération, avec resolution au lieu de quality :

import base64
import os

import requests

BASE = "https://tchavi.com/api/v1"
reference = base64.b64encode(open("sac.png", "rb").read()).decode()

r = requests.post(
    f"{BASE}/images/generations",
    headers={"Authorization": f"Bearer {os.environ['TCHAVI_API_KEY']}"},
    json={
        "model": "nano-banana-2",
        "resolution": "0.5K",
        "images": [reference],
        "prompt": "Garde exactement le même sac. Remplace le fond par un fond blanc uni de studio, "
                  "et ajoute une étiquette de prix ronde jaune avec le texte « 12 500 F » en haut à droite.",
    },
    timeout=120,
)
data = r.json()
if not r.ok:
    raise RuntimeError(f"{data['error']} : {data['message']}")

image = data["data"][0]
ext = image["media_type"].split("/")[1]
open(f"sac-nano.{ext}", "wb").write(base64.b64decode(image["b64_json"]))
print("crédits :", r.headers.get("x-credits-used"))

Mesuré : 200, 8,3 secondes, 96 crédits (32 F), soit le prix du catalogue en 0.5K ce jour-là (101,28 crédits au tarif actuel), sans supplément visible pour l'image de référence, une image de 512 pixels de côté (c'est ce que veut dire 0.5K). Ici, le résultat est nettement meilleur : le sac est fidèle à l'original, le fond est blanc, et l'étiquette dit bien « 12 500 F ».

Ce que ça m'apprend, sur un seul essai par modèle : à ces réglages, la retouche à 96 crédits tient mieux le produit et le texte que celle à 17 crédits. Ça coûte presque six fois plus. À toi de voir si ton catalogue justifie la différence. Un essai ne fait pas une statistique : refais le test avec tes propres photos avant de choisir.

Les réglages qui comptent

  • n : le nombre d'images, de 1 à 4. Les modèles Nano Banana renvoient une image par appel, n est ramené à 1.
  • size pour GPT Image : 1024x1024, 1024x1536 ou 1536x1024. Une autre valeur renvoie 400.
  • quality pour GPT Image : low, medium, high. Si tu l'oublies, high est facturé, donc le plus cher : pense à le fixer.
  • resolution pour Nano Banana : 0.5K, 1K, 2K, 4K, et aspect_ratio pour le cadrage (par exemple 1:1, 4:3, 9:16).
  • response_format : b64_json par défaut, ou url selon le modèle.

Les erreurs courantes et ce qu'elles veulent dire

J'ai provoqué ces erreurs exprès. Aucune n'a débité de crédits.

Ce que j'ai envoyéRéponseCe que ça veut dire
size: "512x512" sur gpt-image-1-mini400 bad_requestLe message liste les tailles acceptées
quality: "ultra"400 bad_requestLe message liste low, medium, high
resolution: "8K" sur nano-banana-2400 bad_requestLe message liste 0.5K, 1K, 2K, 4K
aspect_ratio: "5:1" sur nano-banana-2400 bad_requestLe message liste les rapports acceptés
images sur flux-2-pro400 bad_requestCe modèle n'accepte pas d'image de référence
image et images ensemble400 bad_requestEnvoie l'un ou l'autre
Un modèle qui n'existe pas404 model_not_foundVérifie l'identifiant avec GET /v1/models
images sur /images/generations avec gpt-image-1-mini502 provider_errorUtilise /images/edits pour les modèles GPT Image
Corps sans model ou sans prompt502 provider_errorValide ta requête côté code : l'API ne le dit pas clairement

Les autres codes viennent de la page des erreurs : 401 invalid_api_key (clé fausse), 402 insufficient_credits (solde trop bas, avec credits_remaining et credits_required dans details), 429 rate_limit_exceeded (attends Retry-After secondes). Si tu reçois un 429, une boucle de réessai avec une pause règle ça.

Un 502 ne veut pas toujours dire « panne » : sur ces deux derniers cas, c'était une requête mal formée. Avant de réessayer en boucle, relis ton corps de requête.

Une boucle prête à l'emploi pour un catalogue

Si tu as vingt produits à mettre sur fond blanc, appelle la route en série avec une pause sur 429, et garde le coût total :

async function retoucher(fichier) {
  const images = [readFileSync(fichier).toString('base64')];
  for (let essai = 0; essai < 5; essai++) {
    const res = await fetch(`${BASE}/images/edits`, {
      method: 'POST',
      headers,
      body: JSON.stringify({
        model: 'gpt-image-1-mini',
        quality: 'low',
        images,
        prompt: 'Garde le même produit, fond blanc uni de studio.',
      }),
    });
    if (res.status === 429) {
      const { details } = await res.json();
      await new Promise((r) => setTimeout(r, (details?.retry_after ?? 2) * 1000));
      continue;
    }
    const json = await res.json();
    if (!res.ok) throw new Error(`${json.error} : ${json.message}`);
    return { image: json.data[0], credits: Number(res.headers.get('x-credits-used')) };
  }
  throw new Error('trop de 429');
}

À 17 crédits la retouche, vingt produits font 340 crédits, soit environ 113 F. C'est un calcul à partir de ma mesure, pas une mesure sur vingt images.

FAQ

Combien coûte une image générée par l'API ?

Sur gpt-image-1-mini, 11 crédits en qualité low, 24 en medium et 77 en high, pour une image carrée. Avec 3 crédits pour 1 F, c'est environ 3,7 F pour la plus simple. Le coût réel de chaque appel est dans l'en-tête x-credits-used.

Quels modèles acceptent une image de référence ?

gpt-image-1-mini (jusqu'à 16 images) et nano-banana-2 (jusqu'à 14) l'acceptent, d'après mes essais et la documentation. flux-2-pro la refuse avec un 400. Pour un autre modèle, regarde son onglet API.

Pourquoi mon image retouchée ne ressemble plus au produit ?

Le modèle redessine l'image à partir de ta référence : il ne la copie pas. Plus le réglage est bas, plus il invente. Précise « garde exactement le même produit », essaie un réglage supérieur, et teste plusieurs essais avant de publier.

Comment avoir un texte exact sur l'image, comme un prix ?

Ne compte pas dessus. Mon essai à 17 crédits a écrit « 12300F » au lieu de « 12 500 F ». Ajoute le prix par code après coup, c'est gratuit et exact.

Quel format d'image je reçois ?

Lis data[0].media_type : image/png, image/jpeg ou image/webp. Mes trois images étaient des PNG, de 0,4 à 1,5 Mo, donc compresse-les avant de les mettre sur ta boutique.

Dois-je envoyer l'image en base64 ou en URL ?

Pour la référence, j'ai utilisé du base64 brut dans images. Le champ accepte aussi une chaîne data:image/png;base64,....

Comment payer sans carte bancaire ?

En mobile money : tu recharges à partir de 1 000 F, 3 crédits valent 1 F, et il n'y a ni abonnement ni crédits gratuits à l'inscription. Ta première recharge ajoute 150 crédits de bonus.

crée ton compte et recharge 1 000 F

Cet article fait partie de la série « Construire un SaaS IA depuis l'Afrique francophone ». Tchavi est une passerelle d'API IA compatible OpenAI : une seule clé donne accès à plus de 100 modèles de texte, d'image, d'audio et de vidéo, et tu la paies en mobile money, sans carte bancaire (pays et opérateurs). Plus de guides sur le blog. Pour suivre les prochains : LinkedIn et X.

Partager

Ton premier appel d'API, payé en mobile money

Sans carte bancaire. Recharge à partir de 1 000 F ; ta première recharge ajoute 150 crédits de bonus, et tes crédits n'expirent pas.