MiniMax H3 API

model: minimax/h3-text-to-video

model: minimax/h3-image-to-video

model: minimax/h3-reference-to-video

Принимает текстовое описание и возвращает готовый ролик. Разрешение 768P, 2K, 6 соотношений сторон, от 21:9 до 9:16, описание до 7 000 символов. Модель MiniMax.

Принимает изображение и оживляет его, возвращая ролик. Разрешение 768P, 2K, описание до 7 000 символов. Модель MiniMax.

Принимает изображения-образцы и переносит показанное на них в новый ролик. Разрешение 768P, 2K, 7 соотношений сторон, от adaptive до 9:16, описание до 7 000 символов. Модель MiniMax.

Цена: за секунду, 768P — 10.4 кр. ($0.052), 2K — 16.9 кр. ($0.0845).

Цена: за секунду, 768P — 10.4 кр. ($0.052), 2K — 16.9 кр. ($0.0845).

Цена: за секунду, 768P — 10.4 кр. ($0.052), 2K — 16.9 кр. ($0.0845).

кр. — кредиты, покупаются за рубли.

Песочница

Input

Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 15 не бывает: duration до 15 с. Спишется фактический расход, лишнее вернётся на баланс.

Ролик собирается после входа: новому счёту начисляют 50 кредитов, и первый ролик идёт на них. Ставка у этой модели 10.4–16.9 кр. за секунду. Войдите, чтобы начать.

{
  "model": "minimax/h3-text-to-video",
  "input": {
    "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
    "duration": 4,
    "aspect_ratio": "21:9",
    "resolution": "768P"
  }
}
Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 15 не бывает: duration до 15 с. Спишется фактический расход, лишнее вернётся на баланс.
Output

Нажмите «Запустить» — готовый ролик появится здесь

Ролик делается минуты. Задача живёт не дольше 60 минут, после чего шлюз закрывает её сам и возвращает удержание целиком.

{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "cm8r4t0vk0001s60p2xq7f9ab",
    "model": "minimax/h3-text-to-video",
    "state": "success",
    "resultJson": "{\"resultUrls\":[\"https://file.example/result.mp4\"]}",
    "failCode": "",
    "failMsg": "",
    "creditsConsumed": 10.4
  }
}
Input

Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 15 не бывает: duration до 15 с. Спишется фактический расход, лишнее вернётся на баланс.

Ролик собирается после входа: новому счёту начисляют 50 кредитов, и первый ролик идёт на них. Ставка у этой модели 10.4–16.9 кр. за секунду. Войдите, чтобы начать.

{
  "model": "minimax/h3-image-to-video",
  "input": {
    "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
    "duration": 4,
    "resolution": "768P"
  }
}
Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 15 не бывает: duration до 15 с. Спишется фактический расход, лишнее вернётся на баланс.
Output

Нажмите «Запустить» — готовый ролик появится здесь

Ролик делается минуты. Задача живёт не дольше 60 минут, после чего шлюз закрывает её сам и возвращает удержание целиком.

{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "cm8r4t0vk0001s60p2xq7f9ab",
    "model": "minimax/h3-image-to-video",
    "state": "success",
    "resultJson": "{\"resultUrls\":[\"https://file.example/result.mp4\"]}",
    "failCode": "",
    "failMsg": "",
    "creditsConsumed": 10.4
  }
}
Input

Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 15 не бывает: duration до 15 с. Спишется фактический расход, лишнее вернётся на баланс.

Ролик собирается после входа: новому счёту начисляют 50 кредитов, и первый ролик идёт на них. Ставка у этой модели 10.4–16.9 кр. за секунду. Войдите, чтобы начать.

{
  "model": "minimax/h3-reference-to-video",
  "input": {
    "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
    "duration": 4,
    "aspect_ratio": "adaptive",
    "resolution": "768P"
  }
}
Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 15 не бывает: duration до 15 с. Спишется фактический расход, лишнее вернётся на баланс.
Output

Нажмите «Запустить» — готовый ролик появится здесь

Ролик делается минуты. Задача живёт не дольше 60 минут, после чего шлюз закрывает её сам и возвращает удержание целиком.

{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "cm8r4t0vk0001s60p2xq7f9ab",
    "model": "minimax/h3-reference-to-video",
    "state": "success",
    "resultJson": "{\"resultUrls\":[\"https://file.example/result.mp4\"]}",
    "failCode": "",
    "failMsg": "",
    "creditsConsumed": 10.4
  }
}

Цена MiniMax H3

Цена за секунду. Списывается фактический расход, о котором сообщил поставщик.

Вариант Наша цена Скидка Официальная
768P 10.4 кр. · $0.0525,2 ₽ −35% $0.08
2K 16.9 кр. · $0.08458,45 ₽ −35% $0.13

В среднем на 35% ниже официальной цены поставщика. кр. — кредиты, покупаются за рубли.

Цена за секунду. Списывается фактический расход, о котором сообщил поставщик.

Вариант Наша цена Скидка Официальная
768P 10.4 кр. · $0.0525,2 ₽ −35% $0.08
2K 16.9 кр. · $0.08458,45 ₽ −35% $0.13

В среднем на 35% ниже официальной цены поставщика. кр. — кредиты, покупаются за рубли.

Цена за секунду. Списывается фактический расход, о котором сообщил поставщик.

Вариант Наша цена Скидка Официальная
768P 10.4 кр. · $0.0525,2 ₽ −35% $0.08
2K 16.9 кр. · $0.08458,45 ₽ −35% $0.13

В среднем на 35% ниже официальной цены поставщика. кр. — кредиты, покупаются за рубли.

На время работы шлюз удерживает ставку выбранного разрешения, умноженную на запрошенную длительность; больше 254 кредита по этой модели он не удержит ни при каком запросе (duration до 15 с). Разница между удержанием и фактическим расходом возвращается на баланс тем же запросом состояния, который увидел итог.

Вызов через API

POST https://api.gen202.com/api/v1/jobs/createTask
GET https://api.gen202.com/api/v1/jobs/recordInfo?taskId=…

Ролик делается минуты, поэтому файл не приходит в ответ на запрос. Первый вызов ставит задачу и сразу отвечает её номером, второй по этому номеру отдаёт состояние задачи, а когда она готова — ссылку на результат. Ключ передаётся в обоих запросах заголовком Authorization: Bearer sk-… и выпускается в кабинете. На один ключ шлюз пропускает 30 постановок задачи и 300 запросов состояния в минуту.

Поля запроса

Значения проверяются до обращения к поставщику: поле не из списка модели или значение вне перечисления возвращают 422 и кредитов не тратят.

ПолеТипОбяз.Допустимые значения
model текст обязательное minimax/h3-text-to-video
input объект обязательное Параметры генерации — поля из таблицы ниже

Поля input

ПолеТипОбяз.Допустимые значения
prompt текст обязательное от 1 до 7 000 символов
duration целое число обязательное целое от 4 до 15
aspect_ratio значение из списка обязательное 21:9 · 16:9 · 4:3 · 1:1 · 3:4 · 9:16
resolution значение из списка опц. 768P · 2K

Ответ

Оба запроса отвечают одним конвертом: {"code":…,"msg":…,"data":…}, где code повторяет HTTP-статус, а полезное лежит в data. Тот же конверт приходит и при отказе — разбирать две формы ответа не нужно.

/api/v1/jobs/createTask

ПолеТипЧто в нём
data.taskId строка Номер задачи. Больше в ответе ничего нет и быть не может — работа только началась.

/api/v1/jobs/recordInfo

ПолеТипЧто в нём
data.taskId строка Номер задачи — тот же, что вернул createTask.
data.model строка Имя модели в том виде, в каком его прислал клиент.
data.state строка Состояние задачи: waiting, queuing, generating, success или fail.
data.param строка Параметры, с которыми задача создана, — строкой JSON внутри JSON.
data.resultJson строка Результат строкой JSON внутри JSON: {"resultUrls":["https://…"]} — разбирается вторым разбором. До завершения задачи — пустая строка.
data.failCode строка Код неудачи из таблицы ниже. У остальных задач — пустая строка.
data.failMsg строка Причина неудачи словами. Иначе пустая строка.
data.costTime число Сколько задача заняла, миллисекунды. Пока не завершилась — null.
data.completeTime число Когда завершилась, миллисекунды эпохи Unix. Пока не завершилась — null.
data.createTime число Когда создана, миллисекунды эпохи Unix.
data.updateTime число Когда состояние менялось в последний раз, миллисекунды эпохи Unix.
data.creditsConsumed число Сколько кредитов списано. Ноль, пока задача не завершилась, и ноль у неудачной.

Состояния задачи

Состояние лежит в поле state ответа /api/v1/jobs/recordInfo. Первые три означают, что работа идёт: запрос надо повторить примерно через 3 секунды — чаще спрашивать нечего, столько же ждёт между опросами сам шлюз. Два последних состояния окончательные: после них задача не меняется.

stateЧто происходит
waiting Задача принята и стоит в очереди шлюза; поставщику она ещё не отправлена.
queuing Поставщик задачу принял и поставил в свою очередь.
generating Генерация идёт.
success Готово: ссылки на результат лежат в resultJson, в creditsConsumed — сколько списано.
fail Задача не удалась: причина в failCode и failMsg, удержанные кредиты возвращены целиком.

Опрос не бывает бесконечным: задача на видео живёт не дольше 60 минут, после чего шлюз закрывает её сам состоянием fail и возвращает удержанные кредиты целиком. Причина неудачи приходит двумя полями: failCode из таблицы ниже и failMsg словами.

failCodeЧто произошло
501 Поставщик вернул отказ: генерация не удалась.
408 Результата нет дольше крайнего срока задачи (60 минут для видео).
404 Поставщик не знает такой задачи.
429 Поставщик отбил создание задачи по своему лимиту.
500 Поломка на нашей стороне; подробности остаются в журнале шлюза.

Ссылка на готовый файл живёт 14 дней — столько его хранит поставщик. Файл, который нужен дольше, скачивайте к себе сразу после того, как задача пришла в состояние success.

Отказы

Тело отказа одно на все случаи: {"code":…,"msg":…,"data":null}. Задача, не дошедшая до результата, не тарифицируется — удержанные кредиты возвращаются целиком.

КодАдресКогда
400 /api/v1/jobs/createTask Тело запроса — не разбираемый JSON.
401 /api/v1/jobs/createTask Ключа нет в заголовке Authorization, либо он неверный или отключён.
402 /api/v1/jobs/createTask Свободных кредитов меньше, чем удерживается под задачу.
413 /api/v1/jobs/createTask Тело запроса больше 512 КиБ.
415 /api/v1/jobs/createTask Тело отправлено не как application/json или заголовок Content-Type не передан.
422 /api/v1/jobs/createTask Поле model пустое или его имени нет в каталоге, input — не объект, поле не из списка модели либо значение вне её перечисления. В сообщении перечислено, что принимается.
429 /api/v1/jobs/createTask Больше 30 запросов в минуту на один ключ либо больше 50 незавершённых задач на аккаунте.
500 /api/v1/jobs/createTask Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.
401 /api/v1/jobs/recordInfo Ключа нет в заголовке Authorization, либо он неверный или отключён.
404 /api/v1/jobs/recordInfo Задачи с таким номером нет или она создана другим аккаунтом.
422 /api/v1/jobs/recordInfo Параметр taskId не передан.
429 /api/v1/jobs/recordInfo Больше 300 запросов в минуту на один ключ.
500 /api/v1/jobs/recordInfo Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.

Пример вызова

task.sh
# 1. Поставить задачу — в ответе придёт её номер
TASK=$(curl -s https://api.gen202.com/api/v1/jobs/createTask \
  -H "Authorization: Bearer sk-ваш-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax/h3-text-to-video",
    "input": {
      "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
      "duration": 4,
      "aspect_ratio": "21:9",
      "resolution": "768P"
    }
  }' | jq -r '.data.taskId')

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while true; do
  RECORD=$(curl -s "https://api.gen202.com/api/v1/jobs/recordInfo?taskId=$TASK" \
    -H "Authorization: Bearer sk-ваш-ключ")
  STATE=$(echo "$RECORD" | jq -r '.data.state')
  case "$STATE" in success|fail) break;; esac
  sleep 3
done

# 3. Забрать результат. Ссылка на файл лежит в resultJson — это строка JSON
#    внутри JSON, поэтому её разбирают вторым разбором (fromjson)
echo "$RECORD" | jq -r '.data |
  if .state == "fail"
  then "отказ \(.failCode): \(.failMsg)"
  else .resultJson | fromjson | .resultUrls[0]
  end'
task.py
import json
import time

import requests

headers = {"Authorization": "Bearer sk-ваш-ключ"}

# 1. Поставить задачу — в ответе придёт её номер
created = requests.post(
    "https://api.gen202.com/api/v1/jobs/createTask",
    headers=headers,
    json={
      "model": "minimax/h3-text-to-video",
      "input": {
        "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
        "duration": 4,
        "aspect_ratio": "21:9",
        "resolution": "768P"
      }
    },
).json()
task_id = created["data"]["taskId"]

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while True:
    task = requests.get(
        "https://api.gen202.com/api/v1/jobs/recordInfo",
        headers=headers,
        params={"taskId": task_id},
    ).json()["data"]
    if task["state"] in ("success", "fail"):
        break
    time.sleep(3)

# 3. Разобрать итог. У неудачи причина в failCode и failMsg
if task["state"] == "fail":
    raise SystemExit("отказ " + task["failCode"] + ": " + task["failMsg"])

# resultJson — строка JSON внутри JSON, поэтому разбор второй
result = json.loads(task["resultJson"])
print(result["resultUrls"][0])
print("списано кредитов:", task["creditsConsumed"])

Пример проходит весь путь: ставит задачу, повторяет запрос состояния раз в 3 секунды, пока задача не закончится, и разбирает ответ до ссылки на файл. Разборов два, потому что поле resultJson — строка JSON внутри JSON. В теле первого запроса стоят обязательные поля модели и значения из её перечислений, поэтому он проходит проверку шлюза как есть. Подставить остаётся только свой ключ. Пример на оболочке разбирает ответ через jq; на Python своего ничего не нужно, кроме requests.

POST https://api.gen202.com/api/v1/jobs/createTask
GET https://api.gen202.com/api/v1/jobs/recordInfo?taskId=…

Ролик делается минуты, поэтому файл не приходит в ответ на запрос. Первый вызов ставит задачу и сразу отвечает её номером, второй по этому номеру отдаёт состояние задачи, а когда она готова — ссылку на результат. Ключ передаётся в обоих запросах заголовком Authorization: Bearer sk-… и выпускается в кабинете. На один ключ шлюз пропускает 30 постановок задачи и 300 запросов состояния в минуту.

Поля запроса

Значения проверяются до обращения к поставщику: поле не из списка модели или значение вне перечисления возвращают 422 и кредитов не тратят.

ПолеТипОбяз.Допустимые значения
model текст обязательное minimax/h3-image-to-video
input объект обязательное Параметры генерации — поля из таблицы ниже

Поля input

ПолеТипОбяз.Допустимые значения
prompt текст обязательное от 1 до 7 000 символов
duration целое число обязательное целое от 4 до 15
first_frame_url ссылка опц. одна ссылка на изображение
last_frame_url ссылка опц. одна ссылка на изображение
resolution значение из списка опц. 768P · 2K

Ссылка на изображение принимается только полным адресом на http или https: не длиннее 2048 символов, на общедоступном сайте, без имени и пароля перед адресом и без порта, кроме 80 и 443. Относительный путь, а также адреса внутри сети — localhost, 127.0.0.1, 10.0.0.5, 192.168.1.2 — и имена без точки шлюз отклоняет ответом 422, к поставщику не обращаясь.

Ответ

Оба запроса отвечают одним конвертом: {"code":…,"msg":…,"data":…}, где code повторяет HTTP-статус, а полезное лежит в data. Тот же конверт приходит и при отказе — разбирать две формы ответа не нужно.

/api/v1/jobs/createTask

ПолеТипЧто в нём
data.taskId строка Номер задачи. Больше в ответе ничего нет и быть не может — работа только началась.

/api/v1/jobs/recordInfo

ПолеТипЧто в нём
data.taskId строка Номер задачи — тот же, что вернул createTask.
data.model строка Имя модели в том виде, в каком его прислал клиент.
data.state строка Состояние задачи: waiting, queuing, generating, success или fail.
data.param строка Параметры, с которыми задача создана, — строкой JSON внутри JSON.
data.resultJson строка Результат строкой JSON внутри JSON: {"resultUrls":["https://…"]} — разбирается вторым разбором. До завершения задачи — пустая строка.
data.failCode строка Код неудачи из таблицы ниже. У остальных задач — пустая строка.
data.failMsg строка Причина неудачи словами. Иначе пустая строка.
data.costTime число Сколько задача заняла, миллисекунды. Пока не завершилась — null.
data.completeTime число Когда завершилась, миллисекунды эпохи Unix. Пока не завершилась — null.
data.createTime число Когда создана, миллисекунды эпохи Unix.
data.updateTime число Когда состояние менялось в последний раз, миллисекунды эпохи Unix.
data.creditsConsumed число Сколько кредитов списано. Ноль, пока задача не завершилась, и ноль у неудачной.

Состояния задачи

Состояние лежит в поле state ответа /api/v1/jobs/recordInfo. Первые три означают, что работа идёт: запрос надо повторить примерно через 3 секунды — чаще спрашивать нечего, столько же ждёт между опросами сам шлюз. Два последних состояния окончательные: после них задача не меняется.

stateЧто происходит
waiting Задача принята и стоит в очереди шлюза; поставщику она ещё не отправлена.
queuing Поставщик задачу принял и поставил в свою очередь.
generating Генерация идёт.
success Готово: ссылки на результат лежат в resultJson, в creditsConsumed — сколько списано.
fail Задача не удалась: причина в failCode и failMsg, удержанные кредиты возвращены целиком.

Опрос не бывает бесконечным: задача на видео живёт не дольше 60 минут, после чего шлюз закрывает её сам состоянием fail и возвращает удержанные кредиты целиком. Причина неудачи приходит двумя полями: failCode из таблицы ниже и failMsg словами.

failCodeЧто произошло
501 Поставщик вернул отказ: генерация не удалась.
408 Результата нет дольше крайнего срока задачи (60 минут для видео).
404 Поставщик не знает такой задачи.
429 Поставщик отбил создание задачи по своему лимиту.
500 Поломка на нашей стороне; подробности остаются в журнале шлюза.

Ссылка на готовый файл живёт 14 дней — столько его хранит поставщик. Файл, который нужен дольше, скачивайте к себе сразу после того, как задача пришла в состояние success.

Отказы

Тело отказа одно на все случаи: {"code":…,"msg":…,"data":null}. Задача, не дошедшая до результата, не тарифицируется — удержанные кредиты возвращаются целиком.

КодАдресКогда
400 /api/v1/jobs/createTask Тело запроса — не разбираемый JSON.
401 /api/v1/jobs/createTask Ключа нет в заголовке Authorization, либо он неверный или отключён.
402 /api/v1/jobs/createTask Свободных кредитов меньше, чем удерживается под задачу.
413 /api/v1/jobs/createTask Тело запроса больше 512 КиБ.
415 /api/v1/jobs/createTask Тело отправлено не как application/json или заголовок Content-Type не передан.
422 /api/v1/jobs/createTask Поле model пустое или его имени нет в каталоге, input — не объект, поле не из списка модели либо значение вне её перечисления. В сообщении перечислено, что принимается.
429 /api/v1/jobs/createTask Больше 30 запросов в минуту на один ключ либо больше 50 незавершённых задач на аккаунте.
500 /api/v1/jobs/createTask Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.
401 /api/v1/jobs/recordInfo Ключа нет в заголовке Authorization, либо он неверный или отключён.
404 /api/v1/jobs/recordInfo Задачи с таким номером нет или она создана другим аккаунтом.
422 /api/v1/jobs/recordInfo Параметр taskId не передан.
429 /api/v1/jobs/recordInfo Больше 300 запросов в минуту на один ключ.
500 /api/v1/jobs/recordInfo Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.

Пример вызова

task.sh
# 1. Поставить задачу — в ответе придёт её номер
TASK=$(curl -s https://api.gen202.com/api/v1/jobs/createTask \
  -H "Authorization: Bearer sk-ваш-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax/h3-image-to-video",
    "input": {
      "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
      "duration": 4,
      "resolution": "768P"
    }
  }' | jq -r '.data.taskId')

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while true; do
  RECORD=$(curl -s "https://api.gen202.com/api/v1/jobs/recordInfo?taskId=$TASK" \
    -H "Authorization: Bearer sk-ваш-ключ")
  STATE=$(echo "$RECORD" | jq -r '.data.state')
  case "$STATE" in success|fail) break;; esac
  sleep 3
done

# 3. Забрать результат. Ссылка на файл лежит в resultJson — это строка JSON
#    внутри JSON, поэтому её разбирают вторым разбором (fromjson)
echo "$RECORD" | jq -r '.data |
  if .state == "fail"
  then "отказ \(.failCode): \(.failMsg)"
  else .resultJson | fromjson | .resultUrls[0]
  end'
task.py
import json
import time

import requests

headers = {"Authorization": "Bearer sk-ваш-ключ"}

# 1. Поставить задачу — в ответе придёт её номер
created = requests.post(
    "https://api.gen202.com/api/v1/jobs/createTask",
    headers=headers,
    json={
      "model": "minimax/h3-image-to-video",
      "input": {
        "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
        "duration": 4,
        "resolution": "768P"
      }
    },
).json()
task_id = created["data"]["taskId"]

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while True:
    task = requests.get(
        "https://api.gen202.com/api/v1/jobs/recordInfo",
        headers=headers,
        params={"taskId": task_id},
    ).json()["data"]
    if task["state"] in ("success", "fail"):
        break
    time.sleep(3)

# 3. Разобрать итог. У неудачи причина в failCode и failMsg
if task["state"] == "fail":
    raise SystemExit("отказ " + task["failCode"] + ": " + task["failMsg"])

# resultJson — строка JSON внутри JSON, поэтому разбор второй
result = json.loads(task["resultJson"])
print(result["resultUrls"][0])
print("списано кредитов:", task["creditsConsumed"])

Пример проходит весь путь: ставит задачу, повторяет запрос состояния раз в 3 секунды, пока задача не закончится, и разбирает ответ до ссылки на файл. Разборов два, потому что поле resultJson — строка JSON внутри JSON. В теле первого запроса стоят обязательные поля модели и значения из её перечислений, поэтому он проходит проверку шлюза как есть. Подставить остаётся только свой ключ. Пример на оболочке разбирает ответ через jq; на Python своего ничего не нужно, кроме requests.

POST https://api.gen202.com/api/v1/jobs/createTask
GET https://api.gen202.com/api/v1/jobs/recordInfo?taskId=…

Ролик делается минуты, поэтому файл не приходит в ответ на запрос. Первый вызов ставит задачу и сразу отвечает её номером, второй по этому номеру отдаёт состояние задачи, а когда она готова — ссылку на результат. Ключ передаётся в обоих запросах заголовком Authorization: Bearer sk-… и выпускается в кабинете. На один ключ шлюз пропускает 30 постановок задачи и 300 запросов состояния в минуту.

Поля запроса

Значения проверяются до обращения к поставщику: поле не из списка модели или значение вне перечисления возвращают 422 и кредитов не тратят.

ПолеТипОбяз.Допустимые значения
model текст обязательное minimax/h3-reference-to-video
input объект обязательное Параметры генерации — поля из таблицы ниже

Поля input

ПолеТипОбяз.Допустимые значения
prompt текст обязательное от 1 до 7 000 символов
duration целое число обязательное целое от 4 до 15
reference_image_urls список ссылок опц. до 9 ссылок на изображение
reference_video_urls список ссылок опц. до 3 ссылок на видео
reference_audio_urls список ссылок опц. до 3 ссылок на аудио
aspect_ratio значение из списка опц. adaptive · 21:9 · 16:9 · 4:3 · 1:1 · 3:4 · 9:16
resolution значение из списка опц. 768P · 2K

Ссылка на исходный файл принимается только полным адресом на http или https: не длиннее 2048 символов, на общедоступном сайте, без имени и пароля перед адресом и без порта, кроме 80 и 443. Относительный путь, а также адреса внутри сети — localhost, 127.0.0.1, 10.0.0.5, 192.168.1.2 — и имена без точки шлюз отклоняет ответом 422, к поставщику не обращаясь.

Ответ

Оба запроса отвечают одним конвертом: {"code":…,"msg":…,"data":…}, где code повторяет HTTP-статус, а полезное лежит в data. Тот же конверт приходит и при отказе — разбирать две формы ответа не нужно.

/api/v1/jobs/createTask

ПолеТипЧто в нём
data.taskId строка Номер задачи. Больше в ответе ничего нет и быть не может — работа только началась.

/api/v1/jobs/recordInfo

ПолеТипЧто в нём
data.taskId строка Номер задачи — тот же, что вернул createTask.
data.model строка Имя модели в том виде, в каком его прислал клиент.
data.state строка Состояние задачи: waiting, queuing, generating, success или fail.
data.param строка Параметры, с которыми задача создана, — строкой JSON внутри JSON.
data.resultJson строка Результат строкой JSON внутри JSON: {"resultUrls":["https://…"]} — разбирается вторым разбором. До завершения задачи — пустая строка.
data.failCode строка Код неудачи из таблицы ниже. У остальных задач — пустая строка.
data.failMsg строка Причина неудачи словами. Иначе пустая строка.
data.costTime число Сколько задача заняла, миллисекунды. Пока не завершилась — null.
data.completeTime число Когда завершилась, миллисекунды эпохи Unix. Пока не завершилась — null.
data.createTime число Когда создана, миллисекунды эпохи Unix.
data.updateTime число Когда состояние менялось в последний раз, миллисекунды эпохи Unix.
data.creditsConsumed число Сколько кредитов списано. Ноль, пока задача не завершилась, и ноль у неудачной.

Состояния задачи

Состояние лежит в поле state ответа /api/v1/jobs/recordInfo. Первые три означают, что работа идёт: запрос надо повторить примерно через 3 секунды — чаще спрашивать нечего, столько же ждёт между опросами сам шлюз. Два последних состояния окончательные: после них задача не меняется.

stateЧто происходит
waiting Задача принята и стоит в очереди шлюза; поставщику она ещё не отправлена.
queuing Поставщик задачу принял и поставил в свою очередь.
generating Генерация идёт.
success Готово: ссылки на результат лежат в resultJson, в creditsConsumed — сколько списано.
fail Задача не удалась: причина в failCode и failMsg, удержанные кредиты возвращены целиком.

Опрос не бывает бесконечным: задача на видео живёт не дольше 60 минут, после чего шлюз закрывает её сам состоянием fail и возвращает удержанные кредиты целиком. Причина неудачи приходит двумя полями: failCode из таблицы ниже и failMsg словами.

failCodeЧто произошло
501 Поставщик вернул отказ: генерация не удалась.
408 Результата нет дольше крайнего срока задачи (60 минут для видео).
404 Поставщик не знает такой задачи.
429 Поставщик отбил создание задачи по своему лимиту.
500 Поломка на нашей стороне; подробности остаются в журнале шлюза.

Ссылка на готовый файл живёт 14 дней — столько его хранит поставщик. Файл, который нужен дольше, скачивайте к себе сразу после того, как задача пришла в состояние success.

Отказы

Тело отказа одно на все случаи: {"code":…,"msg":…,"data":null}. Задача, не дошедшая до результата, не тарифицируется — удержанные кредиты возвращаются целиком.

КодАдресКогда
400 /api/v1/jobs/createTask Тело запроса — не разбираемый JSON.
401 /api/v1/jobs/createTask Ключа нет в заголовке Authorization, либо он неверный или отключён.
402 /api/v1/jobs/createTask Свободных кредитов меньше, чем удерживается под задачу.
413 /api/v1/jobs/createTask Тело запроса больше 512 КиБ.
415 /api/v1/jobs/createTask Тело отправлено не как application/json или заголовок Content-Type не передан.
422 /api/v1/jobs/createTask Поле model пустое или его имени нет в каталоге, input — не объект, поле не из списка модели либо значение вне её перечисления. В сообщении перечислено, что принимается.
429 /api/v1/jobs/createTask Больше 30 запросов в минуту на один ключ либо больше 50 незавершённых задач на аккаунте.
500 /api/v1/jobs/createTask Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.
401 /api/v1/jobs/recordInfo Ключа нет в заголовке Authorization, либо он неверный или отключён.
404 /api/v1/jobs/recordInfo Задачи с таким номером нет или она создана другим аккаунтом.
422 /api/v1/jobs/recordInfo Параметр taskId не передан.
429 /api/v1/jobs/recordInfo Больше 300 запросов в минуту на один ключ.
500 /api/v1/jobs/recordInfo Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.

Пример вызова

task.sh
# 1. Поставить задачу — в ответе придёт её номер
TASK=$(curl -s https://api.gen202.com/api/v1/jobs/createTask \
  -H "Authorization: Bearer sk-ваш-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax/h3-reference-to-video",
    "input": {
      "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
      "duration": 4,
      "aspect_ratio": "adaptive",
      "resolution": "768P"
    }
  }' | jq -r '.data.taskId')

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while true; do
  RECORD=$(curl -s "https://api.gen202.com/api/v1/jobs/recordInfo?taskId=$TASK" \
    -H "Authorization: Bearer sk-ваш-ключ")
  STATE=$(echo "$RECORD" | jq -r '.data.state')
  case "$STATE" in success|fail) break;; esac
  sleep 3
done

# 3. Забрать результат. Ссылка на файл лежит в resultJson — это строка JSON
#    внутри JSON, поэтому её разбирают вторым разбором (fromjson)
echo "$RECORD" | jq -r '.data |
  if .state == "fail"
  then "отказ \(.failCode): \(.failMsg)"
  else .resultJson | fromjson | .resultUrls[0]
  end'
task.py
import json
import time

import requests

headers = {"Authorization": "Bearer sk-ваш-ключ"}

# 1. Поставить задачу — в ответе придёт её номер
created = requests.post(
    "https://api.gen202.com/api/v1/jobs/createTask",
    headers=headers,
    json={
      "model": "minimax/h3-reference-to-video",
      "input": {
        "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
        "duration": 4,
        "aspect_ratio": "adaptive",
        "resolution": "768P"
      }
    },
).json()
task_id = created["data"]["taskId"]

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while True:
    task = requests.get(
        "https://api.gen202.com/api/v1/jobs/recordInfo",
        headers=headers,
        params={"taskId": task_id},
    ).json()["data"]
    if task["state"] in ("success", "fail"):
        break
    time.sleep(3)

# 3. Разобрать итог. У неудачи причина в failCode и failMsg
if task["state"] == "fail":
    raise SystemExit("отказ " + task["failCode"] + ": " + task["failMsg"])

# resultJson — строка JSON внутри JSON, поэтому разбор второй
result = json.loads(task["resultJson"])
print(result["resultUrls"][0])
print("списано кредитов:", task["creditsConsumed"])

Пример проходит весь путь: ставит задачу, повторяет запрос состояния раз в 3 секунды, пока задача не закончится, и разбирает ответ до ссылки на файл. Разборов два, потому что поле resultJson — строка JSON внутри JSON. В теле первого запроса стоят обязательные поля модели и значения из её перечислений, поэтому он проходит проверку шлюза как есть. Подставить остаётся только свой ключ. Пример на оболочке разбирает ответ через jq; на Python своего ничего не нужно, кроме requests.

Остальные модели этого семейства и их поля — в разделе документации Видео.

Что делает нейросеть MiniMax H3

MiniMax H3 — видеомодель MiniMax, у разработчика она называется ещё и Hailuo 03.

Записей 3: сцену задают описанием, присланным кадром или образцами — картинками, роликами и звуком.

Переключение между ними — другое имя в поле model, остальные поля запроса те же.

Сколько длится ролик MiniMax H3

Длительность задают полем duration: от 4 до 15 секунд.

Разрешений два — 768P и 2K, — и от выбора зависит ставка за секунду.

Что MiniMax H3 принимает на вход

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

Звуковой образец сам по себе не принимается: к нему нужна картинка или ролик.

Частые вопросы

Как вызвать MiniMax H3 через API из России?
Запрос уходит на api.gen202.com, и это адрес GEN202, а не адрес MiniMax: обращения за границу в вызове MiniMax H3 нет, поэтому VPN не требуется. В поле model уходит идентификатор выбранного варианта: текст в видео (minimax/h3-text-to-video), картинка в видео (minimax/h3-image-to-video), по образцам (minimax/h3-reference-to-video). Порядок вызова MiniMax H3 с примерами кода разобран в документации.
Сколько стоит MiniMax H3
Цена за секунду MiniMax H3 — от 10.4 кр. до 16.9 кр. У MiniMax H3 списывается фактический расход, а задача, завершившаяся ошибкой, не тарифицируется вовсе.
Нужны ли VPN и иностранная карта для MiniMax H3
Ни то, ни другое: обращение к MiniMax H3 идёт на наш адрес, а расчёты с MiniMax ведём мы, поэтому договариваться с ним клиенту не о чем. Оплата рублями с карты российского банка.
Что MiniMax H3 принимает на вход
Обязательные поля запроса к MiniMax H3: Текст в видео — prompt — от 1 до 7 000 символов; duration — целое от 4 до 15; aspect_ratio — 21:9 · 16:9 · 4:3 · 1:1 · 3:4 · 9:16. Картинка в видео — prompt — от 1 до 7 000 символов; duration — целое от 4 до 15. По образцам — prompt — от 1 до 7 000 символов; duration — целое от 4 до 15. Остальные поля необязательны.
Как подключить MiniMax H3 API и сделать первый вызов
Войти на сайт и выпустить ключ в кабинете: при первом входе начисляется 50 кредитов, и этот же ключ открывает MiniMax H3 вместе с остальным каталогом. Собственного ключа MiniMax и договора с ним для вызова MiniMax H3 не нужно.