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, etPOST /v1/images/editspour les modèles GPT Image. - Génération mesurée :
gpt-image-1-minien 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 avecnano-banana-2en 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. Avecgpt-image-1-mini, la même requête envoyée à/images/generationsm'a renvoyé un502sans 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èle | Génération | Image de référence | Réglages de prix |
|---|---|---|---|
gpt-image-1-mini | oui | oui (jusqu'à 16) | quality : low, medium, high |
nano-banana-2 | oui | oui (jusqu'à 14) | resolution : 0.5K, 1K, 2K, 4K |
flux-2-pro | oui | non | tarif 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èle | Réglage | Crédits | En F |
|---|---|---|---|
gpt-image-1-mini | low | 11 | environ 3,7 F |
gpt-image-1-mini | medium | 24 | 8 F |
gpt-image-1-mini | high | 77 | environ 25,7 F |
nano-banana-2 | 0.5K | 101,28 | environ 33,8 F |
nano-banana-2 | 1K | 142 | environ 47,3 F |
nano-banana-2 | 2K | 214 | environ 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,nest ramené à 1.sizepour GPT Image :1024x1024,1024x1536ou1536x1024. Une autre valeur renvoie400.qualitypour GPT Image :low,medium,high. Si tu l'oublies,highest facturé, donc le plus cher : pense à le fixer.resolutionpour Nano Banana :0.5K,1K,2K,4K, etaspect_ratiopour le cadrage (par exemple1:1,4:3,9:16).response_format:b64_jsonpar défaut, ouurlselon 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éponse | Ce que ça veut dire |
|---|---|---|
size: "512x512" sur gpt-image-1-mini | 400 bad_request | Le message liste les tailles acceptées |
quality: "ultra" | 400 bad_request | Le message liste low, medium, high |
resolution: "8K" sur nano-banana-2 | 400 bad_request | Le message liste 0.5K, 1K, 2K, 4K |
aspect_ratio: "5:1" sur nano-banana-2 | 400 bad_request | Le message liste les rapports acceptés |
images sur flux-2-pro | 400 bad_request | Ce modèle n'accepte pas d'image de référence |
image et images ensemble | 400 bad_request | Envoie l'un ou l'autre |
| Un modèle qui n'existe pas | 404 model_not_found | Vérifie l'identifiant avec GET /v1/models |
images sur /images/generations avec gpt-image-1-mini | 502 provider_error | Utilise /images/edits pour les modèles GPT Image |
Corps sans model ou sans prompt | 502 provider_error | Valide 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.