Une génération réussie peut malgré tout devenir une image introuvable dans votre produit si vous enregistrez son URL de livraison temporaire au lieu de copier le fichier. Les liens signés expirent en quelques minutes ; la sortie sous-jacente de l’API reste conservée environ deux semaines.
Une API de médias fournit le même fichier sous plusieurs identités : une référence d’entrée, une clé de sortie générée et une URL signée permettant de récupérer les octets pendant quelques minutes. Chacune expire selon son propre calendrier, et aucune n’est une adresse permanente. Ce guide précise les fenêtres de SpicyAPI, puis montre comment copier les résultats précieux dans un stockage que vous contrôlez. Les réglages de conservation peuvent raccourcir ces fenêtres.
Essayez d’abord dans le Playground
Ouvrez la page Z-Image de texte vers image et connectez-vous :
- Saisissez une courte scène dans Prompt et choisissez un Aspect ratio.
- Cliquez sur Run model. Le résultat apparaît sous Output.
- Téléchargez l’image et enregistrez-la à un endroit que vous contrôlez.
- Rouvrez le fichier enregistré plus tard. Il s’ouvre sans la page ni son lien de résultat temporaire : c’est ce que la suite du guide automatise pour la production.
À retenir
- Copiez dès la fin, dans un processus de travail. Évitez les gros téléchargements dans la requête de callback.
- Gardez les liens signés privés. Posséder l’un de ces liens donne un accès temporaire au fichier.
- Exposez votre propre ID de ressource et URL de livraison. Un enregistrement de tâche peut survivre à ses médias.
Ce qui expire, et quand
| Élément | Durée de validité | Conséquence à l’échéance |
|---|---|---|
Ticket d’envoi (uploadUrl) | Environ 20 minutes ; le PUT et la validation doivent tous deux finir dans ce délai | Une réponse indiquant un ticket expiré impose un nouveau ticket ; une validation occupée peut aussi renvoyer 409 |
Référence validée (spicy://f/fil_...) | 1 jour à partir de la validation, jusqu’au expiresAt de sa réponse | URI refusé pour les nouvelles tâches et les nouvelles tentatives |
Lien du résultat (output.assets[].url) | Environ 20 minutes, sans jamais dépasser l’échéance du média lui-même | Le stockage répond 403 ; relisez la tâche pour obtenir un nouveau lien |
| Image, vidéo ou audio généré | Environ 14 jours à partir de la fin (moins si vous le configurez ainsi) | Supprimé ; aucun lien ne peut plus être émis (410 de download-url) |
| Texte du prompt et chemins des fichiers du résultat dans la tâche | 30 jours | input et output sont vidés ; l’enregistrement de tâche reste |
| Enregistrement de tâche (ID, modèle, état, coût, horodatages) | Conservé indéfiniment | Toujours consultable, sans contenu |
Les médias de courts-métrages créés dans un projet Spicy Studio ont leur propre fenêtre plus longue, d’environ 60 jours ; les générations via API, SDK et Playground utilisent le plafond de 14 jours ci-dessus. La conservation des générations peut aussi être réduite par requête ou par compte, comme l’explique l’article sur les réglages de conservation. Vous pouvez uniquement raccourcir ces plafonds, jamais les allonger.
Les 14 jours commencent à la fin de la tâche, pas à sa soumission. Avant la fin, l’horodatage est provisoire et est recalé au moment de l’achèvement.
« Environ » est volontaire. Le nettoyage du stockage suit une règle de cycle de vie et peut intervenir jusqu’à un jour après l’échéance, mais ce retard ne vous donne pas de temps supplémentaire : dès que la fenêtre se ferme, aucun lien de téléchargement n’est émis.
Un fichier, quatre représentations
local reference image
│
├─ POST /api/v1/common/upload-url → upload ticket (about 20 min)
├─ PUT to the returned uploadUrl → temporary object
├─ POST /api/v1/files/{fileId}/commit → spicy://f/fil_... (usable 1 day)
├─ POST /api/v1/jobs/createTask → task
└─ terminal callback or recordInfo
└─ output.assets[] → key + url + expiresAt (about 20 min)
└─ stream bytes to your storage → your asset ID and delivery URLLes SDK, la CLI et le Playground effectuent les étapes d’envoi pour vous ; vous avez donc rarement à les écrire vous-même. Chaque représentation a un rôle. Le ticket autorise une écriture directe pendant sa validité ; la validation fige les octets acceptés. L’URI spicy:// permet à une tâche de référencer pendant un jour une entrée validée, limitée au compte. Le key de sortie identifie un résultat généré. L’url autorise temporairement une lecture. Votre ID de ressource est la seule identité durable que votre produit devrait exposer.
L’envoi de la référence à SpicyAPI est gratuit ; vous payez la tâche de génération qui l’utilise. Actualiser un lien vers une sortie existante ne crée pas de nouveaux frais de génération. Si la sortie est perdue et que vous décidez de générer à nouveau, cette nouvelle tâche entraîne ses propres frais de modèle. Prévoyez séparément le stockage, les requêtes et les transferts de votre compartiment durable selon l’offre de votre fournisseur de stockage.
Entrées : envoyer, puis prévoir l’expiration après 1 jour
Conservez l’image, la vidéo ou l’audio de référence d’origine dans votre propre stockage. Envoyez une entrée locale via SDK, CLI ou Playground et réutilisez son URI validé tant qu’il reste valable. Renvoyez-la au besoin après expiration ; l’URI temporaire n’est pas votre archive.
Trois façons de fournir une entrée à un modèle :
- Adresse HTTPS publique. Fonctionne si le champ du modèle accepte une URL, à condition qu’elle renvoie le fichier lui-même. Un lien de disque cloud, d’album ou de réseau social renvoie une page web, et la tâche échoue avec
invalid_asset; téléchargez le fichier et envoyez-le à la place. - Petite image intégrée. JPEG, PNG, WebP ou GIF sous forme d’URI de données base64, jusqu’à 1 MiB chacun.
- Envoi. Pour les fichiers locaux. Images (JPEG, PNG, WebP, GIF) jusqu’à 10 MiB ; audio et vidéo WAV, MP3, WebM, MP4 ou MOV jusqu’à 90 MiB et 600 secondes. Un modèle peut accepter moins : vérifiez donc la limite du champ sur sa page. Dans le TypeScript SDK,
await client.uploadFile("./reference.png")renvoie l’URIspicy://dansfile.uri.
N’exposez jamais la clé SpicyAPI dans un navigateur ; faites émettre les tickets d’envoi par votre serveur. La documentation des médias présente l’intégration au navigateur et toutes les limites.
Sorties : à quoi ressemble un résultat terminé
Une tâche terminée et un fichier récupérable sont deux états différents :
- Prêt : copiez le fichier dans votre stockage et consignez son ID de tâche, son format et sa taille.
- En attente : conservez la même tâche et vérifiez à nouveau ; n’achetez pas une autre génération pour obtenir le fichier.
- Indisponible : la copie de la plateforme a disparu et attendre ne la fera pas revenir. Utilisez votre copie enregistrée ; sinon, générez à nouveau, avec la facturation normale. Seul un échec initial de stockage sans fichiers livrés n’est pas facturé.
Les liens temporaires donnent accès à leur détenteur. Évitez de les placer sur des pages publiques ou dans les outils d’analyse ; obtenez un lien actuel lorsque le processus de copie est prêt.
La limite de conservation
Un enregistrement de tâche, une clé de sortie ou un lien de téléchargement actualisé ne conserve pas les octets. Copiez-les avant l’échéance du média.
Quand la fenêtre se ferme, recordInfo indique contentState: "expired", et input et output sont vidés au plus tard après 30 jours. Si vous avez vous-même détruit le contenu de la tâche, l’état est purged. L’état de tâche expired est différent : il signifie que la génération a dépassé son délai et n’a rien produit. N’affichez pas les deux avec le même libellé dans votre interface : l’un appelle une nouvelle tentative, tandis que l’autre ne peut pas être récupéré ainsi.
Fixez l’échéance de copie bien avant la limite. Copiez au premier événement de réussite, puis lancez un travail de rapprochement qui recense les tâches réussies sans ressource durable et alerte tant qu’il reste encore des jours au résultat. Le jour 14 est une marge de récupération, pas un calendrier. Si un réglage du compte ou de la requête raccourcit la conservation, outputsExpireAt peut arriver bien avant 14 jours : fondez les alertes sur ce champ, jamais sur une constante. Un réglage du compte peut aussi agir rétroactivement : raccourcir le délai du texte du prompt supprime les résultats de tâches déjà terminées avant le outputsExpireAt qui leur avait été attribué. Téléchargez ce que vous voulez garder avant de modifier les réglages du compte et attendez-vous à ce que le rapprochement signale ces tâches comme perdues, plutôt que comme des bugs de la chaîne de traitement.
Une chaîne de callbacks qui résiste aux reprises et aux grosses vidéos
Dans une application, vérifiez l’événement de fin, enregistrez-le durablement et mettez la copie en file d’attente avant de répondre. Rendez la copie idempotente pour qu’un événement répété ne crée pas un nouvel élément dans la galerie. Alertez sur les résultats non copiés avant leur échéance acceptée.
Ce processus autonome enregistre les fichiers d’une tâche existante dans un répertoire local privé. Installez spicyapi, définissez SPICY_API_KEY, TASK_ID et éventuellement ARCHIVE_DIR, puis exécutez-le. Il ne crée aucune génération. Placez ce répertoire sur un stockage persistant et incluez-le dans votre plan de sauvegarde ; un disque de serveur éphémère n’est pas une archive durable.
import hashlib
import json
import os
from pathlib import Path
import tempfile
import urllib.request
from spicyapi import SpicyClient, output_assets
client = SpicyClient()
task_id = os.environ["TASK_ID"]
root = Path(os.environ.get("ARCHIVE_DIR", "media-archive"))
root.mkdir(parents=True, exist_ok=True, mode=0o700)
task = client.get_task(task_id)
if task["state"] != "succeeded":
raise RuntimeError("Resume the existing task before archiving it.")
for index, asset in enumerate(output_assets(task)):
if asset.get("pending") or asset.get("unavailable") or not asset.get("url"):
raise RuntimeError("Asset is not ready; retain the task ID.")
request = urllib.request.Request(asset["url"], headers={"User-Agent": "SpicyAPI-Blog-Example/1.0"})
temporary = None
try:
with urllib.request.urlopen(request, timeout=300) as response:
mime = response.headers.get_content_type()
suffix = {"image/png": ".png", "image/jpeg": ".jpg", "image/webp": ".webp",
"video/mp4": ".mp4", "video/webm": ".webm", "audio/mpeg": ".mp3", "audio/wav": ".wav"}.get(mime)
if response.status != 200 or suffix is None:
raise RuntimeError("Unexpected response format; do not guess an extension.")
digest, size = hashlib.sha256(), 0
with tempfile.NamedTemporaryFile(dir=root, delete=False) as output:
temporary = Path(output.name)
while chunk := response.read(1024 * 1024):
output.write(chunk)
digest.update(chunk)
size += len(chunk)
if not size or (asset.get("bytes") and size != int(asset["bytes"])):
raise RuntimeError("Empty or incomplete transfer.")
filename = root / (digest.hexdigest() + suffix)
temporary.replace(filename)
record = {"taskId": task_id, "index": index, "file": filename.name,
"mime": mime, "bytes": size, "sha256": digest.hexdigest()}
(root / (digest.hexdigest() + ".json")).write_text(json.dumps(record, indent=2))
print(filename)
finally:
if temporary is not None:
temporary.unlink(missing_ok=True)Pour une livraison depuis un stockage objet, transférez en continu vers un objet intermédiaire privé et ne l’exposez qu’après un transfert complet. La documentation des médias et des webhooks couvre l’actualisation des résultats et les événements authentifiés. Gardez l’interrogation comme voie de rapprochement pour les callbacks manqués.
Votre ressource durable doit masquer le parcours de génération
Une fois copié, l’objet servi par votre produit a sa propre identité :
asset_01k... database identity
generated/job_.../....mp4 private storage key
https://media.example.com/a/... controlled delivery URLGardez le compartiment privé et distribuez via votre propre CDN, un endpoint de médias authentifié ou vos propres liens de courte durée. Ne réutilisez pas une URL signée SpicyAPI comme URL publique. Ainsi, liens temporaires et détails du stockage restent hors du HTML, des journaux et des exports ; vous pouvez modifier les règles d’accès ou les domaines sans réécrire l’historique des tâches, et supprimer une ressource utilisateur en intervenant sur votre propre enregistrement. Gardez sa provenance privée : ID de tâche source, clé de ressource d’origine, type de contenu, taille, heure de copie et clé de destination.
Pannes et actions de récupération
| Panne | Ce que vous observez | Récupération correcte |
|---|---|---|
| Ticket expiré avant la fin de l’envoi, ou fichier non validé utilisé dans une tâche | Envoi ou validation refusé, ou création de tâche refusant la référence | Demandez un nouveau ticket et renvoyez le fichier ; un 409/429 dû à une validation occupée impose plutôt de réessayer la même validation |
URI spicy:// enregistré utilisé le lendemain | 400 à la création de tâche ou échec d’une nouvelle tentative | Renvoyez l’original ; gardez les originaux dans votre stockage |
| Callback livré deux fois | Deux processus commencent la même copie | Dédupliquez sur request_id, et sur ID de tâche plus index de ressource dans la file et à destination |
| Endpoint de callback lent | Des reprises arrivent pendant un téléchargement | Enregistrez, mettez en file et renvoyez 2xx avant de déplacer les médias |
| Lien du résultat expiré dans la file | La lecture de la source renvoie 403 | Relisez la tâche pour une nouvelle url et réessayez le transfert continu |
La ressource affiche pending: true | key et url sont vides | Attendez quelques secondes et interrogez à nouveau |
La ressource affiche unavailable: true, ou download-url renvoie 410 | Expirée, supprimée ou échec de stockage | Utilisez votre copie privée vérifiée ; sinon, informez l’utilisateur et générez à nouveau avec une nouvelle tâche facturée |
| Copie terminée, mais échec d’écriture dans la base | Objet privé présent, aucune ligne de ressource prête | Vérifiez la destination existante et réessayez l’écriture des métadonnées ; ne supprimez jamais une copie valide et ne régénérez pas |
| La base indique prêt, mais l’objet manque | Page cassée ou 404 du CDN | Rapprochez les lignes de la base et le compartiment avant de marquer prêt |
| Une URL signée apparaît dans les journaux | Une capacité de lecture temporaire est exposée | Masquez les chaînes de requête et les corps de réponse ; les liens expirent en environ 20 minutes mais ne sont pas révocables |
| Grosse vidéo mise en mémoire tampon | Le processus redémarre ou manque de mémoire | Transférez en continu avec une concurrence limitée et une gestion de la contre-pression |
Liste de contrôle avant la mise en production
- Récupérer avant expiration : alertez sur les copies manquantes selon l’échéance acceptée de chaque tâche.
- Clarifier la propriété : ne servez les ressources privées qu’à leur propriétaire et récupérez les résultats avant de renouveler les clés API.
- Supprimer dans tous vos systèmes : incluez compartiment, cache de livraison et sauvegardes dans la politique de suppression.
Questions fréquentes
Puis-je enregistrer l’URL renvoyée dans output.assets[].url ?
Uniquement pour la copie elle-même. Enregistrez votre propre ressource et obtenez un nouveau lien quand vous devez déplacer des octets.
Ai-je encore besoin de download-url ?
Seulement pour actualiser un lien par taskId et key pendant la fenêtre de conservation ; les ressources prêtes contiennent déjà url et expiresAt.
Actualiser un lien prolonge-t-il la conservation des médias ?
Non. Un nouveau lien renouvelle seulement l’accès en lecture ; l’objet est toujours supprimé à retention.outputsExpireAt.
Puis-je fournir une URL publique d’image à un modèle au lieu de l’envoyer ?
Oui, si le schéma du modèle permet une URL dans ce champ, à condition que l’adresse renvoie le fichier lui-même et reste accessible pendant la tâche. Utilisez l’envoi pour les fichiers locaux, les entrées plus volumineuses ou ce qui n’est pas hébergé publiquement.
J’ai besoin de la même image de référence la semaine prochaine. Que faire ?
Renvoyez-la depuis votre propre copie lorsque vous en avez besoin la semaine prochaine. Une référence validée ne fonctionne que 1 jour, mais peut servir plusieurs tâches tant qu’elle reste valable, sauf suppression anticipée.
Dois-je copier le résultat dans la requête de callback ?
Non. Un gros transfert dans la requête dépasse le délai de callback de 15 secondes et déclenche des livraisons en double ; un processus en file peut réessayer sans risque.
Et si je ne veux pas conserver un résultat ?
Ne le copiez pas et laissez-le expirer, ou réduisez la conservation ou détruisez le contenu d’une tâche à la demande. Ces réglages sont expliqués dans Envoi, conservation et livraison des médias IA et dans la documentation de conservation. Détruire du contenu ne modifie jamais le montant facturé.
Laissez un Agent se charger de l’appel
Utilisez un Agent pour récupérer une sortie existante plutôt que la recréer.
- CLI :
npx @spicyapi/cli tasks get "$TASK_ID"récupère la tâche. Voir le guide CLI. - MCP : demandez la sortie actuelle à
spicyapi_task_getet enregistrez le fichier renvoyé. Voir le guide MCP. - Skill : installez
npx skills add spicyapi-ai/spicy-skill, puis demandez à$spicyapid’archiver le résultat de cette tâche dans le répertoire choisi. Voir le guide Skill.







