Перейти к основному контенту

API изображений и видео с ИИ: от первого ввода до проверки результата

Создайте синюю кружку и анимируйте её в MiniMax H3: Playground, затем Python, CLI или агент, с промптами, ценами и проверкой результата.

Автор
Lydia
Опубликовано
Время чтения
10 мин чтения
Обновлено
Вымышленная взрослая керамистка в синем фартуке ставит синюю чашку рядом с камерой в гончарной мастерской

Создайте изображение, утвердите его, затем анимируйте в короткий клип. Эти шаги повторяют наш тест синей кружки в октябре 2026 года. В руководстве используются изображение Z-Image за 0,010 $ и пять секунд MiniMax H3 в 768p за 0,185 $: 0,195 $ за одну попытку на каждом этапе, с выключенным расширением промпта для изображения. Сначала попробуйте ту же сцену в Playground; когда входные данные будут работать, используйте пример Python или поручите вызовы агенту.

Желаемый результат конкретен: кружка правильной формы на верстаке, пар уходит вправо, камера медленно приближается и в конце останавливается. Разворачивать модель или настраивать сервер GPU не нужно.

Попробуйте весь пример в Playground

Создайте аккаунт и пополните баланс, затем подтвердите электронную почту. Playground обрабатывает запрос; ключ API нужен только для кода и инструментов агента ниже.

1. Создайте начальное изображение

Откройте Z-Image: текст в изображение и используйте Playground. Вставьте этот промпт:

Photograph of a handmade blue ceramic mug of tea on a worn wooden workbench in a pottery studio, a thin curl of steam rising from the mug. Soft window light from the left, shallow depth of field, shelves of pots softly out of focus behind. The mug sits left of centre with empty bench to its right. Natural colours, no text, no logos.

Выберите 16:9, PNG, seed 42 и выключите расширение промпта. Проверьте указанную цену и нажмите Run model. Базовое изображение стоит 0,010 $; расширение увеличит цену до 0,016 $. При сравнении изменений промпта оставляйте seed 42 неизменным.

Откройте результат, прежде чем продолжать. Проверьте:

  • Объект: одна кружка, одна ручка и правильная геометрия керамики.
  • Возможность движения: видимый пар и пространство справа для его смещения.
  • Композиция: кружка находится левее центра; полки остаются спокойным фоном.
  • Лишние детали: нет случайных надписей, логотипов или дополнительных чашек.

Скачайте утверждённый PNG как image.png. Если кружка неверна, измените одно предложение и создайте новое изображение, прежде чем тратить деньги на видео. Каждое новое базовое изображение добавляет 0,010 $.

2. Анимируйте утверждённое изображение

Откройте MiniMax H3: изображение в видео, добавьте image.png в Input image и вставьте в Prompt:

Steam rises from the mug and drifts to the right. The camera pushes in slowly toward the mug, then holds. The mug, bench and shelves stay where they are. Quiet room, a faint ceramic clink.

Задайте Duration 5 секунд и Resolution 768p; проверьте цену и нажмите Run model. Этот клип стоит 0,185 $; выбор 2k обойдётся в 0,290 $.

Промпт разделяет движение пара, движение камеры и звук. Он просит оставить кружку на месте, а не описывает её заново. Если кружка перемещается по верстаку, упростите указание для камеры вместо добавления новых действий объекта. Медленный наезд приближает камеру; это не просьба двигать кружку.

3. Проверьте клип и выберите следующий дубль

Воспроизведите весь результат со звуком. Проверьте, что кружка не скользит и не меняет форму, камера приближается и останавливается, нет нежелательной склейки, а звук подходит тихой мастерской. Ожидаемый результат — MP4 примерно на пять секунд со звуковой дорожкой; сама возможность воспроизведения файла ещё не делает кадр пригодным.

Если изображение верно, но движение нет, сохраните утверждённое начальное изображение и измените промпт движения. Если начальное изображение неверно, сначала замените его. У H3 нет seed: генерация в 2k даёт новый дубль, движения в котором могут отличаться от понравившейся версии 768p.

Что получилось в тесте синей кружки

Изображение стоило 0,010 $ и пришло как PNG 1280 × 720. Запрос пятисекундного видео стоил 0,125 $ (в 480p, который больше не предлагается; тот же клип в 768p стоит 0,185 $) и вернул MP4 832 × 480 со звуковой дорожкой. Итого — 0,135 $; в 768p было бы 0,195 $.

Кружка оставалась на верстаке, пар двигался вверх и вправо, а наезд камеры был едва заметным. На изображении кружка располагалась лишь немного левее центра, а ближе к концу пар терял чёткость возле полки. Для более крупного предметного плана точнее задайте положение кружки в промпте изображения до анимации.

Почему эти две модели подходят для первой попытки

Z-Image даёт недорогое изображение для выбора объекта и композиции. H3 принимает это изображение и генерирует движение и звуковую дорожку. Входные данные различаются:

Z-Image: текст в изображениеH3: изображение в видео
ID моделиalibaba/z-image-turbo/text-to-imageminimax/h3/image-to-video
Обязательный вводprompt (от 1 до 1 000 символов)prompt (до 7 000) и image_url
Используемые настройкиaspect_ratio (1:1, 4:3, 3:4, 16:9 или 9:16), output_format (только png), seed, enable_prompt_expansionduration_seconds (от 4 до 15), resolution (768p или 2k)
Не поддерживаетсялюбой другой формат выводаseed и aspect_ratio: форму задаёт начальное изображение
Возвращаетодин PNGодин MP4 со сгенерированной звуковой дорожкой
Цена0,010 $ за изображение0,037 $ за секунду в 768p

Используйте начальное изображение PNG, JPEG, WebP или GIF: каждая сторона от 256 до 5 760 пикселей, соотношение сторон от 2:5 до 5:2, размер файла менее 30 MB. Загруженный PNG — самый простой вариант. Публичная ссылка должна возвращать само изображение; страница общего доступа облачного диска не является изображением на входе.

Выполните тот же пример из Python

Используйте Python 3.11 или новее, установите официальный пакет через python -m pip install spicyapi и обеспечьте доступ к утилите file. Задайте SPICY_API_KEY в своём терминале, сохраните код как blue-mug.py и выполните python blue-mug.py. Он запрашивает согласие на обоих платных этапах и делает паузу для проверки изображения. При возобновлении сохраняйте созданную папку запуска; выбирайте новый RUN_NAME только для намеренно новой попытки. Контракт API и параметры SDK описаны в документации SDK Python.

python
import hashlib
import json
import os
from pathlib import Path
import subprocess
import tempfile
import time
import urllib.request
import uuid
from spicyapi import SpicyClient, output_assets

# Change RUN_NAME for a deliberately new pair of paid generations.
folder = Path(os.getenv("RUN_NAME", "blue-mug-1"))
folder.mkdir(exist_ok=True, mode=0o700)
state_file = folder / "state.json"
state = json.loads(state_file.read_text()) if state_file.exists() else {}
identity = hashlib.sha256(os.environ["SPICY_API_KEY"].encode()).hexdigest()
if state.get("identity", identity) != identity:
    raise RuntimeError("This saved run belongs to a different API key.")
state["identity"] = identity
client = SpicyClient()

def save() -> None:
    temp = state_file.with_suffix(".tmp")
    temp.write_text(json.dumps(state, indent=2))
    temp.chmod(0o600)
    temp.replace(state_file)

def run(stage: str, model: str, inputs: dict) -> dict:
    request = {"model": model, "input": inputs}
    if stage not in state:
        state[stage] = {"request": request, "key": str(uuid.uuid4())}
    saved = state[stage]
    if saved["request"] != request:
        raise RuntimeError("Input changed: choose a new RUN_NAME, not a new retry key.")
    save()  # The same request and key survive a lost create response.
    if not saved.get("taskId"):
        if "first_sent" in saved and time.time() - saved["first_sent"] >= 23 * 3600:
            raise RuntimeError("Unknown acceptance: reconcile task history before retrying.")
        if "quote" not in saved:
            quote = client.quote_task(model=model, input_data=inputs)
            if input(f"{stage}: quoted ${quote['estimatedCost']}. Create? [y/N] ") != "y":
                raise SystemExit("No task created for this stage.")
            saved["quote"] = quote
        saved.setdefault("first_sent", time.time())
        save()
        quote = saved["quote"]
        task = client.create_task(model=model, input_data=inputs,
                                  idempotency_key=saved["key"],
                                  quote_id=quote["quoteId"],
                                  expected_cost=quote["estimatedCost"])
        saved["taskId"] = task["taskId"]
        save()
    task_id = saved["taskId"]
    print(stage, "task:", task_id)
    deadline = time.monotonic() + 1200
    while time.monotonic() < deadline:
        task = client.get_task(task_id)
        if task["state"] in {"failed", "canceled", "expired"}:
            raise RuntimeError(f"{task_id}: {task.get('errorCode')} {task.get('errorMessage')}")
        if task["state"] == "succeeded":
            assets = output_assets(task)
            if not assets or assets[0].get("unavailable"):
                raise RuntimeError(f"{task_id}: no retrievable output; keep the task ID.")
            asset = assets[0]
            if not asset.get("pending") and asset.get("url"):
                print("Charge:", task.get("cost"), "settled:", task.get("settled"))
                return asset
        time.sleep(5)
    raise TimeoutError(f"Resume {task_id} by rerunning this file; do not start another task.")

def download(asset: dict, destination: Path, mime: str) -> None:
    temporary = None
    try:
        request = urllib.request.Request(asset["url"], headers={"User-Agent": "SpicyAPI-Blog-Example/1.0"})
        with urllib.request.urlopen(request, timeout=300) as response:
            if response.status != 200 or response.headers.get_content_type() != mime:
                raise RuntimeError("Unexpected download status or content type.")
            with tempfile.NamedTemporaryFile(dir=folder, delete=False) as handle:
                temporary = Path(handle.name)
                while chunk := response.read(1024 * 1024):
                    handle.write(chunk)
        size = temporary.stat().st_size
        expected = int(asset.get("bytes") or 0)
        detected = subprocess.check_output(["file", "-b", "--mime-type", str(temporary)], text=True).strip()
        if not size or (expected and size != expected) or detected != mime:
            raise RuntimeError("Incomplete or incorrectly formatted download.")
        temporary.replace(destination)  # An interrupted transfer leaves the old file alone.
    finally:
        if temporary is not None:
            temporary.unlink(missing_ok=True)

image_input = {
    "prompt": (
        'Photograph of a handmade blue ceramic mug of tea on a worn wooden workbench '
        'in a pottery studio, a thin curl of steam rising from the mug. Soft window '
        'light from the left, shallow depth of field, shelves of pots softly out of '
        'focus behind. The mug sits left of centre with empty bench to its right. '
        'Natural colours, no text, no logos.'
    ),
    "aspect_ratio": "16:9",
    "output_format": "png",
    "seed": 42,
    "enable_prompt_expansion": False,
}
image_asset = run("image", "alibaba/z-image-turbo/text-to-image", image_input)
download(image_asset, folder / "image.png", "image/png")
if input(f"Open {folder / 'image.png'}. Is the mug/frame approved? [y/N] ") != "y":
    raise SystemExit("Stopped before video generation.")
if not state.get("image_uri"):
    state["image_uri"] = client.upload_file(str(folder / "image.png"))["uri"]
    save()
video_input = {"image_url": state["image_uri"], "prompt": 'Steam rises from the mug and drifts to the right. The camera pushes in slowly toward the mug, then holds. The mug, bench and shelves stay where they are. Quiet room, a faint ceramic clink.',
               "duration_seconds": 5, "resolution": "768p"}
video_asset = run("video", "minimax/h3/image-to-video", video_input)
download(video_asset, folder / "video.mp4", "video/mp4")
print("Play", folder / "video.mp4", "with sound and review it before using it.")

Храните ключ API на своём сервере или контролируемом компьютере, никогда на публичной веб-странице или в мобильном приложении. Перед интеграцией другой модели проверяйте текущую доступность и допустимые входные данные на страницах моделей выше.

Поручите вызовы агенту, если так удобнее

Используйте Agent, если хотите поручить ему подготовку входных данных и получение результата, а цену и творческие решения утверждать самостоятельно. Лично проверьте начальное изображение до разрешения генерации видео.

  • CLI: сохраните тот же входной объект как input.json и используйте npx @spicyapi/cli tasks create --model alibaba/z-image-turbo/text-to-image --input-file input.json --wait. CLI подтверждает цену; сохраните возвращённый ID задачи для восстановления. Для клипа загрузите утверждённое изображение через npx @spicyapi/cli files upload image.png, затем создайте minimax/h3/image-to-video тем же способом, используя возвращённый URI spicy:// как image_url.
  • MCP: попросите помощника использовать spicyapi_task_create для alibaba/z-image-turbo/text-to-image, затем spicyapi_task_wait; для клипа добавьте spicyapi_upload_file для утверждённого image.png, затем spicyapi_task_create с minimax/h3/image-to-video, 5 секундами, 768p и промптом движения выше. Сервер MCP запрашивает подтверждение цены до создания.
  • Skill: установите через npx skills add spicyapi-ai/spicy-skill, затем попросите $spicyapi выполнить тот же процесс и остановиться для вашего согласия. Skill использует настроенное соединение CLI или MCP.

Планируйте бюджет на попытки, а не только финальный клип

Оценки приведены в долларах США. Перед генерацией подтвердите текущую цену.

ЗапросПримерная цена
Одно изображение Z-Image, PNG 16:90,010 $
То же изображение с расширением промпта0,016 $
H3, 4 s в 768p0,148 $
H3, 5 s в 768p (по умолчанию, если resolution не указан)0,185 $
H3, 5 s в 2k0,290 $
H3, 10 s в 768p0,370 $
H3, 15 s в 2k0,870 $
  • Длительность и разрешение: H3 стоит 0,037 $ или 0,058 $ за секунду в 768p или 2k.
  • Неподдерживаемые сочетания: клип на 4 секунды в 2k отклоняется с ошибкой 40004.
  • Два черновика: одно изображение и два пятисекундных видео 768p стоят 0,380 $.
  • Черновик и дубль для передачи: одно изображение, пятисекундная проверка в 768p и один пятисекундный дубль 2k стоят 0,010 $ + 0,185 $ + 0,290 $ = 0,485 $ с повторным использованием утверждённого изображения.

Деньги резервируются при принятии задачи и окончательно списываются по её завершении. Неудачные и просроченные задачи обычно освобождают резерв; некоторые отказы по содержимому оплачиваются согласно заявленной политике модели. При проверке списания смотрите фактический cost и статус settled задачи.

Исправляйте проблему на нужном этапе

СимптомЧто изменить
У кружки две ручки или случайный текстИсправьте промпт изображения и утвердите новое начальное изображение до генерации видео
Кружка движется, хотя вы хотели движение камерыСохраните изображение; запросите одно движение камеры и явно укажите, что кружка неподвижна
Видео хорошо в 768p, но новый дубль 2k отличаетсяСчитайте его новым кадром и проверьте движение до принятия
Ссылка на изображение не работаетЗагрузите файл или используйте прямой доступный URL изображения вместо страницы общего доступа
Аккаунт возвращает 401 или 40310Проверьте ключ или завершите подтверждение электронной почты
40301 / 40302 / 40303Проверьте разрешения модели, список разрешённых IP ключа или региональную доступность
40201 / 40202Проверьте доступный баланс или лимит расходов аккаунта/ключа; сохраняйте запрос при сверке неудачной отправки
Запрос отклоняет seed, aspect_ratio или output_formatПроверьте входные данные выбранной задачи; не копируйте поля Z-Image в H3
40004Измените несовместимое сочетание: 4 секунды в 2k
40901 или конфликт идемпотентностиПолучите новую оценку цены: если ID задачи не сохранён, удалите запись этого этапа в state.json и запустите снова. Если предыдущая попытка могла быть принята, проверьте список задач до создания чего-либо ещё; см. справочник ошибок
429 или ошибка соединенияСохраните папку запуска и ID задачи; не начинайте ещё одну платную попытку только ради восстановления результата
Задача сообщает content_rejected или timeoutПрочитайте ошибку и списание, затем сознательно выберите изменённый промпт, другое исходное изображение или более короткий кадр
Генерация успешна, но результат ещё не готовВозобновите ту же сохранённую задачу; не генерируйте другой клип ради скачивания

Сохраните первый рабочий процесс небольшим

Когда кадр удаётся, выберите длительность и разрешение для передачи, затем создайте и проверьте этот дубль. Необязательное конечное изображение может направлять завершение через last_image_url H3; оно не гарантирует точный последний кадр или движение между кадрами.

Для приложения сохраняйте оба ID задач и храните утверждённые файлы в собственном хранилище. Сравнение опроса и Webhooks поможет выбрать получение результатов, хранение медиа — сохранить файлы, а пакетная генерация пригодится для множества кадров. Первый рубеж тот же: утверждённое изображение и клип, который действительно следует задуманному кадру.

Другие языки English · 日本語 · 한국어 · Deutsch · Français · Español · Português (Brasil) · Italiano · Polski