본문으로 건너뛰기

AI API가 에셋 키를 반환했다면 미디어를 오래 보관하세요

생성 결과는 약 14일, 업로드한 참조 자료는 1일 보관됩니다. 만료 시점과 콜백에서 직접 관리하는 저장소로 복사하는 방법을 알아보세요.

작성자
SpicyAPI Engineering
발행
읽는 시간
약 13분
수정
모래시계가 있는 임시 보관 도크에서 편집 모니터 옆 드라이브 베이로 빛나는 사진 카드를 밀어 넣는 손

생성이 성공해도 파일을 복사하지 않고 임시 전달 URL만 저장하면 제품에서 이미지가 깨질 수 있습니다. 서명된 링크는 몇 분 만에 만료되지만, API 출력 파일 자체는 약 2주 동안 보관됩니다.

미디어 API는 같은 파일을 여러 식별 정보로 제공합니다. 입력 참조, 생성 출력 키, 몇 분 동안 파일의 바이트를 가져올 수 있는 서명된 URL입니다. 각각 별도의 만료 시점이 있으며, 어느 것도 영구 주소가 아닙니다. 이 가이드는 SpicyAPI의 정확한 보관 기간을 설명하고 중요한 결과를 직접 관리하는 저장소로 복사하는 방법을 보여줍니다. 보관 설정으로 이 기간을 단축할 수 있습니다.

먼저 Playground에서 사용해 보세요

Z-Image 텍스트→이미지 페이지를 열고 로그인하세요.

  1. Prompt에 짧은 장면을 입력하고 Aspect ratio를 선택합니다.
  2. Run model을 누릅니다. 결과는 Output에 표시됩니다.
  3. 이미지를 다운로드해 직접 관리하는 곳에 저장합니다.
  4. 나중에 저장한 파일을 다시 엽니다. 원래 페이지나 임시 결과 링크 없이도 열립니다. 이 가이드의 나머지 부분은 운영 환경에서 이 과정을 자동화합니다.

핵심 요점

  • 완료 시 워커에서 복사하세요. 큰 파일 다운로드는 콜백 요청 처리 밖에서 수행합니다.
  • 서명된 링크를 비공개로 유지하세요. 링크를 가진 사람은 파일에 임시로 접근할 수 있습니다.
  • 자체 에셋 ID와 전달 URL을 제공하세요. 작업 기록이 미디어보다 오래 남을 수 있습니다.

무엇이 언제 만료되나요?

대상유효 기간기간이 끝나면 일어나는 일
업로드 티켓 (uploadUrl)약 20분. PUT과 확정 처리가 모두 이 시간 안에 끝나야 합니다티켓 만료 응답을 받으면 새 티켓이 필요합니다. 확정 처리가 혼잡하면 409가 반환될 수도 있습니다
확정된 참조 (spicy://f/fil_...)확정 시점부터 1일, 확정 응답의 expiresAt까지새 작업과 재시도에서 URI가 거부됩니다
결과 링크 (output.assets[].url)약 20분이며, 미디어 자체의 기한을 넘을 수 없습니다저장소가 403을 반환합니다. 작업을 다시 조회해 새 링크를 받습니다
생성된 이미지, 영상 또는 오디오완료 시점부터 약 14일 (더 짧게 설정하면 그보다 짧음)삭제되며 링크를 발급할 수 없습니다 (410을 download-url에서 반환)
작업의 프롬프트 텍스트와 결과 파일 경로30일input과 output이 지워지고 작업 행은 남습니다
작업 기록 (ID, 모델, 상태, 비용, 타임스탬프)무기한 보관내용 없이 계속 조회할 수 있습니다

Spicy Studio 프로젝트 안에서 만든 단편 영상 미디어에는 약 60일의 별도 보관 기간이 적용됩니다. API, SDK, Playground의 생성에는 위의 14일 상한이 적용됩니다. 생성물 보관 기간은 요청별 또는 계정별로 줄일 수도 있습니다. 자세한 내용은 보관 설정 글를 참고하세요. 이 상한보다 짧게만 설정할 수 있으며 늘릴 수는 없습니다.

14일은 요청 제출이 아니라 완료 시점부터 계산합니다. 작업이 끝나기 전 타임스탬프는 임시 값이며 완료 시점에 다시 설정됩니다.

“약”이라고 한 것은 의도적입니다. 저장소 정리는 수명 주기 규칙으로 실행되며 기한보다 최대 하루 늦을 수 있습니다. 하지만 이 지연은 추가 사용 시간이 아닙니다. 보관 기간이 끝나면 다운로드 링크가 더 이상 발급되지 않습니다.

파일 하나, 네 가지 표현

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

SDK, CLI, Playground가 업로드 단계를 대신 실행하므로 직접 작성할 일은 드뭅니다. 각 표현에는 역할이 있습니다. 티켓은 유효 기간 안에 직접 쓰기를 허용하고, 확정 처리는 수락된 바이트를 고정합니다. spicy:// URI는 계정에 속한 확정 입력을 작업이 하루 동안 참조하도록 합니다. 출력 key는 생성 결과를 식별합니다. url은 읽기를 임시로 허용합니다. 제품이 제공해야 할 오래 유지되는 식별 정보는 자체 에셋 ID뿐입니다.

SpicyAPI에 참조 자료를 업로드하는 것은 무료이며, 이를 사용하는 생성 작업에 요금이 부과됩니다. 기존 출력의 링크를 갱신해도 추가 생성 요금은 발생하지 않습니다. 출력을 잃어버리고 다시 생성하기로 하면 새 작업에 별도의 모델 요금이 부과됩니다. 영구 보관 버킷의 저장, 요청, 전송 비용은 저장소 제공업체의 요금제에 따라 따로 예산을 잡으세요.

입력: 업로드하고 1일 만료에 대비하세요

원본 참조 이미지, 영상 또는 오디오는 자체 저장소에 보관하세요. SDK, CLI 또는 Playground로 로컬 입력을 업로드하고 확정된 URI가 유효한 동안 재사용합니다. 만료된 뒤 필요하면 다시 업로드하세요. 임시 URI는 보관용 아카이브가 아닙니다.

모델에 입력을 전달하는 방법은 세 가지입니다.

  • 공개 HTTPS 주소. 모델 필드가 URL을 허용하고 주소가 파일 자체를 반환할 때 사용할 수 있습니다. 클라우드 드라이브, 앨범 또는 소셜 링크는 웹 페이지를 반환하므로 작업이 invalid_asset으로 실패합니다. 대신 파일을 다운로드한 다음 업로드하세요.
  • 작은 인라인 이미지. JPEG, PNG, WebP 또는 GIF를 base64 데이터 URI로 전달하며 각각 최대 1 MiB입니다.
  • 업로드. 로컬 파일용입니다. 이미지 (JPEG, PNG, WebP, GIF)는 최대 10 MiB, WAV, MP3, WebM, MP4 또는 MOV 오디오와 영상은 최대 90 MiB 및 600초입니다. 모델이 더 작은 입력만 허용할 수 있으므로 해당 페이지에서 필드 제한을 확인하세요. TypeScript SDK에서 await client.uploadFile("./reference.png")는 spicy:// URI를 file.uri로 반환합니다.

브라우저에 SpicyAPI 키를 노출하지 마세요. 서버가 업로드 티켓을 발급하도록 하세요. 미디어 문서는 브라우저 통합과 전체 제한을 설명합니다.

출력: 완료된 결과의 상태

완료된 작업과 가져올 수 있는 파일은 서로 다른 상태입니다.

  • 준비 완료: 파일을 자체 저장소로 복사하고 작업 ID, 형식, 크기를 기록합니다.
  • 대기 중: 같은 작업을 유지하고 다시 확인합니다. 파일을 받으려고 다른 생성을 구매하지 마세요.
  • 사용 불가: 플랫폼의 복사본이 없어졌으며 기다려도 돌아오지 않습니다. 직접 저장한 복사본을 사용하세요. 없다면 다시 생성해야 하며 일반 요금이 부과됩니다. 파일을 하나도 전달하지 못한 최초 저장 실패에만 요금이 부과되지 않습니다.

임시 링크는 소유자에게 접근 권한을 줍니다. 공개 페이지와 분석 도구에 넣지 마세요. 복사 워커가 준비되었을 때 현재 유효한 링크를 받으세요.

보관 기간의 경계

작업 기록, 출력 키 또는 갱신된 다운로드 링크는 바이트를 보존하지 않습니다. 미디어 기한 전에 복사하세요.

보관 기간이 끝나면 recordInfo가 contentState: "expired"를 보고하며, input과 output은 늦어도 30일에 지워집니다. 작업 내용을 직접 파기했다면 상태는 purged입니다. 작업 상태 expired는 다른 의미입니다. 생성이 시간 초과되어 아무 결과도 만들지 못했다는 뜻입니다. UI에서 둘을 같은 이름으로 표시하지 마세요. 하나는 재시도가 필요하지만 다른 하나는 재시도로 복구할 수 없습니다.

복사 기한은 보관 기한보다 충분히 앞서 설정하세요. 첫 성공 이벤트에서 복사한 다음, 성공했지만 영구 에셋이 없는 작업을 나열하는 대조 작업을 실행하고 결과에 아직 며칠이 남았을 때 경고하세요. 14일째는 복구를 위한 여유이지 복사 일정이 아닙니다. 계정 또는 요청 설정으로 보관 기간을 줄이면 outputsExpireAt이 14일보다 훨씬 빨리 올 수 있습니다. 따라서 경고는 고정값이 아닌 이 필드를 기준으로 하세요. 계정 설정 하나는 과거 작업에도 영향을 줄 수 있습니다. 프롬프트 텍스트 보관 기간을 줄이면 이미 완료된 작업의 결과가 원래 주어진 outputsExpireAt보다 먼저 삭제됩니다. 계정 설정을 바꾸기 전에 보관할 결과를 다운로드하고, 대조 작업이 이런 작업을 파이프라인 오류가 아닌 유실로 보고할 수 있음을 예상하세요.

재시도와 큰 영상에 대응하는 콜백 파이프라인

애플리케이션에서는 완료 이벤트를 검증하고 영구 저장한 뒤, 복사를 큐에 넣고 응답하세요. 같은 이벤트가 반복돼도 갤러리에 항목을 중복 생성하지 않도록 복사를 멱등하게 만드세요. 복사되지 않은 결과는 수락된 만료 시점 전에 경고하세요.

이 독립 워커는 기존 작업의 파일을 비공개 로컬 디렉터리에 저장합니다. spicyapi를 설치하고 SPICY_API_KEY, TASK_ID, 필요에 따라 ARCHIVE_DIR을 설정한 뒤 실행하세요. 생성 작업을 만들지 않습니다. 해당 디렉터리를 영구 저장소에 두고 백업 계획에 포함하세요. 임시 서버 디스크는 영구 아카이브가 아닙니다.

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)

객체 저장소에서 전달하려면 비공개 임시 객체로 스트리밍하고 전송이 완전히 끝난 뒤에만 노출하세요. 미디어 문서와 Webhook 문서는 결과 갱신과 인증된 이벤트를 설명합니다. 누락된 콜백을 위한 대조 경로로 폴링을 유지하세요.

영구 에셋은 생성 경로를 숨겨야 합니다

복사 후 제품이 제공하는 객체에는 자체 식별 정보가 있습니다.

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

버킷을 비공개로 유지하고 자체 CDN, 인증된 미디어 엔드포인트 또는 자체 단기 링크로 전달하세요. SpicyAPI의 서명된 URL을 공개 URL로 재사용하지 마세요. 그러면 임시 링크와 저장소 세부 정보가 HTML, 로그, 내보내기에 포함되지 않고, 작업 이력을 다시 쓰지 않아도 접근 규칙이나 도메인을 바꿀 수 있으며, 사용자 에셋 삭제를 자체 기록에 대한 작업으로 처리할 수 있습니다. 출처 정보는 비공개로 보관하세요. 원본 작업 ID, 원래 에셋 키, 콘텐츠 유형, 크기, 복사 시각, 대상 키입니다.

장애와 복구 방법

장애관찰되는 상황올바른 복구
업로드가 끝나기 전에 티켓 만료 또는 확정하지 않은 파일을 작업에 사용업로드나 확정이 거부되거나 작업 생성 시 참조 거부새 티켓을 요청하고 다시 업로드합니다. 확정 처리 혼잡으로 인한 409/429는 같은 확정을 재시도합니다
저장한 spicy:// URI를 다음 날 사용작업 생성에서 400 또는 재시도 실패원본을 다시 업로드합니다. 원본은 자체 저장소에 보관합니다
콜백이 두 번 전달됨두 워커가 같은 복사를 시작request_id로 중복을 제거하고, 큐와 대상에서는 작업 ID와 에셋 인덱스 조합으로도 제거합니다
콜백 엔드포인트가 느림다운로드 중 재전송이 도착영구 저장하고 큐에 넣은 뒤 미디어를 옮기기 전에 2xx를 반환합니다
큐 대기 중 결과 링크 만료원본 가져오기가 403 반환작업을 다시 조회해 새 url을 받은 다음 스트리밍을 재시도합니다
에셋에 pending: true 표시key와 url이 비어 있음몇 초 기다린 뒤 다시 조회합니다
에셋에 unavailable: true 표시 또는 download-url에서 410 반환만료, 삭제 또는 저장 실패검증된 비공개 복사본을 사용합니다. 없다면 사용자에게 알리고 새 유료 작업으로 다시 생성합니다
복사는 완료됐으나 데이터베이스 쓰기 실패비공개 객체는 있지만 준비된 에셋 행은 없음기존 대상을 검증하고 메타데이터 쓰기를 재시도합니다. 유효한 복사본을 삭제하거나 다시 생성하지 마세요
데이터베이스에는 준비 완료지만 객체가 없음페이지가 깨지거나 CDN에서 404 반환준비 완료로 표시하기 전에 데이터베이스 행과 버킷을 대조합니다
서명된 URL이 로그에 표시임시 읽기 권한이 노출됨쿼리 문자열과 응답 본문을 가립니다. 링크는 약 20분 뒤 만료되지만 취소할 수는 없습니다
큰 영상을 메모리에 버퍼링워커 재시작 또는 메모리 부족동시 실행 수를 제한하고 역압을 적용해 스트리밍합니다

운영 출시 체크리스트

  • 만료 전에 복구하세요: 각 작업의 수락된 기한을 기준으로 누락된 복사본을 경고합니다.
  • 소유권을 명확히 하세요: 비공개 에셋은 소유자에게만 제공하고 API 키를 교체하기 전에 결과를 수집합니다.
  • 모든 시스템에서 삭제하세요: 버킷, 전달 캐시, 백업을 삭제 정책에 포함합니다.

자주 묻는 질문

output.assets[].url에서 반환된 URL을 저장해도 되나요?

복사 자체를 위해서만 사용하세요. 자체 에셋 기록을 저장하고 바이트를 옮길 때 새 링크를 받으세요.

download-url이 여전히 필요한가요?

보관 기간 안에 taskId와 key로 링크를 갱신할 때만 필요합니다. 준비된 에셋에는 이미 url과 expiresAt이 있습니다.

링크를 갱신하면 미디어 보관 기간이 늘어나나요?

아니요. 새 링크는 읽기 접근 권한만 갱신하며, 객체는 여전히 retention.outputsExpireAt에 삭제됩니다.

업로드 대신 공개 이미지 URL을 모델에 전달할 수 있나요?

네. 모델 스키마가 해당 필드에서 URL을 허용하고 주소가 파일 자체를 반환하며 작업 실행 중 계속 접근 가능할 때 사용할 수 있습니다. 로컬 파일, 더 큰 입력 또는 공개 호스팅되지 않은 자료에는 업로드 절차를 사용하세요.

다음 주에 같은 참조 이미지가 필요하면 어떻게 하나요?

다음 주에 필요할 때 자체 복사본에서 다시 업로드하세요. 확정된 참조는 1일 동안만 작동하지만, 먼저 삭제되지 않는 한 유효한 동안 여러 작업에서 사용할 수 있습니다.

콜백 요청 안에서 결과를 복사해야 하나요?

아니요. 요청 안의 큰 전송은 15초 콜백 제한 시간을 넘겨 중복 전달을 유발합니다. 큐의 워커는 안전하게 재시도할 수 있습니다.

결과를 전혀 보관하고 싶지 않다면요?

복사하지 않고 만료되게 두거나, 보관 기간을 줄이거나, 필요할 때 작업 내용을 파기하세요. 이 설정은 AI 미디어 업로드, 보관 및 전달과 보관 문서에 설명되어 있습니다. 내용을 파기해도 청구된 요금은 바뀌지 않습니다.

Agent에게 호출을 맡기세요

Agent로 기존 출력을 다시 생성하지 않고 복구하세요.

  • CLI: npx @spicyapi/cli tasks get "$TASK_ID"로 작업을 가져옵니다. CLI 가이드를 참고하세요.
  • MCP: spicyapi_task_get에 현재 출력을 요청하고 반환된 파일을 저장하세요. MCP 가이드를 참고하세요.
  • Skill: npx skills add spicyapi-ai/spicy-skill을 설치한 뒤 $spicyapi에 선택한 디렉터리로 해당 작업 결과를 보관하도록 요청하세요. Skill 가이드를 참고하세요.

다른 언어 English · 日本語 · Deutsch · Français · Español · Português (Brasil) · Русский · Italiano · Polski

직접 해 보기

실행한 만큼만 결제하세요. 요금제도, 구독도 없습니다.

사용 모델: MiniMax H3 · ByteDance Seedance 2.0 Fast