Практика API · обновлено 20 октября 2026 г. · 12 минут

Генерация видео API: cURL, JSON и jq от файла запроса до результата

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

Генерация видео 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.

Как это устроено
  1. Файл JSON: Точное содержание заказа
  2. Схема и оценка: Проверка до платного запуска
  3. Один POST: Сохранённые тело и ключ
  4. GET и разбор ответа: ID, результат и billing
Файл JSON → Схема и оценка → Один POST → GET и разбор ответа
Учебная схема действий. Она не является подтверждением выполненной платной генерации.

Сохраните 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 после проверки схемы и оценки. Не переписывать следы уже отправленного заказа.

Источники

Канонический адрес материала · Получить API-ключ