Разработчикам
Salven API
Модели студии по ценам студии: из Claude, из вашего кода или из AI-агента. Каждый запуск оплачивается вашими кредитами, а результат попадает в вашу галерею.
Обзор
Salven API делает то же, что студия Salven: те же модели, те же цены, те же проверки. Через него создаются изображения, видео, аудио (речь, музыка, звуки) и 3D-модели, применяются готовые эффекты, а сохранённые персонажи попадают в изображение или ролик по своему @handle.
Запуск оплачивается кредитами аккаунта, которому принадлежит ключ, а результат попадает в галерею этого аккаунта на salven.ai. Цена известна заранее, до любых списаний (пробный запрос); кредиты списываются при старте, а за неудавшийся запуск возвращаются.
API открыт для каждого аккаунта Salven. Пользоваться им можно тремя способами:
| Способ | Для кого | Что понадобится |
|---|---|---|
| Claude | Для всех, кто пользуется Claude: вы просите в чате, Claude запускает модели. | Адрес https://salven.ai/mcp и вход в ваш аккаунт Salven. |
| REST | Для ваших скриптов, сервисов и автоматизаций. | API-ключ и любой HTTP-клиент. |
| Агент | Для Claude Code, Cursor и других агентов, которые пишут код. | API-ключ и файл SKILL.md. |
Все три способа приводят к одному и тому же серверному коду, поэтому запуск стоит одинаково и проходит одни и те же проверки, каким бы путём он ни начался.
Подключить Claude
Salven работает как удалённый MCP-сервер по адресу https://salven.ai/mcp. Добавьте его в Claude один раз, и Claude сможет выбрать модель, назвать цену, запустить генерацию и отдать вам результат — всё от имени вашего аккаунта Salven.
Claude в браузере, на компьютере и в телефоне
- В Claude откройте Customize → Connectors → Add custom connector. В мобильных приложениях добавить коннектор нельзя: сделайте этот шаг на claude.ai, и после этого коннектор работает и в мобильных приложениях.
- Вставьте адрес
https://salven.ai/mcp. - Claude отправит вас на Salven. Войдите, если ещё не вошли, прочитайте, что сможет приложение, и нажмите Разрешить.
Кто может добавить коннектор, зависит от вашего тарифа Claude: на тарифе Free можно добавить один свой коннектор, а на тарифах Team и Enterprise коннектор добавляет для всей организации её владелец (или участник, роль которого это разрешает), а не каждый участник для себя.
Тот же адрес с кнопкой копирования есть в Настройках, в блоке Claude и API.
Claude Code
# Add Salven to Claude Code as an MCP server. Then, inside Claude Code, run /mcp and pick salven to sign in.
claude mcp add --transport http salven https://salven.ai/mcpКоманда только сохраняет сервер. Чтобы войти, запустите Claude Code, выполните /mcp, выберите salven и начните вход: в браузере откроется Salven, где нужно войти в аккаунт и нажать Разрешить, как описано выше.
Другие MCP-клиенты
Тем же адресом может пользоваться любой клиент, который работает с MCP по Streamable HTTP. Клиент с поддержкой OAuth выполнит вход так же, как Claude. Клиент, который отправляет фиксированный заголовок, может вместо этого использовать API-ключ:
Server URL: https://salven.ai/mcp
Transport: Streamable HTTP
Header: Authorization: Bearer <your Salven API key>В Claude Code тот же заголовок задаётся при добавлении сервера, вместо входа, описанного выше; ключ оболочка берёт из переменной окружения SALVEN_API_KEY:
# Add Salven to Claude Code with an API key instead of the OAuth sign-in.
# The shell reads the key from SALVEN_API_KEY, so it is never typed into the command.
# Claude Code saves the header, with the key in it, together with the server.
claude mcp add --transport http salven https://salven.ai/mcp \
--header "Authorization: Bearer $SALVEN_API_KEY"Что умеет Claude
Подключение даёт Claude инструменты из таблицы ниже. Они вызывают тот же код, что и REST API, а запуски через них учитываются в тех же лимитах.
| Инструмент | Что делает |
|---|---|
account | Аккаунт, от имени которого работает подключение, и его баланс кредитов. |
list_models | Доступные сейчас модели, при желании только одного вида: ID, режимы, входы и базовая цена. |
get_model | Полная спецификация одной модели: режимы, параметры, входы, варианты соотношения сторон, ограничения на промпт и цены. |
generate | Запускает генерацию на выбранной модели или только называет цену при dryRun: true. Ждёт примерно до 45 секунд и возвращает готовые файлы либо ID запусков, которые ещё идут. |
get_generation | Статус одной генерации и её файлы, когда она готова. Может подождать завершения до 40 секунд. |
list_generations | Работы аккаунта, сначала новые: созданные и на сайте, и через API. |
list_effects | Готовые эффекты: какие изображения нужны каждому, его цена и варианты качества. |
run_effect | Применяет эффект к вашему изображению или изображениям либо только называет цену при dryRun: true. |
list_characters | Ваши персонажи и AI-инфлюенсеры, у каждого свой @handle. |
create_character | Сохраняет человека как персонажа: имя, @handle и от одного до четырёх фото. Бесплатно. |
upload_from_url | Копирует файл с публичного веб-адреса в ваши загрузки на Salven. Salven скачивает файл сам: изображения до 10 МБ, аудио до 20 МБ, видео и модели GLB до 60 МБ. |
create_upload_link | Создаёт одноразовую ссылку, по которой можно добавить файлы с вашего устройства. |
get_upload_link | Файлы, которые пришли по ссылке для загрузки. |
Как передать Claude файл
Входом модели может быть только ваш собственный файл на Salven, а чат не может передать инструменту картинку, которую вы прикрепили к сообщению. У Claude есть три способа получить файл:
- Результат одной из ваших прошлых работ: Claude находит его через
list_generations. - Публичный веб-адрес: Claude копирует файл через
upload_from_url. Salven скачивает файл сам, поэтому ограничение прямой загрузки в 25 МБ здесь не действует: изображение может быть до 10 МБ, аудиофайл до 20 МБ, видео или модель GLB до 60 МБ. Это способ передать видео или 3D-модель больше 25 МБ. Скачивание должно завершиться, пока Claude ждёт ответа инструмента, поэтому большому файлу нужен адрес, который отдаёт его быстро. - Файл с вашего устройства: Claude создаёт ссылку для загрузки через
create_upload_linkи даёт вам страницу, которую нужно открыть. Там вы выбираете файлы (входить в аккаунт не нужно) и сообщаете Claude, что они на месте. Ссылка действует 30 минут и принимает до 10 файлов, форматы и размеры те же, что в правилах загрузки: отправленный так файл проходит через веб-сервер, поэтому видео или модель GLB должны быть меньше 25 МБ. Агент с терминалом, например Claude Code, может сам отправить по ссылке файл с диска.
Кредиты и отключение
Всё, что создаёт Claude, оплачивается вашими кредитами по ценам студии. generate и run_effect умеют назвать цену, не запуская генерацию, а инструкции Salven велят Claude назвать стоимость и дождаться вашего согласия перед дорогим запуском (видео, 3D, несколько изображений) и перед тратой, о которой вы не просили. Когда именно спрашивать, всё равно решает Claude, поэтому, если сначала хотите узнать цену, так и скажите.
Чтобы отключить приложение, откройте Настройки, найдите его в блоке Claude и API и нажмите Отключить два раза (второе нажатие подтверждает). Приложение теряет доступ сразу.
Быстрый старт
Одно изображение через REST, от начала до конца. Понадобятся аккаунт Salven с кредитами на балансе и терминал с curl.
- Откройте Настройки, найдите блок Claude и API, введите название ключа и нажмите Создать ключ.
- Скопируйте ключ сразу: он показывается один раз.
- Положите его в переменную окружения
SALVEN_API_KEY, например командойexport SALVEN_API_KEY=<your key>в той оболочке, где будете работать. - Выполните вызовы ниже по одному.
API=https://salven.ai/api/v1
AUTH="Authorization: Bearer $SALVEN_API_KEY"
# Who the key acts as, and the balance
curl -s "$API/me" -H "$AUTH"
# The image models, each with its params, inputs and pricing
curl -s "$API/models?kind=image" -H "$AUTH"
# Price first: dryRun answers the cost and starts nothing
curl -s "$API/generations" -H "$AUTH" -H "Content-Type: application/json" \
-d '{"model":"nano-banana-2","prompt":"a red fox in the snow, film photo","params":{"aspect_ratio":"16:9","resolution":"2K"},"dryRun":true}'
# Then run it. The Idempotency-Key names THIS generation: to retry it (a timeout, a 5xx, a 409),
# rerun the command with the same key; pick a new key only for a new generation.
curl -s "$API/generations" -H "$AUTH" -H "Content-Type: application/json" \
-H "Idempotency-Key: fox-001" \
-d '{"model":"nano-banana-2","prompt":"a red fox in the snow, film photo","params":{"aspect_ratio":"16:9","resolution":"2K"}}'
# Wait for the result: put generations[0].id from the answer above in place of GENERATION_ID,
# and repeat while "status" is "queued" or "processing"
curl -s "$API/generations/GENERATION_ID?wait=60" -H "$AUTH"Примеры написаны для bash: macOS, Linux, WSL или Git Bash в Windows. В Windows PowerShell curl — псевдоним другой команды, поэтому используйте curl.exe и синтаксис PowerShell для переменных и кавычек либо запускайте примеры в Git Bash или WSL.
| Вызов | Что возвращает |
|---|---|
GET /me | Аккаунт и его balance. |
GET /models?kind=image | Модели изображений, у каждой свои params, inputs и pricing. |
POST /generations с "dryRun": true | Цену: cost за одну генерацию, total за весь запрос. Ничего не запускается. |
POST /generations | 202 и generations[].id. Кредиты списываются в этот момент. |
GET /generations/{id}?wait=60 | Генерацию. Повторяйте, пока status равен queued или processing; когда он станет done, файлы лежат в outputs[].url. |
Заголовок Idempotency-Key обозначает именно эту генерацию. Если ответ потерялся, отправьте тот же запрос с тем же значением заголовка: вернётся первый ответ, а повторного списания не будет. См. Идемпотентность.
С входным файлом
Модели, которая работает с изображением, видео или аудиофайлом, нужно, чтобы файл сначала оказался на Salven. Этот пример загружает фото и превращает его в ролик:
API=https://salven.ai/api/v1
AUTH="Authorization: Bearer $SALVEN_API_KEY"
# Upload the picture. The answer's "url" is the file's address on Salven:
# {"url":"https://cdn.salven.ai/uploads/...","kind":"image","mimeType":"image/jpeg","bytes":184233}
curl -s "$API/uploads" -H "$AUTH" -F "file=@photo.jpg"
# Put that url here
PHOTO_URL="PASTE_THE_URL_FROM_THE_ANSWER_ABOVE"
# Price first: the same body with dryRun
curl -s "$API/generations" -H "$AUTH" -H "Content-Type: application/json" -d @- <<EOF
{
"model": "kling-v3-pro",
"prompt": "she turns and smiles, slow dolly in",
"params": { "duration": 5 },
"inputs": { "startImage": "$PHOTO_URL" },
"dryRun": true
}
EOF
# Then run it, with a key that names this generation (reuse it only to retry this very request)
curl -s "$API/generations" -H "$AUTH" -H "Content-Type: application/json" \
-H "Idempotency-Key: anim-001" -d @- <<EOF
{
"model": "kling-v3-pro",
"prompt": "she turns and smiles, slow dolly in",
"params": { "duration": 5 },
"inputs": { "startImage": "$PHOTO_URL" }
}
EOF
# Wait for the clip: put generations[0].id from the answer above in place of GENERATION_ID,
# and repeat while "status" is "queued" or "processing" (video takes minutes)
curl -s "$API/generations/GENERATION_ID?wait=60" -H "$AUTH"Входом может быть только ваш собственный файл: загруженный через POST /uploads или outputs[].url вашей же генерации. Ссылка на файл из любого другого места отклоняется с input_not_allowed: скачайте файл и загрузите его.
Аутентификация
В каждом запросе передаётся API-ключ: Authorization: Bearer <key>. Заголовок X-API-Key: <key> тоже принимается.
- Ключ — это
slv_и за ним 40 букв и цифр. - Он показывается один раз, при создании. Salven хранит только его хеш, поэтому потерянный ключ нельзя показать снова: отзовите его и создайте новый.
- У аккаунта может быть до 10 действующих ключей. Создавать и отзывать их можно в Настройках, в блоке Claude и API. Отозванный ключ перестаёт работать сразу.
- Ключ действует от имени своего аккаунта: списываются кредиты этого аккаунта, результаты попадают в его галерею. Обращайтесь с ним как с паролем: храните в переменной окружения или в хранилище секретов и никогда не пишите в код, репозиторий или лог.
- API проверяет только ключ. Браузер, в котором выполнен вход на salven.ai, не может вызвать API со своей cookie.
| HTTP | Код | Что значит |
|---|---|---|
| 401 | unauthorized | Ключа нет, это не ключ Salven, он неизвестен или отозван. |
| 403 | api_not_available | API недоступен для этого аккаунта. |
Общие правила
- Базовый URL:
https://salven.ai/api/v1. Все пути на этой странице указаны относительно него. - Тела запросов и ответов — JSON. Только
POST /uploadsпринимаетmultipart/form-data. - Время — в формате ISO 8601, в UTC.
- Каждый ответ приходит с
Cache-Control: no-store. - Деньги считаются в кредитах.
- Ошибка от API — это
{ "error": { "code": …, "message": … } }с соответствующим HTTP-статусом. Веб-сервер перед API может ответить и без JSON-тела:413на тело запроса больше 25 МБ и502или504, пока сайт перезапускается. См. Ошибки.
Как проходит генерация
- Баланс.
GET /meвозвращаетbalance: дороже этого запуск стоить не может. - Модель.
GET /modelsили один вид через?kind=. Прочитайте спецификацию выбранной модели:params,inputs,aspect,modes,pricing. Отправляйте только те имена и значения, которые в ней перечислены. - Загрузки. Только если модель принимает файлы:
POST /uploadsвозвращаетurl, который используется как вход. - Цена.
POST /generationsс"dryRun": trueвозвращаетcost(одна генерация) иtotal(cost×count). - Запуск. То же тело без
dryRun, с заголовкомIdempotency-Key. Ответ —202сgenerations[].id, кредиты списаны. - Ожидание.
GET /generations/{id}?wait=60; повторяйте, покаstatusравенqueuedилиprocessing. - Результат.
outputs[].url— прямые ссылки на файлы.
Статусы
| Статус | Что значит |
|---|---|
queued | Принята и оплачена, ждёт своей очереди. |
processing | Создаётся. |
done | Готова. Файлы лежат в outputs. |
failed | Создать не удалось. Кредиты возвращены (refunded). |
У неудавшейся генерации error.code равен generation_failed; причину, которую назвала сама модель, API не сообщает. Кредиты уже вернулись, так что можно попробовать ещё раз, возможно с другими настройками. После двух неудач подряд остановитесь и проверьте запрос.
Сколько это занимает
| Что | Обычное время |
|---|---|
| Изображение | 10–60 секунд |
| Видео | 1–5 минут, дольше для роликов на 10–15 секунд |
| Речь | От нескольких секунд до полуминуты |
| Музыка | До нескольких минут |
| 3D-модель | 2–8 минут; некоторым моделям может понадобиться больше получаса |
| Риг для 3D-модели | Около 2 минут |
На стороне Salven зависший запуск ничто не завершает. Разумный клиент перестаёт ждать примерно через 5 минут для изображения, 10 для аудио, 20 для видео и 60 для 3D и сам не запускает ту же генерацию заново: запуск остаётся в галерее аккаунта.
Эндпоинты
| Эндпоинт | Что делает |
|---|---|
GET /me | Аккаунт, которому принадлежит ключ, и его баланс. |
GET /models | Все модели, которые может запустить API, при желании только одного вида. |
GET /models/{id} | Спецификация одной модели. |
POST /uploads | Сохраняет файл как ваш собственный и возвращает его URL. |
POST /generations | Запускает одну или несколько генераций либо называет их цену. |
GET /generations/{id} | Одна генерация: статус и, когда она готова, файлы. |
GET /generations | Генерации аккаунта, сначала новые. |
GET /characters | Персонажи и инфлюенсеры аккаунта. |
GET /characters/{handle} | Один из них, с фото. |
POST /characters | Создаёт персонажа. |
GET /effects | Готовые эффекты. |
GET /effects/{slug} | Спецификация одного эффекта. |
POST /effects/{slug}/run | Запускает эффект либо называет его цену. |
Числа в примерах на этой странице — только примеры. Актуальные цены — в GET /models и в таблице раздела Модели; точная цена запроса — та, что вернёт пробный запрос с тем же телом.
GET /me
{
"id": "cmg1x2y3z0000abcd1234efgh",
"username": "anna",
"name": "Anna",
"credits": 1200,
"dailyAllowance": 0,
"balance": 1200,
"key": { "id": "cmg4k5m6n0001abcd5678ijkl", "name": "Claude Code", "prefix": "slv_Ab12Cd" }
}credits не сгорают. dailyAllowance — остаток бесплатных ежедневных кредитов партнёра на сегодня; они тратятся первыми. balance — сумма обоих: дороже этого запуск стоить не может. key называет ключ, с которым пришёл запрос.
GET /models и GET /models/{id}
GET /models возвращает { "models": [ … ] } в том же порядке, что в студии. ?kind= сужает список до image, video, audio или 3d; на любое другое значение придёт отказ 400 invalid_kind. GET /models/{id} возвращает одну модель или 404 model_not_found. Модель выглядит так:
{
"id": "kling-v3-pro",
"name": "Kling v3 Pro",
"kind": "video",
"description": "Top-tier video quality, start from a frame",
"pricing": { "credits": 20, "unit": "second", "secondsFrom": "params.duration" },
"modes": [
{ "slug": "generate", "name": "Generate", "promptRequired": true, "priceMultiplier": 1 }
],
"prompt": { "maxLength": 10000 },
"negativePrompt": { "maxLength": 2500 },
"aspect": null,
"params": [
{
"name": "duration",
"type": "number",
"min": 3,
"max": 15,
"step": 1,
"default": 5,
"priced": true
},
{
"name": "aspect_ratio",
"type": "enum",
"options": ["16:9", "9:16", "1:1"],
"default": "16:9"
},
{ "name": "audio", "type": "boolean", "default": false, "priceFactorWhenTrue": 1.5 }
],
"inputs": [
{ "name": "startImage", "kind": "image", "required": false, "multiple": false, "max": 1 },
{
"name": "endImage",
"kind": "image",
"required": false,
"multiple": false,
"max": 1,
"requires": "startImage"
}
],
"maxCount": 1,
"startImageSetsShape": true,
"seed": false,
"references": null,
"characters": { "supported": true, "maxCharacters": 3, "needsStartFrame": true }
}| Поле | Как его читать |
|---|---|
pricing | credits за unit. generation — фиксированная цена. second умножается на секунды, а secondsFrom говорит, на какие: параметр duration или длина входного файла. 1000_characters — для речи: длина промпта. Результат умножается на priceMultiplier режима и на коэффициенты выбранных вариантов. |
modes | Режимы студии, в которых работает модель. Передавайте mode, только если их больше одного; по умолчанию берётся generate, а если его нет — первый. promptRequired: false значит, что модели достаточно входов и промпт не нужен. |
prompt, negativePrompt | Наибольшая длина текста в символах. negativePrompt: null значит, что модель его не принимает. |
params | Настройки, передаются по name. number: в пределах min..max с шагом step. enum: ровно один из options; priceFactors даёт ценовой коэффициент каждого варианта, optionNotes поясняет, что это за вариант (например, пол и тон голоса). boolean: true или false, с priceFactorWhenTrue, если включённый переключатель стоит дороже. Пропущенный параметр берёт свой default; неизвестное имя отклоняется. |
aspect | Не null: соотношение сторон результата задаётся полем верхнего уровня aspect, одним из options. Null: у модели есть свой параметр aspect_ratio либо соотношение сторон определяется её входом. |
inputs | Файлы, передаются как inputs: { "<name>": "<url>" } либо массивом URL для входа с multiple, не больше max. kind — чем должен быть файл. Вход с required обязателен; вход с requires используется только вместе с тем входом, который там назван. |
maxCount | Сколько отдельных генераций один запрос может запустить через count. |
startImageSetsShape | true: первый кадр задаёт соотношение сторон ролика, и aspect_ratio игнорируется, как только кадр передан. |
seed, seedMax | Принимает ли модель seed и его наибольшее значение. См. Seed. |
references | Ограничения для референсных медиафайлов у моделей, которые их принимают, иначе null. |
characters | Добавляет ли @handle в промпте фото этого человека к запросу (supported), для скольких людей в одном запуске (maxCharacters) и обязателен ли при этом первый кадр (needsStartFrame). См. Персонажи. |
Две вещи оставлены для SKILL.md: тип параметра motions, который используется при риггинге 3D-моделей, и правила блока references.
POST /uploads
Multipart-форма с полем file и, по желанию, kind (image, video, audio или model). Без kind вид определяется по первым байтам файла. Ответ — 201:
{
"url": "https://cdn.salven.ai/uploads/cmg1x2y3z0000abcd1234efgh/1790847000000-9f2c4e6a8b0d1f35.jpg",
"kind": "image",
"mimeType": "image/jpeg",
"bytes": 184233
}| Вид | Форматы | Наибольший файл |
|---|---|---|
image | JPEG, PNG, WebP | 10 МБ |
video | MP4, MOV, WebM | 25 МБ |
audio | MP3, WAV, M4A, OGG | 20 МБ |
model | GLB (бинарный glTF 2.0) | 25 МБ |
- Формат читается из байтов, а не из имени файла.
- Фото HEIC отклоняются с
heic_not_supported: сначала конвертируйте их в JPEG. - 3D-модель должна быть одним файлом GLB с текстурами внутри;
.gltfне принимается. - 25 МБ для видео и 3D-модели — это ограничение веб-сервера на всё тело запроса, поэтому сам файл должен быть немного меньше. Запрос с телом крупнее отклоняется веб-сервером ещё до того, как дойдёт до API: такой ответ —
413, и его тело не JSON. - Видео или модель GLB больше этого, до 60 МБ, можно передать только с публичного веб-адреса, через MCP-инструмент
upload_from_url: см. Как передать Claude файл. - Файл становится вашей собственной загрузкой. Входами могут быть только ваши файлы: загрузки и
outputs[].urlваших же генераций.
Отказы: 400 missing_file, invalid_form_data, invalid_kind; 413 file_too_large (изображение или аудиофайл больше своего лимита, с max в байтах); 415 unsupported_format, heic_not_supported; 502 storage_error (повторите один раз); 503 uploads_disabled.
POST /generations
{
"model": "nano-banana-2",
"mode": "generate",
"prompt": "a red fox in the snow, film photo",
"params": { "aspect_ratio": "16:9", "resolution": "2K" },
"count": 1,
"enhancePrompt": true,
"dryRun": false
}| Поле | Что значит |
|---|---|
model | Обязательно. ID модели из GET /models. |
mode | Необязательно. Один из modes[].slug модели. |
prompt | Обязательно, если у режима promptRequired равен true. Любой язык; @handle называет одного из ваших персонажей. См. Промпты. |
negativePrompt | Только там, где negativePrompt модели не null, длиной до его maxLength. |
aspect | Только там, где aspect модели не null: один из его options. |
params | Имя параметра → значение, как они перечислены в params модели. |
inputs | Имя входа → URL файла либо массив URL для входа с multiple. |
seed | Только там, где seed модели равен true: целое число от 0 до seedMax. |
count | От 1 до maxCount отдельных генераций, каждая оплачивается. По умолчанию 1. |
enhancePrompt | По умолчанию true. false отправляет промпт в точности как написан. |
dryRun | true: вернуть цену, ничего не запускать и ничего не списывать. |
Поле, которого нет в этом списке, отклоняется с invalid_request. С каждым настоящим запуском отправляйте заголовок Idempotency-Key; см. Идемпотентность.
На пробный запрос приходит ответ 200. cost — одна генерация, total — сколько будет списано за весь запрос:
{
"dryRun": true,
"model": "nano-banana-2",
"mode": "generate",
"cost": 45,
"count": 1,
"total": 45,
"balance": 1200
}На настоящий запуск приходит ответ 202:
{
"generations": [
{
"id": "cmg7q2w8e0003abcd9012mnop",
"status": "queued",
"kind": "image",
"model": "nano-banana-2",
"mode": "generate",
"effect": null,
"prompt": "a red fox in the snow, film photo",
"characters": [],
"seed": null,
"source": "api",
"cost": 45,
"refunded": 0,
"createdAt": "2026-10-01T09:30:00.000Z",
"startedAt": null,
"finishedAt": null,
"outputs": [],
"error": null
}
],
"charged": 45,
"balance": 1155
}Если что-то (обычно баланс) останавливает запрос, когда запущена только часть из count генераций, уже начатые остаются в силе, а в ответе дополнительно есть "partial": true и "stopped" с кодом причины в code. Чтобы запустить остальное, отправьте новый запрос с меньшим count и новым Idempotency-Key.
balance равен null, если после списания его не удалось прочитать; он есть в GET /me. Если не удалось прочитать только что созданные генерации, в generations будут только id и status каждой, а charged тоже будет null: опрашивайте эти ID как обычно.
GET /generations/{id}
{
"id": "cmg7q2w8e0003abcd9012mnop",
"status": "done",
"kind": "image",
"model": "nano-banana-2",
"mode": "generate",
"effect": null,
"prompt": "a red fox in the snow, film photo",
"characters": [],
"seed": null,
"source": "api",
"cost": 45,
"refunded": 0,
"createdAt": "2026-10-01T09:30:00.000Z",
"startedAt": "2026-10-01T09:30:01.000Z",
"finishedAt": "2026-10-01T09:30:24.000Z",
"outputs": [
{
"url": "https://cdn.salven.ai/generations/cmg7q2w8e0003abcd9012mnop/0.png",
"mimeType": "image/png",
"width": 2048,
"height": 1152,
"durationSeconds": null,
"thumbnailUrl": null
}
],
"error": null
}С ?wait=<seconds>, от 0 до 60, ответ задерживается, пока генерация не станет done или failed либо пока не пройдут эти секунды; тогда приходит ответ с текущим состоянием. Если ID не относится к генерациям аккаунта, приходит 404 not_found.
| Поле | Что значит |
|---|---|
status | queued, processing, done или failed. |
kind | image, video, audio или 3d. |
model, mode, prompt | Что запускалось. У запуска эффекта они равны null, а effect содержит его slug, name и quality. |
characters | Чьи фото добавлены по @-именам из промпта: у каждого handle, name и kind. |
seed | Seed, с которым прошёл запуск, если он был передан. |
source | Откуда запущено: web, bot или api. |
cost, refunded | Списанные кредиты и кредиты, возвращённые при неудаче. |
createdAt, startedAt, finishedAt | Время. Последние два равны null, пока событие не произошло. |
outputs | Файлы, когда статус done: url, mimeType, width, height, durationSeconds, thumbnailUrl. До этого пусто. |
error | null либо, у неудавшегося запуска, объект с code (generation_failed) и message. |
Результат 3D — файл .glb. У такого результата могут быть ещё поля faces, а у модели с ригом — fbxUrl и clips; они описаны в SKILL.md.
GET /generations
Генерации аккаунта, сначала новые: все, а не только запущенные через API. Параметры запроса: limit (по умолчанию 20, до 50), kind (image, video, audio или 3d) и cursor. Ответ — { "generations": [ … ], "nextCursor": … }: передайте nextCursor как cursor, чтобы получить следующую страницу; на последней он равен null. На неверные параметры придёт 400 invalid_kind или invalid_cursor.
Персонажи
Персонаж — это человек, сохранённый в аккаунте: имя, @handle и от одного до четырёх фото одного и того же человека, первое из которых главное. Напишите в промпте @anna, и модель, которая принимает персонажей, получит вместе с запросом фото anna, так что результат сохранит её внешность. Упоминание не увеличивает цену. У AI-инфлюенсеров, созданных на salven.ai, тоже есть @-имена, и работают они так же.
GET /charactersвозвращает{ "characters": [ … ] }: персонажи, сначала новые, затем инфлюенсеры. У каждого естьhandle,name,kind(characterилиinfluencer),photos(сколько фото отправляет упоминание) иcreatedAt.GET /characters/{handle}возвращает{ "character": { … } }(структура показана ниже, вPOST /characters) или 404character_not_found. Здесьphotos— список, главное фото первым, у каждогоurl,widthиheight; у инфлюенсераwidthиheightравныnull.POST /charactersсоздаёт персонажа. Это бесплатно, ответ —201с тем же{ "character": { … } }.
{
"name": "Anna",
"handle": "anna",
"photos": [
"https://cdn.salven.ai/uploads/cmg1x2y3z0000abcd1234efgh/1790847000000-9f2c4e6a8b0d1f35.jpg",
"https://cdn.salven.ai/uploads/cmg1x2y3z0000abcd1234efgh/1790847060000-3b7d9e1c5a2f4860.jpg"
]
}{
"character": {
"handle": "anna",
"name": "Anna",
"kind": "character",
"photos": [
{
"url": "https://cdn.salven.ai/characters/chr_5d1f0a9c3e7b2468ace01357/4a6c8e0b2d4f6a81.jpg",
"width": 1536,
"height": 2048
},
{
"url": "https://cdn.salven.ai/characters/chr_5d1f0a9c3e7b2468ace01357/b3d5f7a9c1e30264.jpg",
"width": 1536,
"height": 2048
}
],
"createdAt": "2026-10-01T09:30:00.000Z"
}
}| Поле | Правила |
|---|---|
name | 1–60 символов в одну строку. |
handle | 2–30 символов из a-z, 0-9 и _, начинается с буквы. Уникально среди персонажей и инфлюенсеров аккаунта. Имена вроде image1, video2, audio3 и element1 зарезервированы. |
photos | 1–4 URL ваших собственных изображений (загрузки или изображения из результатов ваших генераций): PNG, JPEG или WebP, не меньше 300 пикселей по каждой стороне, стороны отличаются не больше чем в 2,5 раза, на всех один и тот же человек. Salven хранит собственные копии. |
Idempotency-Key здесь нет: ключом служит само @-имя. Создание, повторённое после потерянного ответа, получит отказ 409 handle_taken, если первое прошло, и тогда GET /characters/{handle} покажет персонажа. У аккаунта может быть до 50 персонажей (409 limit_reached). Редактирование и удаление — на странице персонажей.
Как использовать: персонажей принимает только модель, в спецификации которой characters.supported: true, не больше characters.maxCharacters за один запуск, а при characters.needsStartFrame: true нужен ещё и первый кадр. На любой другой модели упоминание вашего собственного @-имени отклоняется с character_not_supported, и ничего не списывается. @-имя, которого в аккаунте нет, — не ошибка: оно остаётся обычным текстом, и фото по нему не добавляются, поэтому берите @-имена из GET /characters и проверяйте characters готовой генерации.
Эффекты
Эффекты — это готовые стили со страницы эффектов: Salven уже задал модель, промпт и настройки, а вы даёте изображение или изображения и, для некоторых эффектов, строку текста. Запуск эффекта — обычная генерация.
GET /effects возвращает { "effects": [ … ] } в том же порядке, что на странице эффектов. GET /effects/{slug} возвращает один эффект или 404 effect_not_found:
{
"slug": "wide-angle",
"name": "Wide Angle",
"description": "…",
"kind": "image",
"category": { "slug": "objects", "name": "Objects" },
"preview": {
"videoUrl": null,
"posterUrl": "https://cdn.salven.ai/effects/cmg2a3b4c0002abcd3456qrst/preview.jpg",
"width": 1024,
"height": 1365
},
"inputs": [
{
"key": "object",
"label": "Object",
"hint": "…",
"required": true,
"exampleUrl": "https://cdn.salven.ai/effects/cmg2a3b4c0002abcd3456qrst/object.jpg"
}
],
"text": null,
"pricing": { "unit": "image", "credits": 30 },
"quality": {
"default": "1K",
"options": [
{ "value": "1K", "label": "1K", "pricing": { "unit": "image", "credits": 30 } },
{ "value": "2K", "label": "2K", "pricing": { "unit": "image", "credits": 45 } },
{ "value": "4K", "label": "4K", "pricing": { "unit": "image", "credits": 60 } }
]
},
"maxCount": 4
}| Поле | Что значит |
|---|---|
inputs | Изображения, которые принимает эффект; каждое передаётся по своему key. required говорит, какие обязательны; exampleUrl — образец. |
text | null либо текстовое поле эффекта: label, placeholder, maxLength, required. |
pricing | Эффект для изображений: credits за одно изображение. Видеоэффект: unit равен run, а durations перечисляет каждую доступную длительность с её seconds и credits. |
quality | null либо выбор, который предлагает эффект: default и options, у каждого варианта свои value, label и pricing. pricing верхнего уровня — цена варианта по умолчанию. |
maxCount | Сколько изображений может создать один запрос. |
{
"inputs": {
"object": "https://cdn.salven.ai/uploads/cmg1x2y3z0000abcd1234efgh/1790847000000-9f2c4e6a8b0d1f35.jpg"
},
"quality": "4K",
"count": 2,
"language": "en",
"expectedCost": 60,
"dryRun": false
}| Поле | Что значит |
|---|---|
inputs | Ключ входа → URL вашего собственного изображения (PNG, JPEG или WebP). |
text | Только если у эффекта есть text; обязательно, если text.required равен true. |
duration | Видеоэффекты: одно из pricing.durations[].seconds. По умолчанию первое. |
quality | Только если у эффекта есть quality: одно из quality.options[].value, в точности как написано. По умолчанию quality.default. |
count | Эффекты для изображений: от 1 до maxCount изображений, каждое оплачивается. Видеоэффект всегда делает один запуск. |
language | en или ru: язык названия эффекта, под которым запуск записывается в галерее. По умолчанию en. |
expectedCost | Необязательно. Цена одного запуска, какой её назвал пробный запрос; если цена теперь другая, ответ — 409 price_changed. |
dryRun | true: вернуть цену, ничего не запускать. |
В пробном запросе то же тело, включая изображения, а в ответе — cost (один запуск), count, total, quality и balance. На запуск приходит ответ 202, как у POST /generations, с теми же правилами Idempotency-Key; каждую генерацию опрашивайте через GET /generations/{id}. Обычный порядок: узнать цену через dryRun, затем отправить то же тело с expectedCost, равным полученному cost.
Модели
API предлагает ровно те модели, которые сейчас предлагает студия, в том же порядке. Эта таблица читается из актуального каталога. Цена указана базовая: до множителя режима и платных вариантов параметров.
Изображения
| ID модели | Название | Режимы | Базовая цена |
|---|---|---|---|
nano-banana-2 | Nano Banana 2 | generate | 30 кр. / генерация |
nano-banana-pro | Nano Banana Pro | generate | 56 кр. / генерация |
seedream-v5-pro | Seedream v5 Pro | generate | 40 кр. / генерация |
gpt-image-2-5 | GPT Image 2.5 | generate | 20 кр. / генерация |
gpt-image-2 | GPT Image 2 | generate | 60 кр. / генерация |
flux-kontext | FLUX.1 Kontext | generate | 12 кр. / генерация |
recraft-v4 | Recraft V4 | generate | 20 кр. / генерация |
seedream-4 | Seedream 4 | generate | 25 кр. / генерация |
qwen-image-3 | Qwen Image 3 | generate | 12 кр. / генерация |
ideogram-v4 | Ideogram v4 | generate | 6 кр. / генерация |
grok-imagine-2 | Grok Imagine 2 | generate | 18 кр. / генерация |
virtual-try-on | Virtual Try-On | generate, try-on | 25 кр. / генерация |
image-upscaler | Image Upscaler | generate, upscale | 12 кр. / генерация |
Видео
| ID модели | Название | Режимы | Базовая цена |
|---|---|---|---|
kling-v3-pro | Kling v3 Pro | generate | 20 кр. / секунда |
veo-3 | Google Veo 3 | generate | 600 кр. / генерация |
kling-v3-pro-motion-control | Kling v3 Pro · Motion Control | motion-control | 20 кр. / секунда |
video-upscaler | Video Upscaler · RealESRGAN | upscale | 20 кр. / секунда |
veo-3-1-fast | Google Veo 3.1 Fast | generate | 220 кр. / генерация |
hailuo-02 | MiniMax Hailuo 02 | generate | 120 кр. / генерация |
seedance-v1-pro | Seedance v1 Pro | generate | 150 кр. / генерация |
minimax-h3-max-turbo | MiniMax H3-Max Turbo | generate | 4 кр. / секунда |
minimax-h3-max | MiniMax H3-Max | generate | 12 кр. / секунда |
wan-3 | Wan 3.0 | generate | 18 кр. / секунда |
lipsync-photo | Lipsync — from a photo | lipsync | 24 кр. / секунда |
lipsync-redub | Lipsync — redub a clip | lipsync | 13 кр. / секунда |
video-to-audio | Sound for video | sound | 10 кр. / генерация |
seedance-2-5 | Seedance 2.5 | generate | 85 кр. / секунда |
Аудио
| ID модели | Название | Режимы | Базовая цена |
|---|---|---|---|
minimax-speech-hd | MiniMax Speech 2.8 HD | tts | 18 кр. / 1000 символов |
minimax-speech-turbo | MiniMax Speech 2.8 Turbo | tts | 11 кр. / 1000 символов |
gemini-3-8-flash-tts | Gemini 3.8 Flash TTS | tts | 9 кр. / 1000 символов |
gemini-3-8-flash-lite-tts | Gemini 3.8 Flash Lite TTS | tts | 6 кр. / 1000 символов |
elevenlabs-tts-v3 | ElevenLabs v3 | tts | 18 кр. / 1000 символов |
elevenlabs-tts-multilingual-v2 | ElevenLabs Multilingual v2 | tts | 18 кр. / 1000 символов |
elevenlabs-tts-turbo-v2-5 | ElevenLabs Turbo v2.5 | tts | 9 кр. / 1000 символов |
xai-tts | xAI TTS | tts | 3 кр. / 1000 символов |
inworld-tts-1-5-max | Inworld TTS 1.5 Max | tts | 2 кр. / 1000 символов |
lyria-3-pro | Lyria 3 Pro | music | 15 кр. / генерация |
stable-audio-3 | Stable Audio 3 | music | 7 кр. / генерация |
minimax-music | MiniMax Music 2.6 | music | 27 кр. / генерация |
lyria-3 | Lyria 3 | music | 8 кр. / генерация |
elevenlabs-music-v2-5 | ElevenLabs Music v2.5 | music | 108 кр. / генерация |
elevenlabs-sfx | ElevenLabs Sound Effects | sfx | 4 кр. / генерация |
sonilo-sfx | Sonilo Sound Effects | sfx | 4 кр. / генерация |
3D
| ID модели | Название | Режимы | Базовая цена |
|---|---|---|---|
tripo-h3-1 | Tripo H3.1 | image | 54 кр. / генерация |
tripo-p2 | Tripo P2 | image | 198 кр. / генерация |
hunyuan-3d-pro | Hunyuan 3D Pro | image | 68 кр. / генерация |
rodin-2-5 | Rodin 2.5 | image | 72 кр. / генерация |
trellis-2 | Trellis 2 | image | 54 кр. / генерация |
tripo-h3-1-text | Tripo H3.1 | text | 36 кр. / генерация |
rodin-2-5-text | Rodin 2.5 | text | 72 кр. / генерация |
meshy-7-views | Meshy V7 | views | 216 кр. / генерация |
meshy-rigging | Meshy Rigging | animate | 36 кр. / генерация |
meshy-rigging-multi | Meshy Rigging Multi-Animation | animate | 36 кр. / генерация |
Список меняется: модели добавляются и скрываются. Программе стоит читать GET /models, а не держать собственную копию, а цену запроса узнавать пробным запросом с тем же телом.
Промпты и seed
Пишите промпты на любом языке. По умолчанию (enhancePrompt: true) Salven переводит промпт на английский и переписывает его под модель. Промпт для изображения или видео превращается самое большее примерно в 80 английских слов; длинный подробный английский промпт остаётся почти без изменений.
enhancePrompt: false отправляет промпт в точности как написан, и негативный промпт тоже. Используйте это для тщательно составленного английского промпта, для длинного текста песни или для точной команды редактирования вроде «make the jacket red, change nothing else».
- Текст в изображении. Слова, которые должны быть написаны на изображении, берите в кавычки, например
a cake with the inscription «Happy birthday». - Речь. Модель речи (режим
tts) читает промпт дословно, на его языке; он никогда не переводится. Произносится и оплачивается весь промпт, поэтому отправляйте только те слова, которые нужно сказать. - Музыка и звуки. Промпт превращается в короткое описание на английском. Слова, которые нужно спеть или произнести, остаются как написаны и берутся в кавычки.
- 3D-модели и чистые редакторы. Промпт только переводится, без приукрашивания. Модель изображений, которая принимает референсные изображения, — не чистый редактор: её промпт переписывается, как любой другой, поэтому точную команду редактирования отправляйте на английском с
enhancePrompt: false. - Негативные промпты. Только у моделей, в спецификации которых есть
negativePrompt. По умолчанию негативный промпт переводится на английский и никогда не приукрашивается. - Длина. Держитесь в пределах
prompt.maxLengthмодели, иначе запрос отклоняется сprompt_too_long(сmaxиlength). - Персонажи.
@handleодного из ваших персонажей переживает перевод и переписывание. Любое другое@word— обычный текст. См. Персонажи.
Перевод не гарантирован: когда он не может выполниться, промпт уходит как набран. Для моделей изображений, видео и 3D надёжный выбор — английский.
Seed
Seed — это число, которое задаёт случайность модели. Тот же seed с тем же промптом, входами и параметрами даёт тот же результат снова или почти тот же, так что понравившийся результат можно повторить, изменив что-то одно.
- Seed принимает только модель, в спецификации которой
"seed": true, от 0 доseedMax. На любой другой модели он отклоняется сseed_not_supported, а вне диапазона — сinvalid_seed. - Seed никогда не меняет цену.
- Если вместе с seed передан
count, генерации получают seed, seed + 1, seed + 2 и так далее. - Для точного повтора оставьте
enhancePromptтаким же, как в первом запуске; надёжнее всегоfalse. seedготовой генерации показывает, с чем она запускалась.
Идемпотентность
Старт генерации списывает кредиты, поэтому случайно произойти дважды он не должен. Когда ответ потерян (таймаут, обрыв соединения, 5xx), нельзя понять, начался ли запуск. Заголовок Idempotency-Key делает повтор безопасным: тот же запрос с тем же ключом возвращает первый ответ, а не запускает генерацию заново.
- Отправляйте его с
POST /generationsиPOST /effects/{slug}/run. - Ключ — 1–128 символов из
A-Z,a-z,0-9,.,_,:и-; всё остальное отклоняется с 400invalid_idempotency_key. Подойдёт UUID. - Для каждой новой генерации берите новый ключ, а для каждого её повтора — тот же.
- Воспроизведённый ответ приходит с заголовком
Idempotent-Replayed: true. - Запоминается только ответ, после которого генерации начались (
202). Отказ вроде400,402 insufficient_creditsили429 too_many_runningне запоминается: когда его причина устранена, тот же запрос с тем же ключом выполнится. 202окончателен для своего ключа, включая частичный: повтор воспроизводит этот ответ и ничего не запускает.- Ключи хранятся 24 часа.
- Пробный запрос никогда не запоминается и не воспроизводится: он только называет цену.
| HTTP | Код | Что значит |
|---|---|---|
| 422 | idempotency_key_reused | Ключ уже использован с другим телом. Для другого запроса возьмите новый ключ. |
| 409 | idempotency_in_progress | Первый запрос с этим ключом ещё обрабатывается. Подождите несколько секунд и повторите его с тем же ключом. |
Если 409 приходит дольше нескольких минут, возможно, первая попытка оборвалась уже после старта. Посмотрите в GET /generations, не появились ли с тех пор запуски, и отправляйте запрос с новым ключом, только если ничего не началось.
Ошибки
У всех ошибок, которыми отвечает сам API, одна структура. Стройте обработку на code: он стабилен. message — на английском и написано для людей. Остальные поля — подробности этой ошибки. У ответа, который сформировал не сам API, такого тела нет: 413 веб-сервера на тело запроса больше 25 МБ, 502 или 504 без JSON-тела, пока сервер перезапускается, или 5xx без кода. Проверяйте Content-Type, прежде чем разбирать тело ошибки, а 5xx без кода обрабатывайте так, как сказано в последней строке таблицы.
{
"error": {
"code": "insufficient_credits",
"message": "Not enough credits for this generation",
"required": 45,
"available": 12
}
}За отклонённый запрос ничего не списывается. За генерацию, которая не удалась позже (status — failed), кредиты возвращаются.
| HTTP | Код | Что делать |
|---|---|---|
| 400 | invalid_json, invalid_request, invalid_prompt, invalid_params, invalid_inputs | Исправьте тело; сообщение называет поле. |
| 400 | invalid_idempotency_key | Исправьте заголовок. См. Идемпотентность. |
| 400 | invalid_kind, invalid_cursor | Исправьте параметры запроса GET /models или GET /generations. |
| 400 | unknown_param, invalid_param_value, unknown_input, input_kind, input_missing, input_requires, input_takes_one_file, invalid_input_url, too_many_files, aspect_not_supported, invalid_aspect, prompt_required, negative_prompt_not_supported, count_too_large | Перечитайте спецификацию модели. Подробности говорят, что разрешено: allowed, expected, max, input, param. |
| 400 | input_not_allowed | URL — не ваш собственный файл. Сначала загрузите его через POST /uploads. |
| 400 | prompt_too_long, negative_prompt_too_long | Сократите текст до max символов. |
| 400 | seed_not_supported, invalid_seed | Не передавайте seed для этой модели либо держите его в пределах 0..seedMax. |
| 400 | media_missing, media_unreadable, media_foreign, reference_* | Входной файл не читается или нарушает ограничения модели. |
| 400 | model_too_dense, model_untextured, model_unreadable, model_foreign, model_missing | Для 3D-модели, которую нужно анимировать, нельзя сделать риг: слишком много граней, нет текстуры, GLB не читается или файл не ваш. |
| 400 | character_not_supported, character_too_many, character_needs_start_frame, character_with_frame, character_with_extend, character_no_room, character_no_photos | Промпт называет ваших персонажей, а запрос не может с ними выполниться. Выберите модель, спецификация которой принимает персонажей, назовите меньше @-имён или следуйте сообщению. См. Персонажи. |
| 400 | name_invalid, handle_invalid, handle_reserved, photos_required, too_many_photos, photo_foreign, photo_unreadable, photo_too_small, photo_aspect | POST /characters: исправьте поле. Отказ из-за фото называет это фото (index, photo). |
| 400 | text_required, text_too_long, duration_value, quality_value | Эффекты: перечитайте спецификацию эффекта. |
| 400, 413, 415 | missing_file, invalid_form_data, file_too_large, unsupported_format, heic_not_supported | Загрузки: см. правила загрузки. |
| 401 | unauthorized | Ключа нет, он неверный или отозван. |
| 402 | insufficient_credits | Сравните required и available. Пополните баланс на salven.ai; не повторяйте запрос. |
| 403 | api_not_available | API недоступен для этого аккаунта. |
| 404 | model_not_found, mode_not_available, not_found, effect_not_found, character_not_found | Сверьте ID с соответствующим списком: GET /models, GET /generations, GET /effects, GET /characters. Подробности mode_not_available перечисляют modes модели. |
| 409 | handle_taken, limit_reached | POST /characters: @-имя занято (или это был повтор, а первое создание прошло) либо у аккаунта уже столько персонажей, сколько разрешено. |
| 409 | price_changed | Эффекты: цена изменилась с тех пор, как её назвал пробный запрос. cost — цена одного запуска сейчас. Запросите цену заново. |
| 409 | idempotency_in_progress | Подождите несколько секунд и повторите с тем же ключом. См. Идемпотентность. |
| 422 | idempotency_key_reused | Для другого запроса возьмите новый Idempotency-Key. |
| 429 | rate_limited | Подождите retryAfter секунд; то же говорит заголовок Retry-After. |
| 429 | too_many_running | Сейчас в очереди или в работе running ваших запусков через API, и этот запрос превысил бы предел. Дождитесь, пока часть завершится, и повторите его; тот же Idempotency-Key подойдёт. |
| 5xx | internal_error, storage_error, uploads_disabled или вообще без кода | Повторите один раз после короткой паузы, с тем же Idempotency-Key. Перед повтором дорогого запроса посмотрите в GET /generations, нет ли запуска за последнюю минуту. 502 или 504, тело которых не JSON, приходят от веб-сервера во время перезапуска и могли оборвать запрос на полпути: тогда повтор с тем же ключом вернёт первый результат или 409. |
Полный список со всеми полями подробностей — в SKILL.md.
Лимиты
| Что | Лимит |
|---|---|
| Запросы | 120 в минуту на ключ, включая чтение |
Запросы на генерацию: POST /generations и POST /effects/{slug}/run, включая пробные запросы | 30 в минуту на аккаунт |
| Загрузки | 30 в минуту на аккаунт, POST /uploads и MCP-инструмент upload_from_url вместе |
| Создание персонажей | 20 в минуту на аккаунт |
| Запуски через API, которые одновременно стоят в очереди или создаются | 10 на аккаунт, генерации и эффекты вместе |
| Тело JSON | 64 КБ |
wait | До 60 секунд |
limit у GET /generations | По умолчанию 20, до 50 |
count | До maxCount модели |
| Промпт | prompt.maxLength модели, но не больше 10 000 символов; негативный промпт — не больше 4000 |
Размер файла, который вы отправляете сами: POST /uploads, ссылка для загрузки | Изображения 10 МБ, аудио 20 МБ, видео и модели GLB 25 МБ |
Размер файла, скопированного с веб-адреса: MCP-инструмент upload_from_url | Изображения 10 МБ, аудио 20 МБ, видео и модели GLB 60 МБ |
| Ключи идемпотентности | Хранятся 24 часа |
| API-ключи | 10 действующих ключей на аккаунт |
| Персонажи | 50 на аккаунт, у каждого 1–4 фото |
В REST при превышении лимита частоты приходит 429 rate_limited с retryAfter в секундах и заголовком Retry-After. Запрос, чей count вывел бы число открытых запусков за 10, получает 429 too_many_running с running — числом открытых сейчас.
Для агентов: SKILL.md
SKILL.md — файл с инструкцией для агентов, которые пишут код, таких как Claude Code и Cursor. Это полный справочник по API, написанный для чтения агентом: все эндпоинты и коды отказов, правила для референсных медиафайлов, речи и риггинга 3D-моделей и то, как вести себя с чужими кредитами (сначала назвать цену, не зацикливаться на неудачах, никогда не выводить ключ).
- Скачать: salven.ai/api/skill
- Читать в браузере: salven.ai/api/skill?view=1
- Создайте ключ в Настройках и задайте его как
SALVEN_API_KEYв окружении, где работает агент. - Дайте агенту файл. В Claude Code сохраните его как
.claude/skills/salven-api/SKILL.mdв проекте или как~/.claude/skills/salven-api/SKILL.mdдля всех проектов. Другому агенту добавьте файл в его инструкции или укажите адрес выше. - Попросите то, что вам нужно. Агент прочитает каталог, назовёт цену и выполнит запрос.
Агенту, который умеет работать с MCP, не нужны ни файл, ни ключ: вместо этого добавьте сервер из раздела Подключить Claude.
Помощник на Python
SKILL.md заканчивается небольшим клиентом на Python: он узнаёт цену, спрашивает согласия, запускает генерацию с Idempotency-Key, ждёт и повторяет запрос при лимитах частоты, занятых слотах и перезапусках. Ему нужен пакет requests.
import os, time, uuid, requests
API = "https://salven.ai/api/v1"
H = {"Authorization": f"Bearer {os.environ['SALVEN_API_KEY']}"}
def upload(path):
with open(path, "rb") as f:
r = requests.post(f"{API}/uploads", headers=H, files={"file": f})
r.raise_for_status()
return r.json()["url"]
class StillInProgress(RuntimeError):
"""The request may have started: check GET /generations before sending it under a new key."""
def call(method, url, **kw):
"""One request, retried through rate limits, busy slots, restarts and one server error.
Every retry sends the same arguments, so a POST keeps its Idempotency-Key."""
busy_since, server_errors = None, 0
for attempt in range(80):
try:
r = requests.request(method, url, timeout=(10, 150), **kw)
except (requests.ConnectionError, requests.Timeout, requests.exceptions.ChunkedEncodingError):
time.sleep(10) # a restart, a dropped link: same request again
continue
if r.status_code == 409: # idempotency_in_progress
busy_since = busy_since or time.time()
if time.time() - busy_since > 300:
raise StillInProgress(url)
time.sleep(10)
continue
if r.status_code == 429:
code = r.json().get("error", {}).get("code") if "json" in r.headers.get("content-type", "") else None
time.sleep(60 if code == "too_many_running" else int(r.headers.get("Retry-After", 10)))
continue
if r.status_code >= 500 and server_errors == 0:
server_errors += 1
time.sleep(10)
continue
r.raise_for_status()
return r.json()
raise RuntimeError(f"{method} {url}: gave up")
MAX_WAIT_MIN = {"image": 5, "audio": 10, "video": 20, "3d": 60} # past this a run counts as stuck
def generate(body, confirm):
"""Quote, ask, run, wait. Returns the output URLs and what did not finish."""
quote = call("POST", f"{API}/generations", headers=H, json={**body, "dryRun": True})
if quote["total"] > quote["balance"]:
raise RuntimeError(f"needs {quote['total']} credits, the balance is {quote['balance']}")
if not confirm(quote["total"]): # total = cost × count
return None
kind = call("GET", f"{API}/models/{body['model']}", headers=H)["kind"]
key = str(uuid.uuid4()) # one key for this request and all its retries
started = call("POST", f"{API}/generations", headers={**H, "Idempotency-Key": key}, json=body)
result = {"urls": [], "failed": [], "stuck": [], "stopped": (started.get("stopped") or {}).get("code")}
deadline = time.time() + MAX_WAIT_MIN[kind] * 60
for gen in started["generations"]:
while gen["status"] in ("queued", "processing") and time.time() < deadline:
gen = call("GET", f"{API}/generations/{gen['id']}", headers=H, params={"wait": 60})
if gen["status"] == "done":
result["urls"] += [o["url"] for o in gen["outputs"]]
elif gen["status"] == "failed":
result["failed"].append(gen["id"]) # its credits were returned
else:
result["stuck"].append(gen["id"]) # tell the user; do not start it again
return result # "stopped": why fewer than `count` started, if so
start = upload("portrait.jpg")
print(generate({"model": "kling-v3-pro", "prompt": "she laughs and looks at the camera",
"params": {"duration": 5}, "inputs": {"startImage": start}},
confirm=lambda credits: input(f"Spend {credits} credits? [y/N] ").strip().lower() == "y"))Полезно знать
Через API нельзя сделать следующее; это делается на salven.ai:
- публикация работы в сообществе;
- удаление работ;
- редактирование и удаление персонажей (страница персонажей);
- создание AI-инфлюенсеров (существующий используется по своему
@handle, как персонаж); - пополнение баланса;
- мультишот-ролики Kling.
URL результатов невозможно угадать, но они публичны. Файл откроет любой, у кого есть ссылка, поэтому делитесь ею только с теми, кому его стоит видеть.
Персонажи реальных людей. Создавайте персонажа только из своих фото или фото людей, которые на это согласились, и никогда не используйте его, чтобы создать впечатление, будто реальный человек сказал или сделал то, чего не говорил и не делал.
Вопросы и проблемы: Поддержка или письмо на support@salven.ai.