Pular para o conteúdo principal

Sua API de IA retornou uma chave de recurso. Agora preserve a mídia

Resultados ficam cerca de 14 dias, referências por 1 dia. Veja os prazos e copie os arquivos para seu armazenamento a partir de um callback.

Autor
SpicyAPI Engineering
Publicado
Leitura
13 min de leitura
Atualizado
Uma mão desliza um cartão de foto luminoso de uma base temporária com ampulheta para uma baia de disco ao lado de um monitor de edição

Uma geração bem-sucedida ainda pode virar uma imagem quebrada no seu produto se você salvar a URL temporária de entrega em vez de copiar o arquivo. Links assinados expiram em minutos; a saída da API é mantida por cerca de duas semanas.

Uma API de mídia entrega o mesmo arquivo sob várias identidades: uma referência de entrada, uma chave da saída gerada e uma URL assinada que permite buscar os bytes por alguns minutos. Cada uma tem seu próprio prazo de validade, e nenhuma é um endereço permanente. Este guia mostra as janelas exatas da SpicyAPI e como copiar resultados valiosos para um armazenamento que você controla. Os controles de retenção podem encurtar essas janelas.

Experimente primeiro no Playground

Abra a página de Z-Image de texto para imagem e entre na sua conta:

  1. Digite uma cena curta em Prompt e escolha um Aspect ratio.
  2. Pressione Run model. O resultado aparece em Output.
  3. Baixe a imagem e salve em um lugar que você controla.
  4. Abra o arquivo salvo novamente mais tarde. Ele abre sem a página nem o link temporário do resultado; é isso que o restante do guia automatiza para produção.

Pontos principais

  • Copie na conclusão, em um processo de trabalho. Mantenha downloads grandes fora da requisição de callback.
  • Mantenha links assinados privados. Possuir um deles concede acesso temporário ao arquivo.
  • Exponha seu próprio ID de recurso e URL de entrega. Um registro de tarefa pode durar mais que sua mídia.

O que expira e quando

ItemPrazoO que ocorre no fim
Ticket de upload (uploadUrl)Cerca de 20 minutos; o PUT e a confirmação precisam terminar dentro desse prazoUma resposta de ticket expirado exige um novo ticket; uma confirmação ocupada também pode retornar 409
Referência confirmada (spicy://f/fil_...)1 dia desde a confirmação, até o expiresAt da resposta de confirmaçãoURI rejeitada em novas tarefas e novas tentativas
Link do resultado (output.assets[].url)Cerca de 20 minutos, nunca além do prazo da própria mídiaO armazenamento responde 403; consulte a tarefa novamente para obter um link novo
Imagem, vídeo ou áudio geradoCerca de 14 dias desde a conclusão (menos se você configurar assim)Excluído; nenhum link pode ser emitido (410 de download-url)
Texto do prompt e caminhos dos arquivos do resultado na tarefa30 diasinput e output são limpos; o registro da tarefa permanece
Registro da tarefa (ID, modelo, estado, custo, horários)Mantido indefinidamenteAinda pode ser consultado, sem conteúdo

A mídia de curtas-metragens criada dentro de um projeto Spicy Studio tem sua própria janela mais longa, de cerca de 60 dias; gerações pela API, SDK e Playground usam o limite de 14 dias acima. A retenção das gerações também pode ser reduzida por requisição ou conta, como explica o artigo sobre controles de retenção. Você só pode encurtar esses limites, nunca ampliá-los.

Os 14 dias contam da conclusão, não do envio. Antes de a tarefa terminar, o horário é provisório e é recalculado na conclusão.

“Cerca de” é intencional. A limpeza do armazenamento segue uma regra de ciclo de vida e pode atrasar até um dia após o prazo, mas esse atraso não dá tempo extra a você: assim que a janela fecha, nenhum link de download é emitido.

Um arquivo, quatro representações

text
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 URL

Os SDKs, a CLI e o Playground executam as etapas de upload para você, então raramente é necessário escrevê-las manualmente. Cada representação tem uma função. O ticket autoriza uma gravação direta durante sua validade; a confirmação fixa os bytes aceitos. A URI spicy:// permite que uma tarefa referencie uma entrada confirmada e restrita à conta por um dia. O key de saída identifica um resultado gerado. A url autoriza temporariamente uma leitura. Seu ID de recurso é a única identidade de longa duração que o produto deve expor.

Enviar a referência para a SpicyAPI é grátis; você paga pela tarefa de geração que a usa. Atualizar um link de uma saída existente não cria outra cobrança de geração. Se a saída for perdida e você decidir gerar novamente, essa nova tarefa terá sua própria cobrança do modelo. Reserve separadamente os custos de armazenamento, requisições e transferência do seu bucket permanente conforme o plano do fornecedor de armazenamento.

Entradas: envie e planeje a expiração de 1 dia

Mantenha a imagem, o vídeo ou o áudio de referência original no seu armazenamento. Envie uma entrada local pelo SDK, CLI ou Playground e reutilize sua URI confirmada enquanto for válida. Envie novamente quando precisar após a expiração; a URI temporária não é seu arquivo permanente.

Três maneiras de fornecer uma entrada a um modelo:

  • Endereço HTTPS público. Funciona quando o campo do modelo aceita uma URL, desde que ela retorne o próprio arquivo. Um link de drive na nuvem, álbum ou rede social retorna uma página web, e a tarefa falha com invalid_asset; baixe o arquivo e envie-o em vez disso.
  • Imagem pequena embutida. JPEG, PNG, WebP ou GIF como URI de dados base64, até 1 MiB cada.
  • Upload. Para arquivos locais. Imagens (JPEG, PNG, WebP, GIF) de até 10 MiB; áudio e vídeo WAV, MP3, WebM, MP4 ou MOV de até 90 MiB e 600 segundos. Um modelo pode aceitar menos, então confira o limite do campo na sua página. No TypeScript SDK, await client.uploadFile("./reference.png") retorna a URI spicy:// em file.uri.

Nunca exponha a chave da SpicyAPI em um navegador; faça seu servidor emitir os tickets de upload. A documentação de mídia explica a integração com navegadores e todos os limites.

Saídas: como é um resultado concluído

Uma tarefa concluída e um arquivo recuperável são estados diferentes:

  • Pronto: copie o arquivo para seu armazenamento e registre ID da tarefa, formato e tamanho.
  • Pendente: mantenha a mesma tarefa e consulte novamente; não compre outra geração para obter o arquivo.
  • Indisponível: a cópia da plataforma se foi, e esperar não vai recuperá-la. Use sua cópia salva; caso contrário, gere novamente, com cobrança normal. Apenas uma falha inicial de armazenamento sem arquivos entregues não é cobrada.

Links temporários dão acesso a quem os possui. Mantenha-os fora de páginas públicas e ferramentas de análise; obtenha um link atual quando o processo de cópia estiver pronto.

O limite da retenção

Um registro de tarefa, uma chave de saída ou um link de download atualizado não preserva os bytes. Copie-os antes do prazo da mídia.

Quando a janela fecha, recordInfo informa contentState: "expired", e input e output são limpos no máximo aos 30 dias. Se você destruiu o conteúdo da tarefa, o estado é purged. O estado de tarefa expired é diferente: significa que a geração excedeu o prazo e não produziu nada. Não mostre ambos com o mesmo rótulo na interface: um pede nova tentativa, enquanto o outro não pode ser recuperado tentando de novo.

Defina o prazo da cópia bem antes do limite. Copie no primeiro evento de sucesso e execute depois uma rotina de reconciliação que liste tarefas bem-sucedidas sem recurso permanente e alerte enquanto ainda restam dias ao resultado. O dia 14 é uma margem de recuperação, não um cronograma. Se uma configuração de conta ou requisição encurtar a retenção, outputsExpireAt pode chegar muito antes dos 14 dias; baseie os alertas nesse campo, nunca em uma constante. Uma configuração de conta também pode agir retroativamente: encurtar o prazo do texto do prompt exclui resultados de tarefas já concluídas antes do outputsExpireAt que receberam. Baixe o que deseja guardar antes de alterar as configurações da conta e espere que a reconciliação relate essas tarefas como perdidas, não como falhas do pipeline.

Um pipeline de callbacks que suporta novas tentativas e vídeos grandes

Em uma aplicação, verifique o evento de conclusão, persista-o e coloque a cópia na fila antes de responder. Faça a cópia ser idempotente para que um evento repetido não crie outro item na galeria. Alerte sobre resultados não copiados antes da expiração aceita.

Este processo autônomo salva os arquivos de uma tarefa existente em um diretório local privado. Instale spicyapi, defina SPICY_API_KEY, TASK_ID e, opcionalmente, ARCHIVE_DIR, e execute-o. Ele não cria nenhuma geração. Guarde o diretório em armazenamento persistente e inclua-o no plano de backup; um disco efêmero de servidor não é um arquivo permanente.

python
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)

Para entrega por armazenamento de objetos, transmita para um objeto intermediário privado e exponha-o só após a transferência completa. A documentação de mídia e de webhooks explica a atualização de resultados e eventos autenticados. Mantenha a sondagem como caminho de reconciliação para callbacks perdidos.

Seu recurso permanente deve ocultar o caminho da geração

Depois da cópia, o objeto servido pelo seu produto tem sua própria identidade:

text
asset_01k...                     database identity
generated/job_.../....mp4        private storage key
https://media.example.com/a/...  controlled delivery URL

Mantenha o bucket privado e entregue pelo seu CDN, por um endpoint de mídia autenticado ou por seus próprios links de curta duração. Não reutilize uma URL assinada da SpicyAPI como URL pública. Isso mantém links temporários e detalhes de armazenamento fora de HTML, logs e exportações, permite alterar regras de acesso ou domínios sem reescrever o histórico de tarefas e torna a exclusão de um recurso do usuário uma operação no seu próprio registro. Guarde a procedência de forma privada: ID da tarefa de origem, chave original do recurso, tipo de conteúdo, tamanho, horário da cópia e chave de destino.

Falhas e ações de recuperação

FalhaO que você observaRecuperação correta
Ticket expirado antes do fim do upload ou arquivo não confirmado usado na tarefaUpload ou confirmação rejeitados, ou criação de tarefa recusa a referênciaPeça um ticket novo e envie novamente; 409/429 por confirmação ocupada significa repetir a mesma confirmação
URI spicy:// salva usada no dia seguinte400 na criação da tarefa ou nova tentativa falhaEnvie o original novamente; mantenha os originais no seu armazenamento
Callback entregue duas vezesDois processos iniciam a mesma cópiaElimine duplicatas por request_id e por ID da tarefa mais índice do recurso na fila e no destino
Endpoint de callback lentoChegam novas tentativas durante um downloadPersista, enfileire e retorne 2xx antes de mover a mídia
Link do resultado expirado na filaBusca na origem retorna 403Consulte a tarefa novamente para uma nova url e repita a transmissão
Recurso mostra pending: truekey e url estão vaziosEspere alguns segundos e consulte novamente
Recurso mostra unavailable: true ou download-url retorna 410Expirou, foi excluído ou não pôde ser armazenadoUse sua cópia privada verificada; se não houver, informe o usuário e gere novamente como uma tarefa nova cobrada
Cópia terminou, mas gravação no banco falhouObjeto privado existe, sem registro de recurso prontoVerifique o destino existente e repita a gravação dos metadados; nunca exclua uma cópia válida nem gere de novo
Banco indica pronto, mas objeto está ausentePágina quebrada ou 404 do CDNReconcilie as linhas do banco com o bucket antes de marcar como pronto
URL assinada aparece nos logsUma capacidade temporária de leitura foi expostaOculte parâmetros de consulta e corpos de resposta; os links expiram em cerca de 20 minutos, mas não podem ser revogados
Vídeo grande armazenado na memóriaProcesso reinicia ou fica sem memóriaTransmita com concorrência limitada e controle de contrapressão

Lista para lançamento em produção

  • Recupere antes da expiração: alerte sobre cópias ausentes usando o prazo aceito de cada tarefa.
  • Deixe a propriedade explícita: entregue recursos privados apenas ao proprietário e recolha os resultados antes de rotacionar as chaves de API.
  • Exclua em todos os seus sistemas: inclua bucket, cache de entrega e backups na política de exclusão.

Perguntas frequentes

Posso salvar a URL retornada em output.assets[].url?

Só para a própria cópia. Salve seu próprio registro de recurso e obtenha um link novo quando precisar mover bytes.

Ainda preciso de download-url?

Só para atualizar um link por taskId e key dentro da janela de retenção; recursos prontos já trazem url e expiresAt.

Não. Um link novo só renova o acesso de leitura; o objeto ainda é excluído em retention.outputsExpireAt.

Posso passar uma URL pública de imagem para o modelo em vez de enviar o arquivo?

Sim, quando o esquema do modelo permite URL nesse campo, desde que o endereço retorne o próprio arquivo e continue acessível durante a tarefa. Use o upload para arquivos locais, entradas maiores ou conteúdo sem hospedagem pública.

Preciso da mesma imagem de referência na semana que vem. O que faço?

Envie novamente a partir da sua cópia quando precisar na semana que vem. Uma referência confirmada funciona apenas por 1 dia, mas pode atender várias tarefas enquanto válida, a menos que seja excluída antes.

Devo copiar o resultado dentro da requisição de callback?

Não. Uma transferência grande dentro da requisição ultrapassa o timeout de callback de 15 segundos e dispara entregas duplicadas; um processo na fila pode tentar novamente com segurança.

E se eu não quiser guardar um resultado?

Não o copie e deixe-o expirar, ou encurte a retenção ou destrua o conteúdo de uma tarefa sob demanda. Esses controles estão em Uploads, retenção e entrega de mídia de IA e na documentação de retenção. Destruir conteúdo nunca altera o que foi cobrado.

Deixe um Agent cuidar da chamada

Use um Agent para recuperar uma saída existente em vez de criá-la novamente.

  • CLI: npx @spicyapi/cli tasks get "$TASK_ID" recupera a tarefa. Veja o guia da CLI.
  • MCP: peça a spicyapi_task_get a saída atual e salve o arquivo retornado. Veja o guia MCP.
  • Skill: instale npx skills add spicyapi-ai/spicy-skill e peça a $spicyapi para arquivar o resultado da tarefa no diretório escolhido. Veja o guia de Skill.

Também em English · 日本語 · 한국어 · Deutsch · Français · Español · Русский · Italiano · Polski

Experimente você mesmo

Você paga só pelo que executar — sem plano e sem assinatura.

Feito com MiniMax H3 · ByteDance Seedance 2.0 Fast