Практика API · обновлено 20 октября 2026 г. · 12 минут
Генерация видео API: cURL, JSON и jq от файла запроса до результата
Разберём API видеогенерации через обычные файлы и команды. Такой вариант удобно проверять вручную, прикладывать к клиентской инструкции и воспроизводить без собственного приложения. Главное — сохранить тело и ключ до отправки, чтобы сетевой повтор не стал второй покупкой.

Подготовьте отдельную папку для одного заказа
Откройте bash и создайте отдельную папку для примера. Здесь будут request.json, ответ оценки, заголовки создания и текущий статус. Одна папка соответствует одному самостоятельному заказу. Для нового содержания используйте новую папку и новый Idempotency-Key, а не переписывайте уже отправленные материалы.
Понадобятся cURL с поддержкой --fail-with-body и jq. Первый отправляет HTTP-запросы, второй проверяет и читает JSON. Ключ Celestria Gate задайте в окружении CELESTRIA_API_KEY скрытым вводом по базовой инструкции. В сам request.json секрет не включается. IDEMPOTENCY_KEY обозначает один заказ и должен оставаться постоянным при восстановлении.
Ниже команды рассчитаны на bash, включая процессы сравнения JSON в скрипте. В PowerShell синтаксис переносов и окружения отличается; для самостоятельной программы там удобнее взять Python или Node.js из quickstart. Не пытайтесь исправить непонятную команду массовым повтором платного POST.
- Файл JSON: Точное содержание заказа
- Схема и оценка: Проверка до платного запуска
- Один POST: Сохранённые тело и ключ
- GET и разбор ответа: ID, результат и billing
Сохраните request.json и проверьте типы полей
Учебная сцена — бумажный кораблик на спокойном пруду. Выбираем Kling 3 Pro, пять секунд, горизонтальный кадр и выключенный звук. Общий контракт Gate использует число duration: 5 и boolean generate_audio: false. Строка "false" имеет другой тип и не является правильной заменой логического значения.
Не добавляйте resolution только из желания увидеть 1080p. Для этого проверенного общего примера поле намеренно отсутствует: совместимость явного размера требует отдельной проверки. Текст сцены описывает одно движение и неподвижную камеру. Он не является гарантией, что все сгенерированные кадры идеально повторят ожидание.
Сохраните JSON обычным текстовым файлом в UTF-8, затем проверьте jq -e . request.json. Это проверяет синтаксис, а не соответствие схеме модели. Корректные кавычки и запятые ещё не доказывают наличие нужной операции или поддерживаемой длительности. Следующим шагом читается схема выбранного режима.
| Поле | Правильный тип в этом общем запросе |
|---|---|
| model | Строка с полным каноническим ID |
| input | Объект настроек |
| duration | Число 5 |
| generate_audio | Логическое false |
| operation | Строка text-to-video |
{
"model": "kwaivgi/kling-v3.0-pro",
"input": {
"operation": "text-to-video",
"prompt": "A single paper boat floats slowly across a calm pond. One continuous shot, a fixed camera, soft evening light. No text.",
"duration": 5,
"aspect_ratio": "16:9",
"generate_audio": false
}
}
Получите схему, не создавая генерацию
Команда ниже использует --get и --data-urlencode, чтобы корректно передать ID со слешем и название операции. Ответ сохраняется в schema.json. Это чтение описания полей, а не запуск модели. Если вернулась ошибка, сначала выясните причину: неправильный ID, операция или временная недоступность требуют разных действий.
В input_schema смотрите properties, required, перечисления и defaults. Модельные настройки находятся в input.parameters, а общие поля — прямо в input. duration передаётся числом для моделей, которые её поддерживают. При смене модели API остаётся тем же, но допустимые поля и их смысл проверяются заново. Не смешивайте настройки разных режимов только ради универсального JSON.
Файл схемы полезен как снимок условий при подготовке заказа, но не даёт бессрочной гарантии. Перед новой самостоятельной интеграцией и сменой модели получите актуальную версию. В истории заказа сохраняйте фактически отправленное тело, а не восстанавливайте его позднее из изменившихся значений формы.
curl --fail-with-body --silent --show-error --get \
"https://api.celestriagate.com/v1/media/models/schema" \
-H "Authorization: Bearer $CELESTRIA_API_KEY" \
--data-urlencode "model=kwaivgi/kling-v3.0-pro" \
--data-urlencode "operation=text-to-video" \
--output schema.json
jq '{model, input_schema}' schema.json
Оцените тот же файл и прочитайте причину маршрута
Отправьте request.json в /v1/media/estimate и сохраните estimate.json. Здесь используется POST, но он не создаёт платного видео. Платная операция в этой инструкции — POST /v1/media/generations. Различать их по последней части пути важнее, чем запоминать общий смысл слова POST.
Посмотрите provider, reason и estimate. Предварительная рублёвая сумма не является окончательным списанием; null не означает бесплатный запрос. reason помогает понять, сравнивались ли несколько подтверждённых совместимых цен, подошёл ли один маршрут или проверка цены неполная. Это выбор внутри подтверждённой модели, а не автоматическая замена её другим семейством.
Если оценка не подходит бюджету, измените заказ до отправки, заново проверьте его и повторите оценку. После платного создания не переписывайте исходное тело в той же папке. Один файл помогает сохранить связь между показанными условиями и фактическим запросом, но дисциплина именования и фиксации остаётся вашей ответственностью.
curl --fail-with-body --silent --show-error \
"https://api.celestriagate.com/v1/media/estimate" \
-H "Authorization: Bearer $CELESTRIA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @request.json --output estimate.json
jq '{provider, reason, compared, estimate}' estimate.json
Один сценарий создания и продолжения заказа
Сохраните следующий скрипт как run-order.sh. Он проверяет JSON, фиксирует тело и Idempotency-Key перед отправкой, затем сохраняет ответ 202. При следующем запуске с корректным created.json используется уже известный ID. Если ответ потерян, исходная пара ключа и тела остаётся для безопасного восстановления через Gate.
Этот скрипт создаёт реальную платную генерацию при первом запуске. Выполняйте его только после ручной проверки оценки выше. Вызов bash run-order.sh не задаёт цену жёстко: окончательное списание подтвердится после выполнения. Скрипт ограничивает локальное ожидание числом проверок; время самих сетевых вызовов добавляется к паузам.
JSON-сравнение сортирует ключи объектов и сохраняет смысл массивов. Изменение тела или ключа в папке уже отправленного заказа останавливает скрипт. Сбой GET тоже прекращает текущий запуск, но не уничтожает серверное задание. Для продолжения запустите прежний скрипт с прежними данными; новый ключ ради сетевой ошибки не нужен.
#!/usr/bin/env bash
set -euo pipefail
umask 077
: "${CELESTRIA_API_KEY:?Задайте ключ Gate в окружении}"
: "${IDEMPOTENCY_KEY:?Задайте постоянный ключ одного заказа}"
jq -e 'type == "object" and (.model | type == "string") and (.input | type == "object")' request.json >/dev/null
if [[ -f submitted-request.json ]]; then
if ! cmp -s <(jq -S -c . request.json) <(jq -S -c . submitted-request.json); then
echo "Тело изменено: используйте отдельную папку и новый ключ для нового заказа." >&2
exit 1
fi
else
cp request.json submitted-request.json
fi
if [[ -f submitted-key.txt ]]; then
saved_key=$(cat submitted-key.txt)
[[ "$saved_key" == "$IDEMPOTENCY_KEY" ]] || {
echo "Ключ изменён в папке существующего заказа." >&2
exit 1
}
else
printf '%s' "$IDEMPOTENCY_KEY" > submitted-key.txt
fi
if [[ -f created.json ]] && job_id=$(jq -er '.id | select(type == "string") | select(test("^media_generation_[A-Za-z0-9_-]+quot;))' created.json); then
echo "Продолжаем существующее задание: $job_id"
else
# Это единственное место, которое создаёт платное задание.
# После обрыва повтор использует то же тело и тот же ключ.
curl --fail-with-body --silent --show-error --max-time 40 \
'https://api.celestriagate.com/v1/media/generations' \
-H "Authorization: Bearer $CELESTRIA_API_KEY" \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
--data-binary @submitted-request.json \
--dump-header created.headers --output created.json
job_id=$(jq -er '.id | select(type == "string") | select(test("^media_generation_[A-Za-z0-9_-]+quot;))' created.json)
fi
for ((check=0; check<60; check++)); do
curl --fail-with-body --silent --show-error --max-time 40 \
"https://api.celestriagate.com/v1/media/generations/$job_id" \
-H "Authorization: Bearer $CELESTRIA_API_KEY" \
--output current.json
status=$(jq -er '.status' current.json)
echo "$job_id $status"
case "$status" in
completed)
jq '{id, model, provider, billing, files}' current.json
exit 0 ;;
failed|cancelled)
jq '{id, status, error, billing}' current.json
exit 1 ;;
queued|submitting|pending|in_progress|awaiting_billing)
sleep 5 ;;
*) echo "Неизвестный статус; сохраните ID и проверьте контракт." >&2; exit 1 ;;
esac
done
echo "Предел локального ожидания. Серверное задание не отменено: $job_id" >&2
exit 2
Как читать сохранённые ответы и завершение
created.headers хранит HTTP-заголовки, включая Location, а created.json — объект задания с ID. Ответ HTTP 202 означает принятие в асинхронную обработку. current.json обновляется последующими GET. В завершённом состоянии читайте files, result и billing конкретного задания, а не общую разницу кошелька.
Скрипт печатает файлы как данные, но не скачивает любой URL автоматически. content_type может отсутствовать или оказаться application/octet-stream. Идентификация видео требует MIME, расширения, имени файла и структуры result по схеме. Для сохранения MP4 с проверкой адреса и содержимого есть отдельная инструкция.
При failed или cancelled сохраняйте error и billing. При awaiting_billing продолжайте ждать подтверждения расхода. Если локальная граница ожидания достигнута, ID остаётся рабочим: это не cancel и не повод создавать другую генерацию. Повтор GET завершённого задания полезен для свежей подписанной ссылки.
Разберите неизвестный исход без догадок
Если cURL завершился с сетевой ошибкой после отправки, нельзя считать, что платный запрос не был принят. Не очищайте submitted-request.json и submitted-key.txt ради новой попытки. Сначала восстановите тот же POST с тем же ключом или используйте уже сохранённый ID. Серверное состояние важнее отсутствия красивого ответа в терминале.
409 означает конфликт ключа с другим телом, а не приглашение бесконечно менять ключи. Проверьте, какой заказ уже связан с ним. Для нового содержания действительно нужен новый самостоятельный заказ. Для восстановления старого нужны прежние параметры и исходники, включая подписанные ссылки, если они были частью тела.
Не включайте --retry-all-errors в платную отправку без понимания идемпотентности. В примере автоматического POST-повтора cURL нет; восстановление контролируется сохранённой парой. Ключ API тоже не стоит выводить диагностическими командами или включать в подробный сетевой лог, который потом отправится клиенту.
Как передать клиенту рабочий пример
Передавайте request.json без секрета, краткое описание роли каждого поля и порядок вызовов: схема, оценка, создание, получение по ID. Отдельно отметьте, какой шаг платный и как восстановиться после потери ответа. Учебные ID и нулевые значения нельзя выдавать за подтверждение выполненного заказа.
Для воспроизводимости полезно приложить дату проверки, точный model и operation, действующие ограничения и один реальный отчёт отдельного теста, если он проводился. Снимок документации не заменяет текущую схему. Историческую стоимость одного ролика не переносите на другой режим или будущую дату.
Локальная приёмка скрипта выполняется с подставным cURL, который возвращает фиктивные ответы: первый запуск делает один POST, повтор — только GET, изменение тела останавливается до сети. После такой проверки согласованный реальный запуск проверит качество и учёт. Это даёт клиенту пример, который можно понять и безопасно продолжить.
Частые вопросы
POST /estimate тоже расходует баланс генерацией?
Он проверяет общий запрос и возвращает предварительную оценку без создания платного ролика. Создание работы в этой инструкции выполняется через POST /v1/media/generations.
Зачем jq, если JSON можно открыть редактором?
jq проверяет синтаксис, извлекает ID и статус и помогает сравнивать тело без зависимости от порядка ключей объектов. Он не заменяет схему модели.
Почему повторный запуск скрипта не должен создавать новый ролик?
Скрипт сохраняет тело, постоянный ключ и полученный ID. При известном ID выполняется GET; при потерянном ответе восстановление использует прежний ключ и тело.
Как начать новый самостоятельный заказ?
Использовать новую папку с новым телом и новым Idempotency-Key после проверки схемы и оценки. Не переписывать следы уже отправленного заказа.