Una generazione riuscita può comunque diventare un’immagine non disponibile nel tuo prodotto se salvi l’URL temporaneo di consegna invece di copiare il file. I link firmati scadono in pochi minuti; il risultato dell’API viene conservato per circa due settimane.
Un’API multimediale ti consegna lo stesso file con diverse identità: un riferimento di input, una chiave del risultato generato e un URL firmato che consente di recuperare i byte per qualche minuto. Ognuno scade secondo il proprio termine e nessuno è un indirizzo permanente. Questa guida indica le finestre esatte di SpicyAPI, poi mostra come copiare i risultati utili in uno spazio di archiviazione che controlli. I controlli di conservazione possono abbreviare queste finestre.
Prova prima nel Playground
Apri la pagina Z-Image da testo a immagine e accedi:
- Scrivi una breve scena in Prompt e scegli un Aspect ratio.
- Premi Run model. Il risultato compare sotto Output.
- Scarica l’immagine e salvala in uno spazio che controlli.
- Riapri il file salvato in seguito. Si apre senza la pagina né il suo link temporaneo al risultato: il resto della guida automatizza proprio questo in produzione.
Punti chiave
- Copia al completamento, in un processo di lavoro. Tieni i download pesanti fuori dalla richiesta di callback.
- Mantieni privati i link firmati. Chi ne possiede uno ottiene accesso temporaneo al file.
- Esponi il tuo ID risorsa e il tuo URL di consegna. Un record di attività può sopravvivere ai suoi media.
Cosa scade e quando
| Elemento | Durata | Cosa succede alla scadenza |
|---|---|---|
Ticket di caricamento (uploadUrl) | Circa 20 minuti; PUT e conferma devono terminare entrambi entro questo intervallo | Una risposta di ticket scaduto richiede un nuovo ticket; una conferma occupata può anche restituire 409 |
Riferimento confermato (spicy://f/fil_...) | 1 giorno dalla conferma, fino a expiresAt nella relativa risposta | URI rifiutato per nuove attività e nuovi tentativi |
Link del risultato (output.assets[].url) | Circa 20 minuti, mai oltre la scadenza del media stesso | L’archiviazione risponde 403; rileggi l’attività per un link nuovo |
| Immagine, video o audio generato | Circa 14 giorni dal completamento (meno se lo imposti così) | Eliminato; nessun link può essere emesso (410 da download-url) |
| Testo del prompt e percorsi dei file del risultato nell’attività | 30 giorni | input e output vengono svuotati; il record dell’attività rimane |
| Record dell’attività (ID, modello, stato, costo, timestamp) | Conservato a tempo indeterminato | Ancora consultabile, senza contenuto |
I media dei cortometraggi creati in un progetto Spicy Studio hanno una propria finestra più lunga, di circa 60 giorni; le generazioni con API, SDK e Playground usano il limite di 14 giorni indicato sopra. Puoi anche ridurre la conservazione delle generazioni per richiesta o per account, come spiega l’articolo sui controlli di conservazione. Puoi solo abbreviare questi limiti, mai allungarli.
I 14 giorni decorrono dal completamento, non dall’invio. Prima che l’attività finisca, il timestamp è provvisorio e viene ricalcolato al completamento.
«Circa» è intenzionale. La pulizia dell’archiviazione segue una regola del ciclo di vita e può avvenire fino a un giorno dopo la scadenza, ma questo ritardo non ti dà tempo aggiuntivo: quando la finestra si chiude, non viene più emesso alcun link di download.
Un file, quattro rappresentazioni
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 URLSDK, CLI e Playground eseguono i passaggi di caricamento per te, quindi raramente devi scriverli a mano. Ogni rappresentazione ha una funzione. Il ticket autorizza una scrittura diretta entro la sua validità; la conferma fissa i byte accettati. L’URI spicy:// permette a un’attività di riferirsi per un giorno a un input confermato e limitato all’account. Il key di output identifica un risultato generato. L’url autorizza temporaneamente una lettura. Il tuo ID risorsa è l’unica identità duratura che il prodotto dovrebbe esporre.
Caricare il riferimento su SpicyAPI è gratuito; paghi l’attività di generazione che lo usa. Aggiornare un link a un output esistente non crea un altro addebito di generazione. Se il risultato è perso e scegli di generarlo di nuovo, la nuova attività avrà il proprio costo del modello. Prevedi separatamente archiviazione, richieste e trasferimenti del tuo bucket permanente secondo il piano del fornitore di archiviazione.
Input: carica e pianifica la scadenza di 1 giorno
Conserva l’immagine, il video o l’audio di riferimento originale nel tuo spazio di archiviazione. Carica un input locale tramite SDK, CLI o Playground e riutilizza l’URI confermato finché è valido. Ricaricalo quando ti serve dopo la scadenza; l’URI temporaneo non è il tuo archivio.
Tre modi per fornire un input a un modello:
- Indirizzo HTTPS pubblico. Funziona dove il campo del modello accetta un URL, purché restituisca il file stesso. Un link a un disco cloud, un album o un social restituisce una pagina web e l’attività fallisce con
invalid_asset; scarica il file e caricalo invece. - Piccola immagine incorporata. JPEG, PNG, WebP o GIF come URI di dati base64, fino a 1 MiB ciascuna.
- Caricamento. Per file locali. Immagini (JPEG, PNG, WebP, GIF) fino a 10 MiB; audio e video WAV, MP3, WebM, MP4 o MOV fino a 90 MiB e 600 secondi. Un modello può accettare meno, quindi controlla il limite del campo sulla sua pagina. Nel TypeScript SDK,
await client.uploadFile("./reference.png")restituisce l’URIspicy://comefile.uri.
Non esporre mai la chiave SpicyAPI nel browser; fai emettere i ticket di caricamento al tuo server. La documentazione dei media descrive l’integrazione nel browser e tutti i limiti.
Output: come si presenta un risultato completato
Un’attività completata e un file recuperabile sono stati diversi:
- Pronto: copia il file nel tuo spazio di archiviazione e registra ID dell’attività, formato e dimensione.
- In attesa: mantieni la stessa attività e controlla di nuovo; non acquistare un’altra generazione per ottenere il file.
- Non disponibile: la copia della piattaforma non c’è più e aspettare non la farà tornare. Usa la tua copia salvata; altrimenti genera di nuovo, con la normale fatturazione. Solo un errore iniziale di archiviazione senza file consegnati non viene addebitato.
I link temporanei danno accesso a chi li possiede. Tienili fuori da pagine pubbliche e sistemi di analisi; ottieni un link aggiornato quando il processo di copia è pronto.
Il limite della conservazione
Un record di attività, una chiave di output o un link di download aggiornato non conserva i byte. Copiali prima della scadenza del media.
Quando la finestra si chiude, recordInfo riporta contentState: "expired", e input e output vengono svuotati al più tardi dopo 30 giorni. Se hai distrutto tu il contenuto dell’attività, lo stato è purged. Lo stato dell’attività expired è diverso: significa che la generazione ha superato il tempo limite e non ha prodotto nulla. Non mostrarli entrambi con la stessa etichetta nell’interfaccia: uno richiede un nuovo tentativo, l’altro non si recupera riprovando.
Imposta la scadenza della copia ben prima del limite. Copia al primo evento di successo, poi esegui un processo di riconciliazione che elenchi le attività riuscite senza risorsa permanente e avvisi mentre ai risultati restano ancora giorni. Il giorno 14 è un margine di recupero, non una pianificazione. Se un’impostazione dell’account o della richiesta riduce la conservazione, outputsExpireAt può essere molto prima di 14 giorni: basa gli avvisi su quel campo, mai su una costante. Un’impostazione dell’account può anche agire retroattivamente: accorciare il periodo del testo del prompt elimina i risultati delle attività già concluse prima del outputsExpireAt assegnato. Scarica ciò che vuoi conservare prima di cambiare le impostazioni dell’account e aspettati che la riconciliazione segnali quelle attività come perse, anziché come errori della pipeline.
Una pipeline di callback che regge nuovi tentativi e video pesanti
In un’applicazione, verifica l’evento di completamento, salvalo e accoda la copia prima di rispondere. Rendi la copia idempotente, così un evento ripetuto non crea un altro elemento nella galleria. Segnala i risultati non copiati prima della loro scadenza accettata.
Questo processo autonomo salva i file di un’attività esistente in una directory locale privata. Installa spicyapi, imposta SPICY_API_KEY, TASK_ID e facoltativamente ARCHIVE_DIR, poi eseguilo. Non crea alcuna generazione. Memorizza la directory su uno spazio persistente e includila nel piano di backup; un disco temporaneo del server non è un archivio duraturo.
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)Per la consegna da uno spazio di archiviazione a oggetti, trasferisci in streaming verso un oggetto provvisorio privato ed esponilo solo dopo un trasferimento completo. La documentazione dei media e dei webhook descrive l’aggiornamento dei risultati e gli eventi autenticati. Mantieni il polling come percorso di riconciliazione per i callback mancati.
La tua risorsa permanente deve nascondere il percorso di generazione
Una volta copiato, l’oggetto servito dal tuo prodotto ha la propria identità:
asset_01k... database identity
generated/job_.../....mp4 private storage key
https://media.example.com/a/... controlled delivery URLMantieni privato il bucket e distribuisci tramite il tuo CDN, un endpoint multimediale autenticato o i tuoi link di breve durata. Non riutilizzare un URL firmato SpicyAPI come URL pubblico. Così link temporanei e dettagli di archiviazione non finiscono in HTML, log ed esportazioni, puoi cambiare regole di accesso o domini senza riscrivere la cronologia delle attività e cancellare una risorsa utente agendo sul tuo record. Conserva privatamente la provenienza: ID dell’attività sorgente, chiave originale della risorsa, tipo di contenuto, dimensione, ora della copia e chiave di destinazione.
Errori e azioni di recupero
| Errore | Cosa osservi | Recupero corretto |
|---|---|---|
| Ticket scaduto prima della fine del caricamento o file non confermato usato in un’attività | Caricamento o conferma rifiutati, oppure la creazione dell’attività rifiuta il riferimento | Richiedi un nuovo ticket e ricarica; un 409/429 per conferma occupata richiede invece di riprovare la stessa conferma |
URI spicy:// salvato usato il giorno seguente | 400 alla creazione o nuovo tentativo fallito | Ricarica l’originale; tieni gli originali nel tuo spazio di archiviazione |
| Callback consegnato due volte | Due processi avviano la stessa copia | Deduplica per request_id e per ID attività più indice risorsa nella coda e nella destinazione |
| Endpoint di callback lento | Arrivano nuovi tentativi durante un download | Salva, accoda e restituisci 2xx prima di spostare i media |
| Link al risultato scaduto in coda | Il recupero dalla sorgente restituisce 403 | Rileggi l’attività per una nuova url e riprova lo streaming |
La risorsa mostra pending: true | key e url sono vuoti | Aspetta qualche secondo e interroga di nuovo |
La risorsa mostra unavailable: true o download-url restituisce 410 | Scaduta, eliminata o archiviazione fallita | Usa la copia privata verificata; se non c’è, informa l’utente e genera di nuovo con una nuova attività addebitata |
| Copia completata ma scrittura nel database fallita | Oggetto privato presente, nessuna riga di risorsa pronta | Verifica la destinazione esistente e riprova la scrittura dei metadati; non cancellare mai una copia valida né rigenerare |
| Il database indica pronto ma l’oggetto manca | Pagina non funzionante o 404 dal CDN | Riconcilia le righe del database con il bucket prima di segnare come pronto |
| URL firmato nei log | Esposta una capacità temporanea di lettura | Oscura stringhe di query e corpi delle risposte; i link scadono in circa 20 minuti ma non sono revocabili |
| Video pesante caricato in memoria | Il processo si riavvia o esaurisce la memoria | Usa streaming con concorrenza limitata e gestione della contropressione |
Lista per il lancio in produzione
- Recupera prima della scadenza: segnala le copie mancanti usando la scadenza accettata di ogni attività.
- Rendi esplicita la proprietà: servi risorse private solo al proprietario e raccogli i risultati prima di ruotare le chiavi API.
- Elimina in tutti i tuoi sistemi: includi bucket, cache di consegna e backup nella politica di cancellazione.
Domande frequenti
Posso salvare l’URL restituito in output.assets[].url?
Solo per la copia stessa. Salva il tuo record della risorsa e ottieni un link nuovo quando devi spostare i byte.
Mi serve ancora download-url?
Solo per aggiornare un link tramite taskId e key nella finestra di conservazione; le risorse pronte hanno già url ed expiresAt.
Aggiornare un link prolunga la conservazione dei media?
No. Un link nuovo rinnova solo l’accesso in lettura; l’oggetto viene comunque eliminato a retention.outputsExpireAt.
Posso passare un URL pubblico di immagine al modello invece di caricarla?
Sì, se lo schema del modello consente un URL in quel campo, purché l’indirizzo restituisca il file stesso e rimanga raggiungibile durante l’attività. Usa il caricamento per file locali, input più grandi o contenuti non ospitati pubblicamente.
Mi serve la stessa immagine di riferimento la prossima settimana. Cosa faccio?
Ricaricala dalla tua copia quando ti serve la prossima settimana. Un riferimento confermato funziona solo per 1 giorno, ma può servire più attività mentre è valido, salvo eliminazione anticipata.
Devo copiare il risultato nella richiesta di callback?
No. Un trasferimento pesante nella richiesta supera il timeout del callback di 15 secondi e provoca consegne duplicate; un processo in coda può riprovare in sicurezza.
E se non voglio conservare affatto un risultato?
Non copiarlo e lascialo scadere, oppure riduci la conservazione o distruggi il contenuto di un’attività su richiesta. Questi controlli sono descritti in Caricamento, conservazione e consegna dei media AI e nella documentazione di conservazione. Distruggere il contenuto non cambia mai quanto ti è stato addebitato.
Lascia che un Agent gestisca la chiamata
Usa un Agent per recuperare un output esistente invece di crearlo di nuovo.
- CLI:
npx @spicyapi/cli tasks get "$TASK_ID"recupera l’attività. Vedi la guida CLI. - MCP: chiedi a
spicyapi_task_getl’output attuale e salva il file restituito. Vedi la guida MCP. - Skill: installa
npx skills add spicyapi-ai/spicy-skill, poi chiedi a$spicyapidi archiviare il risultato dell’attività nella directory scelta. Vedi la guida Skill.







