URLs temporaires et stratégies de téléchargement
Sur cette page
Leçon complétée le 3 novembre 2026 d’après les pages Image Generation et Imagine outputs de xAI : le paramètre
storage_optionsconserve l’image dans la Files API et peut créer une URL publique permanente ; la documentation ne chiffre pas la durée de vie des URL éphémères (les « erreurs 403 ou 404 » de l’ancienne version n’y figurent pas) ; les images sortent en JPEG par défaut. Sources : Image Generation · Imagine outputs.
Gérer la nature éphémère des images générées
Les URL retournées par l’API de génération et d’édition d’images sont temporaires : la documentation de xAI les dit de courte durée, sans en chiffrer la durée de vie, et demande de télécharger ou de traiter les images sans attendre. Cette contrainte a un impact direct sur la conception de vos applications.
Le problème des URLs temporaires
Lorsque vous utilisez response_format="url" (le défaut), l’API retourne une URL éphémère. Passé un délai que la documentation ne chiffre pas, elle cesse de servir l’image.
# URL éphémère : à télécharger sans attendre
response = client.images.generate(
model="grok-imagine-image",
prompt="Un paysage de montagne"
)
url = response.data[0].url
Conséquence : vous ne pouvez pas stocker ces URL dans une base de données et les réutiliser plus tard. Vous devez systématiquement télécharger les images.
Stratégie 1 : téléchargement immédiat
La stratégie la plus fiable consiste à télécharger chaque image dès sa génération :
import urllib.request
import os
from datetime import datetime
def generate_and_save(prompt, output_dir="images"):
"""Génère une image et la sauvegarde immédiatement."""
os.makedirs(output_dir, exist_ok=True)
response = client.images.generate(
model="grok-imagine-image",
prompt=prompt
)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filepath = os.path.join(output_dir, f"gen_{timestamp}.jpg") # JPEG par défaut
urllib.request.urlretrieve(response.data[0].url, filepath)
print(f"Image sauvegardée : {filepath}")
return filepath
Stratégie 2 : utiliser Base64
En demandant la réponse en Base64, vous recevez directement les données binaires de l’image, sans dépendre d’une URL temporaire :
import base64
response = client.images.generate(
model="grok-imagine-image",
prompt="Un paysage de montagne",
response_format="b64_json"
)
# Les données sont directement dans la réponse
image_bytes = base64.b64decode(response.data[0].b64_json)
with open("montagne.jpg", "wb") as f: # JPEG par défaut
f.write(image_bytes)
Avantages du Base64
L’atout du Base64 tient en un mot : l’autonomie. L’image arrive directement dans la réponse JSON, sans dépendre d’une URL temporaire qui peut expirer avant que votre code ne la consomme, et sans requête HTTP supplémentaire pour la télécharger — un aller-retour réseau de moins, c’est aussi un point de défaillance de moins dans votre pipeline.
Inconvénients du Base64
La contrepartie est mécanique : l’encodage Base64 gonfle la réponse JSON d’environ 33 %, ce qui se paie trois fois — en temps de transfert de la réponse API, en mémoire consommée côté client pour la décoder, et en complexité si vous manipulez plusieurs images haute résolution dans la même requête. Pour une image ponctuelle, c’est négligeable ; pour un batch de dix images en 2k, la différence devient tangible.
Stratégie 3 : stockage cloud automatique
Pour les applications en production, combinez la génération avec un upload vers votre stockage :
import boto3
import urllib.request
import tempfile
def generate_and_upload_s3(prompt, bucket, key):
"""Génère une image et l'upload directement sur S3."""
response = client.images.generate(
model="grok-imagine-image",
prompt=prompt
)
# Télécharger dans un fichier temporaire
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp:
urllib.request.urlretrieve(response.data[0].url, tmp.name)
# Upload vers S3
s3 = boto3.client("s3")
s3.upload_file(tmp.name, bucket, key)
permanent_url = f"https://{bucket}.s3.amazonaws.com/{key}"
return permanent_url
Stratégie 4 : laisser xAI stocker l’image
Toute requête Imagine accepte un paramètre storage_options qui enregistre l’image produite dans la Files API de votre équipe, et peut en plus créer une URL publique permanente, à partager ou à intégrer. Avec le SDK xAI :
import os
import xai_sdk
client_xai = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))
response = client_xai.image.sample(
prompt="Un paysage de montagne au lever du soleil",
model="grok-imagine-image-2.0",
storage_options={"filename": "montagne.jpg", "public_url": True},
)
print(response.file_output.file_id) # identifiant stable dans la Files API
print(response.public_url) # URL publique permanente
La réponse garde l’URL éphémère habituelle et ajoute, pour chaque image, un bloc file_output : file_id, filename et, si demandée, public_url. Seul filename est obligatoire : sans autre option, l’image est stockée en privé, sans expiration ni URL publique. expires_after (de 3 600 secondes à 30 jours) programme la suppression du fichier, et public_url accepte aussi son propre expires_after, qui ne peut pas dépasser celui du fichier. Une équipe peut avoir jusqu’à 1 000 URL publiques actives ; au-delà, ou en cas d’incident, l’image est bien stockée et la réponse porte un public_url_error à la place de l’URL. Par REST, le même objet storage_options se place dans le corps de la requête, y compris pour une édition.
Pipeline robuste avec retry
Les URL temporaires peuvent parfois échouer au téléchargement. Implémentez un mécanisme de retry :
import time
import urllib.request
import urllib.error
def download_with_retry(url, filepath, max_retries=3):
"""Télécharge une image avec retry en cas d'erreur."""
for attempt in range(max_retries):
try:
urllib.request.urlretrieve(url, filepath)
return True
except urllib.error.URLError as e:
if attempt < max_retries - 1:
wait_time = 2 ** attempt # Backoff exponentiel
print(f"Tentative {attempt + 1} échouée, retry dans {wait_time}s...")
time.sleep(wait_time)
else:
print(f"Échec après {max_retries} tentatives : {e}")
return False
Bonnes pratiques
Toute la discipline découle d’un fait unique : les URL sont temporaires. Le téléchargement doit donc suivre immédiatement la génération, dans le même flux de code — chaque minute d’écart est une fenêtre où l’image peut devenir irrécupérable. Pour la même raison, une URL temporaire ne doit jamais être stockée en base comme référence permanente : elle sera morte à la prochaine consultation, et c’est le genre de bug qui n’apparaît qu’en production, des jours plus tard. Dans les applications critiques où la fiabilité prime, court-circuitez le problème avec b64_json, qui livre l’image dans la réponse, ou avec storage_options, qui la conserve chez xAI avec un identifiant stable.
Le téléchargement lui-même mérite le traitement d’une opération réseau normale : un retry avec backoff pour absorber les échecs transitoires, un nommage de fichiers par timestamp ou identifiant unique pour éviter les collisions quand plusieurs générations tournent en parallèle. Enfin, loggez les métadonnées — prompt, modèle, paramètres — avec chaque image sauvegardée : six mois plus tard, quand il faudra régénérer un visuel « comme celui-là mais en 2k », ce journal vaudra de l’or.
Points clés à retenir
- Les URL de sortie sont temporaires : téléchargez immédiatement
- Le format
b64_jsonévite le problème des URL éphémères - Le Base64 augmente la taille de la réponse de ~33%
- Implémentez un retry avec backoff exponentiel pour la robustesse
- En production, uploadez vers un stockage permanent (S3, GCS), ou faites stocker l’image par xAI avec
storage_options