Руководство: генерация и редактирование изображений
Как ведут себя эндпоинты изображений как класс — две формы запроса, три способа записать размер вывода, что референсная картинка добавляет к счёту, веса адаптеров и как читать отказ.
Это руководство — о классе эндпоинтов изображений, а не об отдельной модели. Оно объясняет, что у них одинаково, и — что полезнее — те места, где они расходятся настолько, что тело запроса, написанное для одного, на следующем уже не пройдёт.
Таблицы параметров здесь намеренно нет. У параметров ровно один источник: inputSchema, который живой каталог возвращает для той самой модели, которую вы вызываете. Эта страница объясняет, как читать эту схему и о чём она умалчивает.
| Если вам нужно | Читайте вместо этого |
|---|---|
| Первое изображение целиком: от ключа и баланса до файла | Быстрый старт |
| Тот же путь для видео, по шагам | Учебник: превратить фотографию в видео |
| Все поля конкретной модели | Её inputSchema — см. Каталог моделей и схемы |
| Перенести лицо или голову с одной фотографии на другую | Подмена объекта |
MODEL_ID_FROM_CATALOG — это заполнитель
Во всех примерах ниже MODEL_ID_FROM_CATALOG стоит там, где должен быть настоящий идентификатор модели. Возьмите его из аутентифицированного живого каталога — GET /api/v1/models?modality=image — и требуйте одновременно enabled: true и available: true, прежде чем что-то тратить. Чтение каталога бесплатно. Отправка самого заполнителя вернёт лишь ошибку «not found».
Две формы запроса
Работа с изображениями приходит в виде двух задач, и каталог их разделяет:
- Генерирующие эндпоинты заканчиваются на
/text-to-image. Вы отправляете слова и получаете картинку. - Редактирующие эндпоинты заканчиваются на
/edit. Вдобавок вы отправляете хотя бы одну картинку.
У редактирования два допустимых написания, и оба стоит знать до того, как фильтровать каталог: эндпоинты публикуются с суффиксом edit, тогда как массив tasks классифицирует их как image-to-image. Параметр запроса task принимает любое из двух. См. Каталог моделей и схемы.
Обе формы используют ту же оболочку создания, тот же жизненный цикл задачи и тот же формат результата, что и любая другая медиамодель. Меняется только input.
prompt обязателен в обеих формах
Соблазнительно решить, что редактирующему эндпоинту достаточно одной картинки и prompt там необязателен. На эндпоинтах, которые вообще принимают подсказку, это не так: prompt входит в корневой список required у обеих форм, и его пропуск отклоняется ещё до того, как что-то будет списано.
{
"model": "MODEL_ID_FROM_CATALOG",
"input": {
"image_urls": ["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]
}
}prompt: is requiredКартинка говорит, от чего вы отталкиваетесь; подсказка — что с этим сделать. Редактирующему эндпоинту, не получившему указания, не над чем работать.
Исключение одно, и это целое семейство, а не случайный эндпоинт: некоторые редактирующие эндпоинты не принимают prompt вовсе и берут только картинки, потому что операция задана самим эндпоинтом. Они относятся к набору инструментов подмены объекта и несут в каталоге поле toolkit, по которому их можно отличить; см. Подмена объекта. Для них prompt: is not a supported field — правильный ответ, а не сбой.
У поля входной картинки три имени
Какое использует эндпоинт — часть его схемы, и из вида задачи это не выводится:
| Поле | Форма | Что обычно означает |
|---|---|---|
image_url | одна ссылка на изображение | Есть ровно одна картинка-объект, и понятия второй у эндпоинта нет |
image_urls | массив ссылок на изображения | Картинки — это сам материал, который редактируют или совмещают, в том порядке, на который ссылается указание |
reference_image_urls | массив ссылок на изображения | Картинки направляют генерацию, которая иначе была бы чисто текстовой: это ориентир, а не объект |
Удивляет именно reference_image_urls: оно встречается на генерирующих эндпоинтах, так что эндпоинт /text-to-image вполне может принимать картинки. Не считайте, что «text-to-image» означает «в запросе изображений нет».
Ссылка на изображение — это публичный HTTPS-адрес, подтверждённый после загрузки URI spicy:// или небольшой URI data:image/…;base64,…. О загрузках см. Медиа, о канонических именах полей — контракт Spicy Schema.
Границы массивов разнятся сильнее, чем кажется
У перечисленных массивов есть minItems и maxItems, сервер соблюдает и то и другое, а числа различаются от эндпоинта к эндпоинту: сегодня диапазон — от массива, принимающего ровно один элемент, до массивов на десять. Эндпоинт того же семейства с тем же именем поля может иметь другой потолок.
image_urls: must contain at least 1 items
image_urls: must contain at most 3 itemsЧитайте minItems и maxItems из схемы той модели, которую вызываете. Таблица параметров, где массив показан голым array без формы элемента и без ограничения длины, — это ровно та строка, с которой читателю нечего делать; именно поэтому такой таблицы на этом сайте нет.
Размер вывода записывается тремя способами
Это первое, обо что спотыкаются, направляя готовое тело запроса на второй эндпоинт.
В каталоге изображений в ходу три написания, и эндпоинт принимает ровно одно из них:
| Написание | Поля | Примечания |
|---|---|---|
| Раздельное | aspect_ratio и resolution | Форма кадра и ступень разрешения выбираются по отдельности |
| Предустановка | size | Одно значение перечисления задаёт сразу оба |
| Явное | width и height | Целые пиксели, у каждого свои minimum и maximum |
На новых эндпоинтах чаще всего встречается раздельное написание, и оно же самое полезное из трёх: aspect_ratio компонует кадр до генерации, а не обрезает после, так что форму можно менять, не трогая бюджет пикселей.
Поле из чужого написания отклоняется, а не игнорируется
В каждой inputSchema изображения стоит additionalProperties: false. Поле, которого у эндпоинта нет в списке, отвергается: тело запроса, перенесённое с другого эндпоинта, падает целиком, а не откатывается молча к значению по умолчанию.
Падает в обе стороны, и сообщение называет поле:
size: is not a supported field
aspect_ratio: is not a supported fieldНи то ни другое не тарифицируется. Цена здесь не денежная, а в самом допущении: нельзя написать один объект input и направить его сразу на несколько эндпоинтов изображений, и «совместимого» написания, которое приняли бы все, не существует.
Какое из них выбирает ценовую ступень
На эндпоинте с раздельным написанием эти два поля обычно играют в счёте разные роли:
resolutionобычно выбирает ценовую ступень. В расчёте стоимости оно проявляется какvariant, и его изменение меняет сумму.aspect_ratioобычно нет. Перекадрировать — не то же самое, что масштабировать.
Слово «обычно» в этих двух фразах работает по-настоящему. Машиночитаемый ответ — на самом поле: x-ui.affects_price: true и, если эндпоинт это объявляет, членство в x-pricing.variantFields. Сверяйтесь со схемой, а не с привычкой. См. Как читать цены и справочник аннотаций в контракте Spicy Schema.
Допустимые значения тоже свои у каждого эндпоинта. Отказ их перечисляет — бесплатный способ их увидеть:
aspect_ratio: must be one of: "1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "21:9"Удобно при изучении, но чтение enum это не заменяет: в схеме есть ещё и default, который применяется, если поле опустить.
Что референсная картинка добавляет к счёту
Это тихая статья расхода, и она заслуживает отдельного раздела, потому что в ответе на неё ничто не указывает.
Обычная арифметика медиамодели — цена × количество, где количество берётся из значения quantityField в вашем вводе, либо равно одной единице, если quantityField пуст. На эндпоинтах изображений quantityField чаще всего num_outputs, и ведёт он себя ровно так, как ожидается: попросите четыре картинки — и в расчёте quantity покажет 4. Это честное измерение: его видно.
Некоторые эндпоинты вдобавок берут отдельную плату за каждую входную картинку, и вот её движения не видно. Запросите расчёт для одного и того же эндпоинта дважды — с единственным референсом и с полным массивом — и получите:
- тот же
unit; - то же
quantity; - тот же
variant; - другую сумму.
Три из четырёх полей, по которым обычно собирают оценку, остаются ровно на месте. Оценка, посчитанная как цена × количество, занижает итог, и ничто в ответе об этом не сигналит.
Машиночитаемая примета
Поле, помеченное x-ui.affects_price: true, которое не является quantityField и не перечислено в x-pricing.variantFields, двигает сумму, не выбирая ступень. Для такого эндпоинта читайте pricing каталога как нижнюю границу, а сумму берите из расчёта стоимости.
Это то же правило, что сформулировано в разделе Каталог моделей и схемы, и именно при редактировании изображений вы столкнётесь с ним чаще всего: массив входных картинок нередко помечен так.
Насколько именно вырастет сумма, зависит от эндпоинта и не подчиняется постоянной пропорции. Сегодня в каталоге есть эндпоинты, где заполненный массив стоит в несколько раз дороже одного референса, и эндпоинты, где дополнительные референсы не стоят ничего, — и отличить их можно только спросив. Поэтому не оценивайте цену массива по соседнему эндпоинту и не выводите стоимость полного массива из расчёта с одним референсом.
Считайте стоимость того тела, которое реально отправите
curl -sS https://api.spicyapi.ai/api/v1/jobs/quote \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"MODEL_ID_FROM_CATALOG","input":{"prompt":"your instruction","image_urls":["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]}}'Расчёт стоимости выполняет ту же проверку, что и создание задачи, ничего не стоит, ничего не запускает и ничего не резервирует. Он возвращает estimatedCost, maxCharge, quantity и unit для этого самого тела. Изменили массив — запросите расчёт заново. Подробности и порядок действий при потерянном ответе — в разделе Расчёт стоимости и совместимость; где расчёт уместен в реальной интеграции — в разделе Внедрение в продакшене.
Веса адаптеров
Некоторые эндпоинты позволяют наложить поверх базовой модели ваши собственные дообученные веса. Каноническое поле — loras, либо high_noise_loras и low_noise_loras, если эндпоинт выставляет свои стадии по отдельности.
Массив объектов со своими правилами внутри
Эти поля несут x-ui.widget: "object-list": массив, у которого items — полноценная схема объекта сама по себе. У неё свой required, свой additionalProperties: false и свои тип, диапазон и default для каждого подполя. Типичный элемент называет один набор весов и силу его применения.
{
"model": "MODEL_ID_FROM_CATALOG",
"input": {
"prompt": "your scene",
"loras": [
{ "path": "https://weights.example.com/style.safetensors", "scale": 1 },
{ "path": "https://weights.example.com/character.safetensors", "scale": 0.6 }
]
}
}Форма элемента не одинакова на всех эндпоинтах. Что считается файлом весов — прямой адрес или ключ из встроенной библиотеки — сужает собственная подсхема items конкретного эндпоинта; то же касается имён подполей, числа элементов и того, какие из них обязательны. Читайте items, а не этот пример. Виджет описан в контракте Spicy Schema; как превратить его во вложенную таблицу — в разделе Каталог моделей и схемы.
Одно правило внутри этих элементов легко прочесть неверно: значения по умолчанию достают до элементов массива. Подполе, которое одновременно есть в items.required и имеет default, фактически необязательно: опустите его — и значение по умолчанию подставится до проверки. Подполе без значения по умолчанию при отсутствии отклоняется. То есть items.required не означает «должно присутствовать в том JSON, который я отправляю».
Откуда берутся веса
В path можно указать одно из трёх:
- Публичную ссылку на файл
.safetensorsили, если эндпоинт это допускает, id репозитория Hugging Face видаowner/repo. Модель скачивает файл в момент генерации, поэтому первый вызов с новыми весами заметно медленнее последующих. - Загрузку. Загрузите файл как
application/octet-streamчерез прямую загрузку, до 2 GiB, и используйте возвращённый URIspicy://f/…. На этапе commit проверяется, что это полный файл safetensors. - Импорт. Для файла, который требует входа, например из закрытого или gated-репозитория Hugging Face либо файла Civitai за ключом API, отправьте ссылку и токен доступа на
POST /api/v1/files/import. SpicyAPI скачивает файл один раз и возвращает URIspicy://f/…. Токен используется только для этой загрузки и нигде не сохраняется.
curl -sS https://api.spicyapi.ai/api/v1/files/import \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
--data "$(jq -n --arg url "https://huggingface.co/OWNER/REPO/blob/main/your_lora.safetensors" --arg token "$HF_TOKEN" '{url: $url, token: $token}')"url может быть и id репозитория (OWNER/REPO), и страницей репозитория, если в нём ровно один файл .safetensors. Ответ имеет ту же форму, что и ответ commit; подставьте data.uri в loras[].path. Загруженные и импортированные веса хранятся один день, как и любая другая загрузка, и никогда не доступны другим аккаунтам. После этого загрузите или импортируйте их заново.
При отправке проверяется ссылка, а не веса внутри
Перед созданием задачи каждая ссылка на веса пробуется. Ссылка, которая требует входа, отвечает 404 или открывает веб-страницу, отклоняется с ошибкой 400, ничего не списывается, а в сообщении указан элемент. Всё остальное проходит, поэтому хост, который позже упадёт, или веса, которые не удастся загрузить, всё равно придут к вам как сбой задачи.
Проблемы, которые проба увидеть не может, приходят позже — сбоем задачи с errorCode из девяти публичных классов платформы и сообщением о том, что пошло не так; сумма, удержанная под задачу, возвращается. См. Ошибки.
Худший случай ещё тише: веса, собранные под другую базовую модель, могут загрузиться без ошибки и почти не изменить результат. Читать нечего — есть только картинка, похожая на результат базовой модели.
Значит, проверяйте на этапе развёртывания
Вынесите проверку из пути запроса в то, что выполняется при выкладке:
- Достижимость. Запрос
HEADк каждому адресу весов, который собираетесь вывести в продакшен. Платформа пробует каждую ссылку при создании задачи, но ссылка, работающая сегодня, может перестать работать на следующей неделе, а проверка при развёртывании заметит это раньше, чем ваш трафик. - Действие. Одна генерация на каждый новый файл весов, сравниваемая с тем же промптом и тем же зерном без него. Если различить их нельзя, файл делает не то, что вы думаете, — убедитесь в этом до того, как от него будет зависеть пакет.
- Устойчивость. Ваши адреса должны работать всё то время, пока они в продакшене. Исчезнувший файл весов превращается в сбои задач без очевидной причины — через несколько дней после выкладки, на которую их и спишут.
Советы по этим полям, привязанные к конкретному эндпоинту, — относительно чего на самом деле меряется значение, какая настройка заметно замедляет вызов, какие входные данные схема принимает, но вы о них пожалеете — живут в paramNotes публичного каталога, с ключом по имени параметра и на всех десяти языках документации. Это проза, а не правило; что именно принимается, по-прежнему решает inputSchema. См. Каталог моделей и схемы.
Асинхронно, как и любая медиамодель
Эндпоинты изображений — это задачи, а не вызовы «запрос-ответ». Вы создаёте задачу, затем забираете результат опросом или через вебхук; queued и running — нетерминальные состояния, succeeded, failed и expired — терминальные. Первоисточник по состояниям — Асинхронные задачи.
Изображения готовятся быстрее видео, и это соблазняет написать вызов так, будто он синхронный. Не поддавайтесь: ограниченный опрос здесь достаточно короток, чтобы ощущаться синхронным, но коду всё равно придётся обработать задачу, которая ещё выполняется, когда бюджет ожидания исчерпан, — а передать это вебхуку масштабируется лучше, чем держать запрос открытым. Опрос использует отдельный лимит чтения аккаунта и не расходует квоту отправки задач. Слишком частый опрос может ограничить запросы состояния; см. Ограничения частоты.
Чтение результата
Успешная задача перечисляет свои файлы в output.assets[]. Три вещи об этом списке на эндпоинтах изображений:
- В нём может быть больше одного файла. Если
num_outputsбольше единицы, на каждую картинку приходится свой ресурс. Пишите обработчик циклом с самого начала: обработчик, читающийassets[0], молча выбрасывает всё остальное, за что вы заплатили. - Расширение определяет
mime, а не имя эндпоинта и не подсказка. Если эндпоинт выставляетoutput_format, кодировку выбирает это поле; если нет — кодировка своя у эндпоинта, иmimeостаётся единственным способом узнать, что вы получили. - У файла, который ещё копируется, стоит
pending: trueи нетurl. Результаты копируются в собственное хранилище SpicyAPI прежде чем быть выданными, и это занимает несколько секунд после того, как задача сообщила об успехе. Перечитайте задачу, а не считайте отсутствие ссылки сбоем.
Ссылки на скачивание живут около двадцати минут и не требуют API-ключа; за новой просто перечитайте задачу. Сгенерированные файлы хранятся не дольше четырнадцати дней, меньше — если срок хранения вашей учётной записи короче. См. Результаты и скачивание и Хранение и уничтожение.
Как читать отказ
Отклонённый запрос ничего не стоит и ничего не создаёт, поэтому эндпоинт расчёта стоимости заодно служит бесплатным валидатором для тела, которое вы собираетесь отправить.
Сообщаются все проблемы, а не только первая. В сообщении они соединены через ; , так что один отклонённый расчёт может перечислить всё, что не так с телом:
prompt: is required; loras.0.path: is required; loras.0.scale: must be at most 4; bogus: is not a supported fieldПроблема внутри массива указывает на конкретный элемент. Указатель имеет вид поле.индекс.подполе, индекс отсчитывается с нуля:
loras.1.scale: must be at most 4Передайте этот указатель тому, кто будет исправлять. Когда элементов несколько, знание того, какой именно вышел за границы, — это разница между правкой в одну строку и бинарным поиском.
Формы, которые вам встретятся:
| Сообщение | Что значит | Что делать |
|---|---|---|
X: is required | X есть в корневом списке required | Добавить. На редактирующих эндпоинтах это обычно prompt или поле картинки |
X: is not a supported field | X нет в properties; неизвестные поля отклоняются, а не игнорируются | Убрать или взять написание этого эндпоинта — см. Размер вывода |
X: must be one of: … | Значение вне enum | Сообщение перечисляет допустимые значения; enum и default есть в схеме |
X: must contain at least N items | Ниже minItems | Массив, которого эндпоинт требует, нельзя отправить пустым |
X: must contain at most N items | Выше maxItems | Прочитайте потолок именно этого эндпоинта; он не везде одинаков |
X: must be an array | Значение не того типа, что объявлен | Обернуть одиночный элемент в список |
X: must be at most N characters | Выше maxLength | У подсказок тоже есть свой потолок на каждом эндпоинте |
X.<n>.<sub>: is required | Подполе элемента <n> обязательно и не имеет значения по умолчанию | Добавить только в этот элемент |
X.<n>.<sub>: is not a supported field | В схеме элемента стоит additionalProperties: false | Убрать этот ключ из этого элемента |
X.<n>.<sub>: must be at most N | Подполе вышло за собственный диапазон | Ограничить; диапазоны заданы на подполе, а не на массив |
Если отказ приходит там, где вы ждали успеха, обычная причина — устаревшая схема. Перечитайте каталог после 400 и никогда не держите постоянный снимок — Кэширование каталога объясняет, сколько живёт копия. Остальное разбирает Диагностика.
Что дальше
Каталог моделей и схемы
Живой каталог, ценовые ступени и собственная таблица параметров из inputSchema
Контракт Spicy Schema
Канонические имена полей, аннотации x-ui, условные поля и формат результата
Медиа
Загрузить картинку и превратить её в ссылку spicy://
Расчёт стоимости и совместимость
Узнать цену конкретного запроса до оплаты
Подмена объекта
Редактирующие эндпоинты, которые берут картинки и никакой подсказки
Учебник: превратить фотографию в видео
Тот же путь для видео: CLI, SDK и чистый HTTP
Ошибки
Все коды ошибок и то, где повтор действительно помогает
Внедрение в продакшене
Что добавить, прежде чем от этого начнут зависеть реальные пользователи
Пошаговое руководство «Изображение в видео»
Начните с одного изображения на своём компьютере и закончите скачанным видеофайлом. На каждом шаге показаны CLI, TypeScript SDK и обычный HTTP.
Замена человека в кадре
Один набор инструментов, две формы. Модели с одним вызовом меняют лицо или голову за один запрос; модели с двумя вызовами сначала разбирают клип, чтобы вы выбрали, кого менять.

