Auch eine erfolgreiche Generierung kann in Ihrem Produkt zu einem defekten Bild führen, wenn Sie die temporäre Auslieferungs-URL speichern, statt die Datei zu kopieren. Signierte Links laufen nach Minuten ab; die zugrunde liegende API-Ausgabe bleibt etwa zwei Wochen gespeichert.
Eine Medien-API liefert dieselbe Datei unter mehreren Identitäten: als Eingabereferenz, als Schlüssel der generierten Ausgabe und als signierte URL, über die sich die Bytes einige Minuten lang abrufen lassen. Für jede gilt eine eigene Ablaufzeit, und keine ist eine dauerhafte Adresse. Dieser Leitfaden nennt die genauen Zeitfenster bei SpicyAPI und zeigt, wie Sie wertvolle Ergebnisse in Ihren eigenen Speicher kopieren. Die Aufbewahrungseinstellungen können diese Zeitfenster verkürzen.
Zuerst im Playground ausprobieren
Öffnen Sie die Z-Image-Seite für Text-zu-Bild und melden Sie sich an:
- Geben Sie eine kurze Szene in Prompt ein und wählen Sie ein Aspect ratio.
- Klicken Sie auf Run model. Das Ergebnis erscheint unter Output.
- Laden Sie das Bild herunter und speichern Sie es an einem Ort, den Sie kontrollieren.
- Öffnen Sie die gespeicherte Datei später erneut. Sie lässt sich ohne die Seite oder deren temporären Ergebnislink öffnen. Genau das automatisiert der weitere Leitfaden für den Produktivbetrieb.
Das Wichtigste
- Bei Abschluss in einem Worker kopieren. Große Downloads gehören nicht in die Callback-Anfrage.
- Signierte Links privat halten. Wer einen besitzt, erhält vorübergehend Zugriff auf die Datei.
- Eigene Asset-ID und Auslieferungs-URL bereitstellen. Ein Task-Datensatz kann seine Medien überleben.
Was wann abläuft
| Was | Gültigkeitsdauer | Was am Ende geschieht |
|---|---|---|
Upload-Ticket (uploadUrl) | Etwa 20 Minuten; PUT und Commit müssen beide innerhalb dieses Zeitfensters abgeschlossen sein | Bei einer Antwort wegen eines abgelaufenen Tickets ist ein neues Ticket nötig; ein ausgelasteter Commit kann auch 409 zurückgeben |
Bestätigte Referenz (spicy://f/fil_...) | 1 Tag ab Commit, bis zum expiresAt aus der Commit-Antwort | URI wird für neue Tasks und Wiederholungen abgelehnt |
Ergebnislink (output.assets[].url) | Etwa 20 Minuten, nie über die Ablaufzeit der Medien hinaus | Speicher antwortet mit 403; lesen Sie den Task erneut, um einen neuen Link zu erhalten |
| Generiertes Bild, Video oder Audio | Etwa 14 Tage ab Abschluss (kürzer, wenn Sie es so einstellen) | Gelöscht; kein Link mehr ausstellbar (410 von download-url) |
| Prompt-Text und Ergebnisdateipfade im Task | 30 Tage | input und output werden geleert; der Task-Datensatz bleibt |
| Task-Datensatz (ID, Modell, Status, Kosten, Zeitstempel) | Unbefristet gespeichert | Weiter abfragbar, jedoch ohne Inhalt |
Kurzfilmmedien, die innerhalb eines Spicy Studio-Projekts entstehen, haben ein eigenes längeres Zeitfenster von etwa 60 Tagen; für Generierungen über API, SDK und Playground gilt die oben genannte Obergrenze von 14 Tagen. Die Aufbewahrung generierter Medien lässt sich auch pro Anfrage oder Konto verkürzen, wie im Artikel zu den Aufbewahrungseinstellungen beschrieben. Sie können diese Obergrenzen nur verkürzen, nie verlängern.
Die 14 Tage beginnen mit dem Abschluss, nicht mit der Einreichung. Vor dem Abschluss ist der Zeitstempel vorläufig und wird beim Abschluss neu gesetzt.
„Etwa“ ist bewusst gewählt. Die Speicherbereinigung erfolgt über eine Lebenszyklusregel und kann bis zu einen Tag hinter dem Stichtag liegen. Diese Verzögerung gibt Ihnen aber keine zusätzliche Zeit: Sobald das Zeitfenster schließt, wird kein Downloadlink mehr ausgestellt.
Eine Datei, vier Darstellungen
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 URLSDKs, CLI und Playground übernehmen die Upload-Schritte, sodass Sie sie selten selbst implementieren müssen. Jede Darstellung erfüllt eine Aufgabe. Das Ticket erlaubt innerhalb seiner Gültigkeit einen direkten Schreibzugriff; der Commit fixiert die akzeptierten Bytes. Die spicy://-URI erlaubt einem Task, einen Tag lang auf eine bestätigte, kontogebundene Eingabe zu verweisen. Der Ausgabe-key identifiziert ein generiertes Ergebnis. Die url erlaubt vorübergehend einen Lesezugriff. Ihre eigene Asset-ID ist die einzige langlebige Identität, die Ihr Produkt bereitstellen sollte.
Der Upload der Referenz zu SpicyAPI ist kostenlos; Sie bezahlen den Generierungs-Task, der sie verwendet. Das Aktualisieren eines Links zu einer vorhandenen Ausgabe verursacht keine weitere Generierungsgebühr. Ist die Ausgabe verloren und entscheiden Sie sich für eine neue Generierung, fallen für diesen neuen Task eigene Modellkosten an. Planen Sie Speicher, Anfragen und Datentransfer Ihres dauerhaften Buckets separat nach dem Tarif Ihres Speicheranbieters ein.
Eingaben: hochladen und den Ablauf nach 1 Tag einplanen
Bewahren Sie das ursprüngliche Referenzbild, -video oder -audio in Ihrem eigenen Speicher auf. Laden Sie eine lokale Eingabe über SDK, CLI oder Playground hoch und verwenden Sie ihre bestätigte URI erneut, solange sie gültig ist. Laden Sie sie nach Ablauf bei Bedarf erneut hoch; die temporäre URI ist kein Archiv.
Drei Möglichkeiten, einem Modell eine Eingabe zu übergeben:
- Öffentliche HTTPS-Adresse. Funktioniert dort, wo das Modellfeld eine URL akzeptiert, sofern diese die Datei selbst zurückgibt. Ein Cloud-Drive-, Album- oder Social-Media-Link liefert eine Webseite, und der Task schlägt mit
invalid_assetfehl; laden Sie stattdessen die Datei herunter und anschließend hoch. - Kleines eingebettetes Bild. JPEG, PNG, WebP oder GIF als base64-Daten-URI, jeweils bis zu 1 MiB.
- Upload. Für lokale Dateien. Bilder (JPEG, PNG, WebP, GIF) bis zu 10 MiB; Audio und Video in WAV, MP3, WebM, MP4 oder MOV bis zu 90 MiB und 600 Sekunden. Ein Modell kann niedrigere Grenzen haben; prüfen Sie daher das Feldlimit auf seiner Seite. Im TypeScript SDK gibt
await client.uploadFile("./reference.png")diespicy://-URI alsfile.urizurück.
Stellen Sie den SpicyAPI-Schlüssel niemals im Browser bereit; lassen Sie Ihren Server Upload-Tickets ausstellen. Die Mediendokumentation erläutert die Browserintegration und sämtliche Grenzen.
Ausgaben: So sieht ein fertiges Ergebnis aus
Ein abgeschlossener Task und eine abrufbare Datei sind unterschiedliche Zustände:
- Bereit: Kopieren Sie die Datei in Ihren Speicher und erfassen Sie Task-ID, Format und Größe.
- Ausstehend: Behalten Sie denselben Task und prüfen Sie erneut; kaufen Sie keine weitere Generierung, um die Datei zu erhalten.
- Nicht verfügbar: Die Plattformkopie ist verschwunden, und Warten bringt sie nicht zurück. Verwenden Sie Ihre gespeicherte Kopie; andernfalls generieren Sie erneut, was regulär berechnet wird. Nur ein anfänglicher Speicherfehler ohne ausgelieferte Dateien bleibt unberechnet.
Temporäre Links erlauben ihrem Besitzer Zugriff. Halten Sie sie aus öffentlichen Seiten und Analysesystemen heraus; holen Sie einen aktuellen Link, wenn der Kopier-Worker bereit ist.
Die Grenze der Aufbewahrung
Ein Task-Datensatz, Ausgabeschlüssel oder aktualisierter Downloadlink bewahrt die Bytes nicht auf. Kopieren Sie sie vor der Medien-Ablaufzeit.
Nach Ablauf des Zeitfensters meldet recordInfo den Wert contentState: "expired"; input und output werden spätestens nach 30 Tagen geleert. Wenn Sie den Task-Inhalt selbst vernichtet haben, lautet der Zustand purged. Der Task-Zustand expired bedeutet etwas anderes: Die Generierung hat ihr Zeitlimit überschritten und nichts erzeugt. Zeigen Sie beides in Ihrer Oberfläche nicht mit derselben Bezeichnung an, denn ein Fall erfordert eine Wiederholung, während der andere dadurch nicht wiederhergestellt werden kann.
Setzen Sie die Kopierfrist deutlich vor den Stichtag. Kopieren Sie beim ersten Erfolgsereignis und führen Sie zusätzlich einen Abgleichjob aus, der erfolgreiche Tasks ohne dauerhaftes Asset auflistet und Alarm schlägt, solange dem Ergebnis noch Tage bleiben. Tag 14 ist eine Reserve zur Wiederherstellung, kein Zeitplan. Verkürzen Konto- oder Anfrageeinstellungen die Aufbewahrung, kann outputsExpireAt deutlich früher als nach 14 Tagen liegen. Richten Sie Alarme daher nach diesem Feld aus, niemals nach einer Konstante. Eine Kontoeinstellung kann auch rückwirkend wirken: Eine Verkürzung der Frist für Prompt-Text löscht die Ergebnisse bereits abgeschlossener Tasks früher als ihr ursprünglich angegebenes outputsExpireAt. Laden Sie alles, was Sie behalten möchten, vor dem Ändern der Kontoeinstellungen herunter und rechnen Sie damit, dass der Abgleichjob solche Tasks als verloren meldet, statt als Pipelinefehler.
Eine Callback-Pipeline für Wiederholungen und große Videos
In einer Anwendung prüfen Sie das Abschlussereignis, speichern es dauerhaft und stellen den Kopierauftrag vor der Antwort in eine Warteschlange. Gestalten Sie das Kopieren idempotent, damit ein wiederholtes Ereignis keinen weiteren Galerieeintrag erzeugt. Warnen Sie vor unkopierten Ergebnissen, bevor ihre zugesagte Ablaufzeit erreicht ist.
Dieser eigenständige Worker speichert die Dateien eines vorhandenen Tasks in einem privaten lokalen Verzeichnis. Installieren Sie spicyapi, setzen Sie SPICY_API_KEY, TASK_ID und optional ARCHIVE_DIR, und führen Sie ihn aus. Er erzeugt keine Generierung. Legen Sie das Verzeichnis auf dauerhaftem Speicher ab und beziehen Sie es in Ihre Sicherungsplanung ein; eine kurzlebige Serverfestplatte ist kein dauerhaftes Archiv.
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)Für die Auslieferung aus einem Objektspeicher streamen Sie in ein privates Zwischenobjekt und geben es erst nach vollständiger Übertragung frei. Die Medien- und Webhook-Dokumentation erklären die Aktualisierung von Ergebnissen und authentifizierte Ereignisse. Behalten Sie Polling als Abgleichpfad für verpasste Callbacks bei.
Ihr dauerhaftes Asset sollte den Generierungsweg verbergen
Nach dem Kopieren hat das Objekt, das Ihr Produkt ausliefert, seine eigene Identität:
asset_01k... database identity
generated/job_.../....mp4 private storage key
https://media.example.com/a/... controlled delivery URLHalten Sie den Bucket privat und liefern Sie über Ihr eigenes CDN, einen authentifizierten Medienendpunkt oder eigene kurzlebige Links aus. Verwenden Sie eine signierte SpicyAPI-URL nicht als öffentliche URL. So bleiben temporäre Links und Speicherdetails aus HTML, Protokollen und Exporten heraus. Sie können Zugriffsregeln oder Domains ändern, ohne den Task-Verlauf neu zu schreiben, und ein Nutzer-Asset über Ihren eigenen Datensatz löschen. Bewahren Sie die Herkunft privat auf: ursprüngliche Task-ID, ursprünglicher Asset-Schlüssel, Inhaltstyp, Größe, Kopierzeitpunkt und Zielschlüssel.
Fehlerfälle und die richtige Wiederherstellung
| Fehlerfall | Beobachtung | Richtige Wiederherstellung |
|---|---|---|
| Upload-Ticket vor Abschluss abgelaufen oder unbestätigte Datei in einem Task verwendet | Upload oder Commit wird abgelehnt, oder die Task-Erstellung verweigert die Referenz | Neues Ticket anfordern und erneut hochladen; bei 409/429 wegen eines ausgelasteten Commits hingegen denselben Commit wiederholen |
Gespeicherte spicy://-URI am nächsten Tag verwendet | 400 bei der Task-Erstellung oder fehlgeschlagene Wiederholung | Original erneut hochladen; Originale im eigenen Speicher aufbewahren |
| Callback zweimal zugestellt | Zwei Worker starten denselben Kopiervorgang | Nach request_id sowie nach Task-ID plus Asset-Index in Warteschlange und Ziel deduplizieren |
| Callback-Endpunkt ist langsam | Wiederholungen treffen während eines Downloads ein | Speichern, einreihen und 2xx zurückgeben, bevor Medien übertragen werden |
| Ergebnislink läuft in der Warteschlange ab | Abruf der Quelle liefert 403 | Task erneut lesen, um eine neue url zu erhalten, und Streaming wiederholen |
Asset zeigt pending: true | key und url sind leer | Einige Sekunden warten und erneut abfragen |
Asset zeigt unavailable: true oder download-url liefert 410 | Abgelaufen, gelöscht oder Speicherung fehlgeschlagen | Verifizierte private Kopie verwenden; fehlt diese, Nutzer informieren und als neuen, kostenpflichtigen Task erneut generieren |
| Kopie fertig, aber Datenbankschreibvorgang fehlgeschlagen | Privates Objekt vorhanden, kein bereiter Asset-Datensatz | Vorhandenes Ziel verifizieren und Metadaten erneut schreiben; niemals eine gültige Kopie löschen oder neu generieren |
| Datenbank meldet bereit, aber Objekt fehlt | Defekte Seite oder 404 vom CDN | Datenbankzeilen mit dem Bucket abgleichen, bevor „bereit“ gesetzt wird |
| Signierte URL steht in Protokollen | Temporäre Leseberechtigung offengelegt | Abfragezeichenfolgen und Antwortinhalte schwärzen; Links laufen nach etwa 20 Minuten ab, können aber nicht widerrufen werden |
| Großes Video im Arbeitsspeicher gepuffert | Worker startet neu oder läuft aus dem Speicher | Mit begrenzter Parallelität und Rückstaukontrolle streamen |
Checkliste für den Produktionsstart
- Vor Ablauf wiederherstellen: Fehlende Kopien anhand der zugesagten Frist jedes Tasks überwachen.
- Eigentum eindeutig halten: Private Assets nur an ihren Eigentümer ausliefern und Ergebnisse vor dem Rotieren der API-Schlüssel sichern.
- Systemübergreifend löschen: Bucket, Auslieferungscache und Sicherungen in die Löschrichtlinie einbeziehen.
Häufige Fragen
Kann ich die in output.assets[].url zurückgegebene URL speichern?
Nur für den Kopiervorgang selbst. Speichern Sie Ihren eigenen Asset-Datensatz und holen Sie einen neuen Link, wenn Sie Bytes übertragen müssen.
Brauche ich weiterhin download-url?
Nur zum Aktualisieren eines Links anhand von taskId und key innerhalb des Aufbewahrungsfensters; bereite Assets enthalten bereits url und expiresAt.
Verlängert eine Linkaktualisierung die Aufbewahrungsdauer der Medien?
Nein. Ein neuer Link erneuert nur den Lesezugriff; das Objekt wird weiterhin zu retention.outputsExpireAt gelöscht.
Kann ich einem Modell statt eines Uploads eine öffentliche Bild-URL übergeben?
Ja, wenn das Modell-Schema in diesem Feld eine URL erlaubt, die Adresse die Datei selbst liefert und während der Task-Ausführung erreichbar bleibt. Nutzen Sie den Upload-Ablauf für lokale Dateien, größere Eingaben oder Inhalte ohne öffentliches Hosting.
Ich brauche nächste Woche dasselbe Referenzbild. Was soll ich tun?
Laden Sie es nächste Woche bei Bedarf erneut aus Ihrer eigenen Kopie hoch. Eine bestätigte Referenz gilt nur 1 Tag, kann aber während ihrer Gültigkeit mehrere Tasks bedienen, sofern sie nicht vorher gelöscht wird.
Soll ich das Ergebnis innerhalb der Callback-Anfrage kopieren?
Nein. Eine große Übertragung innerhalb der Anfrage überschreitet das Callback-Zeitlimit von 15 Sekunden und löst doppelte Zustellungen aus; ein Worker in der Warteschlange kann sicher wiederholen.
Was, wenn ich ein Ergebnis überhaupt nicht behalten möchte?
Dann kopieren Sie es nicht und lassen es ablaufen, oder verkürzen Sie die Aufbewahrung beziehungsweise vernichten den Task-Inhalt bei Bedarf. Diese Einstellungen werden in Uploads, Aufbewahrung und Auslieferung von KI-Medien und der Aufbewahrungsdokumentation beschrieben. Das Vernichten von Inhalten verändert niemals den berechneten Betrag.
Lassen Sie einen Agenten den Aufruf übernehmen
Verwenden Sie einen Agenten, um eine vorhandene Ausgabe wiederherzustellen, statt sie erneut zu erzeugen.
- CLI:
npx @spicyapi/cli tasks get "$TASK_ID"ruft den Task ab. Siehe CLI-Anleitung. - MCP: Fordern Sie über
spicyapi_task_getdie aktuelle Ausgabe an und speichern Sie die zurückgegebene Datei. Siehe MCP-Anleitung. - Skill: Installieren Sie
npx skills add spicyapi-ai/spicy-skillund bitten Sie anschließend$spicyapi, das Ergebnis dieses Tasks im gewählten Verzeichnis zu archivieren. Siehe Skill-Anleitung.







