promptstringОбязательноУкажите, что взять из каждого референса. Пишите video 1, video 2 для референсных видео и audio 1, audio 2 для аудиофрагментов, по порядку загрузки.
- По умолчанию
- Не задано
- Ограничения
- minLength:1
pruna-ai/p-video-2-pro/reference-to-videoP-Video 2 Pro reference-to-video собирает клип 5–15 секунд в 480p или 768p из до 3 референсных видео и 3 аудиофрагментов, которые в промпте называются video 1 и audio 1, плюс необязательные начальный и конечный кадры. Без контент-фильтра.
Примеры используют публичные параметры выбранного эндпоинта. Замените API-ключ и примеры медиафайлов, затем отправьте запрос со своего сервера.
SDK и интеграцииhttps://api.spicyapi.ai/api/v1/jobs/createTaskХраните API-ключ в серверной переменной окружения SPICY_API_KEY. Используйте Bearer-аутентификацию и Content-Type: application/json для JSON-запросов.
Authorization: Bearer $SPICY_API_KEY# Generate the idempotency key once; reuse it when retrying after a lost response.
export SPICY_IDEMPOTENCY_KEY="${SPICY_IDEMPOTENCY_KEY:-$(uuidgen)}"
curl -X POST https://api.spicyapi.ai/api/v1/jobs/createTask \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $SPICY_IDEMPOTENCY_KEY" \
-d '{
"model": "pruna-ai/p-video-2-pro/reference-to-video",
"input": {
"aspect_ratio": "auto",
"duration_seconds": 5,
"generation_mode": "speed",
"prompt": "A slow aerial shot drifting over a misty pine forest at sunrise",
"prompt_optimization_mode": "off",
"resolution": "768p"
}
}'
# Copy data.taskId from the response into TASK_ID, then poll the status.
TASK_ID="TASK_ID_FROM_RESPONSE"
curl "https://api.spicyapi.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $SPICY_API_KEY"Пример построен по входной схеме этой модели — той же, по которой API проверяет каждый запрос.
Поместите эти поля в объект input тела запроса. Имена и типы соответствуют API. Не предполагайте значение по умолчанию, если оно не задано.
promptstringОбязательноУкажите, что взять из каждого референса. Пишите video 1, video 2 для референсных видео и audio 1, audio 2 для аудиофрагментов, по порядку загрузки.
seedintegerНеобязательноДелает запуски сопоставимыми при изменении промпта. Не гарантирует покадрово одинаковый клип.
prompt_optimization_modestringНеобязательноoff (по умолчанию) отправляет ваш промпт дословно; turbo и max позволяют сервису сначала его переписать. Цену не меняет.
"off""turbo""max"duration_secondsintegerНеобязательноВлияет на ценуЦелые секунды от 5 до 15 (по умолчанию 5). Определяет цену.
keep_reference_audiobooleanНеобязательноПо умолчанию выключено. Если включено, ваш аудиофрагмент используется как звуковая дорожка вместо генерации нового звука; нужен ровно один аудиофрагмент.
generation_modestringНеобязательноВлияет на ценуcost (дешевле, немного медленнее) или speed (по умолчанию, быстрее при том же качестве).
"cost""speed"resolutionstringНеобязательноВлияет на цену480p или 768p (по умолчанию 768p). 768p стоит дороже за секунду.
"480p""768p"aspect_ratiostringНеобязательноauto (по умолчанию) подстраивается под референсы; или выберите 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 или 1:1. Цену не меняет.
"auto""16:9""9:16""4:3""3:4""3:2""2:3""1:1"reference_audio_urlsarray<string>НеобязательноВлияет на ценуДо 3 аудиофрагментов, всего до 15 секунд, для голоса, диалога или музыки. Также требует начальный кадр или референсное видео. Добавляет фиксированную плату.
reference_video_urlsarray<string>НеобязательноВлияет на ценуДо 3 видео, каждое не короче 2 секунд и всего до 15 секунд, для движения, камеры и темпа. Их секунды оплачиваются плюс одна секунда на округление.
last_image_urlstringНеобязательноВлияет на ценуНеобязательный конечный кадр, на котором заканчивается клип. Добавляет небольшую плату.
image_urlstringНеобязательноВлияет на ценуНеобязательный начальный кадр: кто и где начинается сцена. Добавляет небольшую плату.
{
"additionalProperties": false,
"properties": {
"aspect_ratio": {
"default": "auto",
"description": "Output aspect ratio; auto adapts to the references.",
"enum": [
"auto",
"16:9",
"9:16",
"4:3",
"3:4",
"3:2",
"2:3",
"1:1"
],
"type": "string",
"x-ui": {
"order": 80,
"widget": "select"
}
},
"duration_seconds": {
"default": 5,
"description": "Output length in whole seconds, from 5 to 15. Billed per second.",
"maximum": 15,
"minimum": 5,
"type": "integer",
"x-ui": {
"affects_price": true,
"order": 60,
"step": 1,
"unit": "s",
"widget": "slider"
}
},
"generation_mode": {
"default": "speed",
"description": "cost is cheaper and slower, speed (the default) is faster at the same quality.",
"enum": [
"cost",
"speed"
],
"type": "string",
"x-ui": {
"affects_price": true,
"order": 65,
"widget": "radio"
}
},
"image_url": {
"contentMediaType": [
"image/jpeg",
"image/png",
"image/webp",
"image/gif"
],
"description": "Optional opening frame. Adds a small per-image charge.",
"format": "uri",
"type": "string",
"x-ui": {
"affects_price": true,
"order": 92,
"widget": "upload"
}
},
"keep_reference_audio": {
"default": false,
"description": "Use the reference audio as the soundtrack instead of generating new sound. Needs exactly one audio clip.",
"type": "boolean",
"x-ui": {
"order": 63,
"widget": "switch"
}
},
"last_image_url": {
"contentMediaType": [
"image/jpeg",
"image/png",
"image/webp",
"image/gif"
],
"description": "Optional closing frame. Adds a small per-image charge.",
"format": "uri",
"type": "string",
"x-ui": {
"affects_price": true,
"order": 91,
"widget": "upload"
}
},
"prompt": {
"description": "Say what the shot should take from each reference. Refer to reference videos as video 1, video 2 and to audio clips as audio 1, audio 2, numbered in upload order.",
"minLength": 1,
"type": "string",
"x-ui": {
"order": 100,
"primary": true,
"reference_tokens": {
"reference_audio_urls": "audio {index}",
"reference_video_urls": "video {index}"
},
"widget": "textarea"
}
},
"prompt_optimization_mode": {
"default": "off",
"description": "Let the service rewrite and expand the prompt first: off follows it word for word, turbo is a quick rewrite, max a fuller one. Does not change the price.",
"enum": [
"off",
"turbo",
"max"
],
"type": "string",
"x-ui": {
"advanced": true,
"affects_price": false,
"order": 30,
"widget": "radio"
}
},
"reference_audio_urls": {
"description": "Reference audio for a voice, dialogue or music: up to 3 clips, 15 seconds in total. Needs an opening frame or a reference video as well. Refer to them as audio 1, audio 2.",
"items": {
"contentMediaType": [
"audio/mpeg",
"audio/wav"
],
"format": "uri",
"type": "string"
},
"maxItems": 3,
"minItems": 1,
"type": "array",
"x-ui": {
"accept": [
"audio/mpeg",
"audio/wav"
],
"affects_price": true,
"max_total_duration_seconds": 15,
"order": 88,
"widget": "multi-upload"
}
},
"reference_video_urls": {
"description": "Reference videos for motion, camera and pacing: up to 3, 2 seconds or longer each and 15 seconds in total. Refer to them as video 1, video 2. Their seconds are billed too.",
"items": {
"contentMediaType": [
"video/mp4",
"video/webm",
"video/quicktime"
],
"format": "uri",
"type": "string"
},
"maxItems": 3,
"minItems": 1,
"type": "array",
"x-ui": {
"accept": [
"video/mp4",
"video/webm"
],
"affects_price": true,
"max_total_duration_seconds": 15,
"order": 90,
"widget": "multi-upload"
}
},
"resolution": {
"default": "768p",
"description": "Output resolution. 768p costs more per second than 480p.",
"enum": [
"480p",
"768p"
],
"type": "string",
"x-ui": {
"affects_price": true,
"order": 70,
"widget": "radio"
}
},
"seed": {
"description": "Random seed for controlled comparisons, not a frame-identical guarantee.",
"maximum": 2147483647,
"minimum": 0,
"type": "integer",
"x-ui": {
"order": 10,
"widget": "number"
}
}
},
"required": [
"prompt"
],
"type": "object",
"x-pricing": {
"billingMode": "formula",
"variantFields": [
"generation_mode",
"resolution"
]
}
}HTTP 202 означает принятие задачи, а не завершение генерации. Сохраните полученный taskId и проверяйте эту задачу. Не создавайте новую, пока ждёте результат.
HTTP/1.1 202 Accepted
Location: /api/v1/jobs/recordInfo?taskId=job_01k3m8x9q2z4v7n5p6r8s0t1w3
{
"code": 200,
"msg": "success",
"request_id": "req_example_create",
"data": {
"taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
"state": "queued",
"estimatedCost": "0.750000"
}
}В ответе о приёме есть оценка стоимости — приложение сможет показать ту же сумму в истории задач и в расчётах.
curl --get "https://api.spicyapi.ai/api/v1/jobs/recordInfo" \
--data-urlencode "taskId=$TASK_ID" \
-H "Authorization: Bearer $SPICY_API_KEY"Начните с интервала около 2 секунд и постепенно увеличивайте его. Прекратите опрос при конечном состоянии. После сбоя соединения используйте исходный taskId. Повторно создавать задачу можно только с исходным ключом идемпотентности и исходным телом запроса.
import { verifyWebhook } from "@spicyapi/sdk";
// express.raw keeps the bytes we signed; express.json never matches.
app.post("/hooks/spicy", express.raw({ type: "application/json" }), (req, res) => {
const { payload } = verifyWebhook({
rawBody: req.body,
timestamp: req.header("X-Webhook-Timestamp"),
signature: req.header("X-Webhook-Signature"),
payloadVersion: Number(req.header("X-Webhook-Payload-Version")),
secret: process.env.SPICY_WEBHOOK_SECRET,
});
res.status(200).end();
void handleFinishedTask(payload);
});Передайте callBackUrl при создании задачи — готовая запись придёт к вам POST-запросом, и никакого опроса не понадобится. В каждой доставке есть X-Webhook-Timestamp, X-Webhook-Signature и X-Webhook-Payload-Version. Подпись — это HMAC-SHA256 в base64 от taskId.timestamp.sha256(body), поэтому проверять её нужно по исходным байтам: если разобрать JSON и сериализовать заново, байты изменятся и проверка не пройдёт. Неудачные доставки повторяются, так что один и тот же taskId может прийти дважды — сделайте обработчик идемпотентным. Сначала ответьте 2xx, работу выполняйте после: медленный обработчик считается неудачной доставкой.
Обрабатывайте output только при state, равном succeeded. При ошибке проверьте errorCode, errorMessage и request_id. Стоимость и состояния в примерах не являются фактическим счётом.
{
"code": 200,
"msg": "success",
"request_id": "req_example_record",
"data": {
"taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
"model": "pruna-ai/p-video-2-pro/reference-to-video",
"state": "succeeded",
"output": {
"assets": [
{
"key": "tasks/2026/08/28/job_01k3m8x9q2z4v7n5p6r8s0t1w3/3f7a1c9e5b2d48f0a6c1e3d5b7f9a1c3.mp4",
"mime": "video/mp4",
"url": "https://example.r2.cloudflarestorage.com/results/job_01k3m8x9q2z4v7n5p6r8s0t1w3/result.mp4?X-Amz-Signature=SIGNATURE_FROM_RESPONSE",
"expiresAt": "2026-08-28T12:20:08.000Z"
}
]
},
"cost": "0.750000",
"settled": true,
"createdAt": "2026-08-28T12:00:00Z",
"completedAt": "2026-08-28T12:00:30Z"
}
}Доступные файлы можно открыть или скачать по assets[].url без заголовка API Authorization. Подписанная ссылка действует до expiresAt. Чтобы обновить её, повторно запросите задачу в течение срока хранения файла. У ожидающих или недоступных файлов URL отсутствует. Не передавайте подписанные ссылки посторонним.
Используйте SpicyAPI в приложении, терминале, ИИ-ассистенте или визуальном процессе. Выберите руководство по настройке.
Писать код выше вовсе не обязательно. Подключите сервер MCP один раз — и любой агент, который говорит на MCP (Claude Code, Codex, Cursor, Windsurf, VS Code, Gemini CLI, Cline, LibreChat), вызовет эту модель по имени. Либо запустите её из терминала одной командой.
Возьми pruna-ai/p-video-2-pro/reference-to-video и сделай пятисекундный кадр: крыша в неоне на закате, медленный наезд. Сохрани в ./out/.
Агент найдёт модель в каталоге, сам загрузит локальный файл, покажет точную смету и предельную сумму списания — и дождётся вашего согласия. До этого ничего не списывается.
claude mcp add spicyapi -e SPICY_API_KEY=$SPICY_API_KEY \
-- npx --yes --package=@spicyapi/mcp spicyapi-mcp
npx skills add spicyapi-ai/spicy-skillПервая строка даёт агенту инструменты, вторая — Skill, то есть правила обращения с ними: сначала смета, потом списание; локальные файлы загружать, а не вставлять байтами; URL готового результата читать, а не угадывать. Остальные клиенты подключают тот же сервер через собственный файл настроек — см. раздел для разработчиков.
npx @spicyapi/cli tasks create --model pruna-ai/p-video-2-pro/reference-to-video \
--input-json '{"aspect_ratio":"auto","duration_seconds":5,"generation_mode":"speed","prompt":"A slow aerial shot drifting over a misty pine forest at sunrise","prompt_optimization_mode":"off","resolution":"768p"}' \
--waitВходные данные взяты из схемы этого эндпоинта — той же, что и в примерах выше. --wait дождётся завершения задачи и выведет её, а --yes пропустит подтверждение в CI. Сначала задайте SPICY_API_KEY: один ключ работает и для API, и для CLI, и для сервера MCP.
npx @spicyapi/cli tasks create --model pruna-ai/p-video-2-pro/reference-to-video \
--input-json '{…}' --retention 1h --wait
npx @spicyapi/cli tasks get $TASK_ID # read back the retention that appliedКлюч --retention только сокращает срок — 30m, 1h, 7d или 0, чтобы удалить сразу после перехода задачи в конечное состояние. Реально действует наименьшее из трёх: ваш запрос, настройки аккаунта и предел платформы, — поэтому считайте его из записи задачи, а не исходите из того, что запрошенное число применилось. Запросить срок больше разрешённого аккаунтом нельзя: это ничего не изменит.
Установите узлы SpicyAPI через ComfyUI Manager или командой ниже и найдите на холсте P-Video 2 Pro · Reference to Video. У узла те же параметры, что и на этой странице, а цена показывается до запуска.
cd ComfyUI/custom_nodes
git clone https://github.com/spicyapi-ai/comfyui-spicyapiВстроенная панель читает ту же схему, что и эта страница, поэтому не может разойтись с API. Если содержимое должно индексироваться поиском по документации, возьмите JSON из /api/embed/models/ и отрисуйте его самостоятельно.
<iframe src="https://spicyapi.ai/embed/ru/models/p-video-2-pro/api?model=pruna-ai%2Fp-video-2-pro%2Freference-to-video" width="100%" height="560" loading="lazy" title="Пример запроса" style="border:1px solid rgba(0,0,0,.12)"></iframe>