spicyapiДокументация
Разработка с API
Основное содержимое

Каталог моделей и схемы

Как выбирать доступные модели по актуальному авторизованному каталогу, читать цены и строить запросы на основе текущей JSON Schema.

createTask.model принимает точное значение model из авторизованного ответа GET /api/v1/models. Формат input определяет текущая inputSchema той же записи. Именно этот ответ во время выполнения — единственный достоверный источник ID, доступных для вызова.

Не копируйте ID со статической страницы

Снимки каталога при запуске, страницы семейств и каталоги разработчиков моделей помогают находить модели. Они могут включать запланированные, отключённые или переименованные записи и никогда не должны заменять пустой актуальный каталог. Перед платной работой запросите авторизованный каталог и проверьте одновременно enabled: true и available: true.

В примерах этой страницы MODEL_ID_FROM_CATALOG — заполнитель, а не настоящая модель: замените его значением model из своего актуального каталога. Чтение каталога бесплатно.

Выберите модель вместе с проверенными входными данными

Схемы запросов различаются между моделями, поэтому для надёжного запуска выбирайте одновременно доступную модель и один из её примеров, проверенных сервером:

const response = await fetch(
  'https://api.spicyapi.ai/api/v1/models?includeSchema=1&includeExamples=1',
  { headers: { Authorization: `Bearer ${process.env.SPICY_API_KEY}` } },
);
const envelope = await response.json();
if (!response.ok || envelope.code !== 200) throw new Error(envelope.msg);

const selected = envelope.data.items.find(
  (item) => item.enabled && item.available && item.examples?.length,
);
if (!selected) throw new Error('No callable model with a validated example');

const createTaskBody = {
  model: selected.model,
  input: selected.examples[0].input,
};

Каждый пример — полный input, который сервер уже проверил по текущей схеме, поэтому его можно отправлять без изменений.

Читайте каталог программно

GET /api/v1/models
GET /api/v1/models/{model}
curl "https://api.spicyapi.ai/api/v1/models?modality=video" \
  -H "Authorization: Bearer $SPICY_API_KEY"

Список не разделён на страницы: один ответ содержит все видимые вашему ключу модели, соответствующие фильтрам. Все параметры необязательны.

ПараметрЗначение
modalityimage, video, audio или text
providerРазработчик модели, с точным совпадением и учётом регистра, например ByteDance. Копируйте из provider записи
taskОдин тип задачи, например text-to-image, edit (редактирование изображений), image-to-video, text-to-video, reference-to-video, speech-to-text, video-analyze, video-edit, video-extend, character-animation или chat. Текущий набор смотрите в tasks каталога. Для редактирования изображений допустимы два написания: модели публикуются как .../edit, но tasks относит их к image-to-image; фильтр принимает оба варианта
searchТекст без учёта регистра, встречающийся в любой части ID модели или отображаемого названия; до 128 символов
includeSchematrue или 1 добавляет inputSchema каждой записи
includeExamplestrue или 1 добавляет examples каждой записи, проверенные по текущей схеме

Эндпоинт одной модели, GET /api/v1/models/{model}, всегда включает inputSchema и examples; includeSchema и includeExamples имеют значение только для списка. Косые черты в ID можно оставлять как есть; кодируйте их через процентное представление только если HTTP-клиент считает параметр пути одним сегментом. Несуществующая или сейчас не предлагаемая модель возвращает 404.

Сокращённый ответ со списком:

{
  "code": 200,
  "msg": "success",
  "data": {
    "total": 1,
    "items": [
      {
        "model": "MODEL_ID_FROM_CATALOG",
        "family": "…",
        "displayName": "…",
        "provider": "…",
        "modality": "video",
        "tasks": ["image-to-video"],
        "async": true,
        "enabled": true,
        "available": true,
        "availability": "available",
        "taskTimeoutSeconds": 1200,
        "maxOutputDurationSeconds": 10,
        "quantityField": "duration_seconds",
        "pricing": [
          { "variant": "generate_audio=false;resolution=720p", "unit": "per_second", "price": "0.05", "currency": "USD" },
          { "variant": "generate_audio=true;resolution=720p", "unit": "per_second", "price": "0.08", "currency": "USD" }
        ],
        "startingPrice": { "variant": "generate_audio=false;resolution=720p", "unit": "per_second", "price": "0.05", "currency": "USD" },
        "badges": ["audio_output"],
        "relatedModels": [],
        "version": "v1",
        "updatedAt": "2026-09-16T16:47:57Z"
      }
    ]
  },
  "request_id": "req_01k3m8x9q2z4v7n5p6r8s0t1w2"
}

Цены выше приведены для иллюстрации. inputSchema и examples появляются в элементах списка только по запросу.

Поля, которые следует использовать клиентам

ПолеНазначение
modelТочное значение для createTask.model, в формате publisher/model/task
familyСтабильный идентификатор семейства продукта для группировки связанных эндпоинтов
displayNameПонятное человеку название эндпоинта
providerРазработчик модели, например создавшая её лаборатория
modality / tasksВид медиа, к которому эндпоинт относится в каталоге, и выполняемая задача. modality определяет просмотр и фильтр modality, но не гарантирует тип результата: эндпоинт speech-to-text относится к audio, а возвращает текст. См. Формат результата
toolkitСемейство инструментов, к которому относится эндпоинт; сейчас только subject-swap. Поле отсутствует, если модель не относится ни к одному набору, поэтому проверяйте значение, а не наличие ключа. Как и badges, это классификация, которая не влияет на принятие запроса, цену или валидацию. См. Замена персонажа
asynctrue: создайте задачу и получите результат опросом или вебхуком. false: ответ возвращается непосредственно в ответе на запрос
inputSchemaJSON Schema 2020-12 для построения форм и валидации запросов
examplesВходные данные, проверенные по текущей схеме; в списке выдаются только по запросу
enabledМожно ли вызвать запись сейчас. Каталог также перечисляет опубликованные, но ещё не открытые записи и записи, приостановленные платформой; у них возвращается false. Проверяйте вместе с available перед отправкой задачи
availableМожно ли вызвать модель сейчас. false означает, что вызов завершится ошибкой, например из-за отсутствия текущей цены
availabilityЗаявленный этап: planned, available, preview (поля и результаты ещё могут меняться) или maintenance (временная неисправность с ожидаемым восстановлением)
pricing / startingPriceВаши цены; см. Как читать цены
quantityFieldПоле входных данных, значение которого задаёт оплачиваемое количество; для видео обычно duration_seconds. Пустое значение означает одну единицу на вызов. Всегда читайте его из каталога
taskTimeoutSecondsКрайний срок выполнения одной задачи на платформе. Это не длительность медиафайла
maxOutputDurationSecondsМаксимальная длительность результата модели, взятая из maximum её свойства duration_seconds; поле не выдаётся, если максимум не объявлен
badgesМетки возможностей и лицензирования, например commercial_use или no_watermark. Незнакомые значения игнорируйте
relatedModelsПодобранные вручную альтернативы или следующие шаги в виде доступных для вызова ID моделей
version / updatedAtОтслеживание изменений: сравнивайте значения, чтобы понять, изменилось ли что-либо при следующем чтении

mature и policyTier — информационные метки. Они никак не участвуют в решении о принятии запроса.

Как читать цены

Каждая запись в pricing — одна ценовая категория:

ПолеЗначение
unitper_image, per_second (длительности выходного медиафайла), per_request или per_1k_tokens
priceВаша цена за единицу в долларах США, в виде десятичной строки. Она уже учитывает тарифы аккаунта, которые могут различаться между аккаунтами
variantНастройки, выбирающие категорию. Пусто при единственной цене; только значение (например, 720p), если категорию выбирает одно поле; пары field=value, соединённые через ; и отсортированные по имени поля (например, generate_audio=false;resolution=480p), если полей несколько
currencyВсегда USD
regularPrice, offerLabel, offerPercent, offerEndsAtПрисутствуют только во время действия предложения для клиента: цена до предложения, его название, процент и время окончания

startingPrice — самая дешёвая категория, а не цена ваших настроек. Поля выбора категории перечислены в inputSchema["x-pricing"].variantFields, если оно есть, а иначе — это поля с пометкой x-ui.affects_price: true (кроме quantityField). Пропущенное поле учитывается со своим значением default из схемы.

  • Медиамодели: стоимость равна price × quantity, где количество — значение quantityField во входных данных (или одна единица, если quantityField пусто). Некоторые эндпоинты затем добавляют отдельную плату за каждый дополнительный входной файл, чаще всего за каждое референсное изображение. Это полноценная часть счёта, а не мелочь округления: на эндпоинте редактирования, принимающем массив референсов, заполнение массива может увеличить сумму в несколько раз. При этом unit, quantity, variant и цена категории price остаются такими же, как для одного изображения, — поэтому формула выше занижает сумму без видимого признака. Машиночитаемый признак — поле с пометкой x-ui.affects_price: true, которое не является quantityField и не входит в x-pricing.variantFields: оно меняет сумму, но не выбирает категорию. Для такого эндпоинта считайте pricing нижней границей, а сумму берите из расчёта цены.
  • Текстовые модели (per_1k_tokens): одно число не описывает всю оплату. price — только тариф выходных токенов за 1000 токенов. Для входных и кешированных входных токенов действуют отдельные, обычно меньшие тарифы; они оплачиваются по измеренному использованию каждого вызова. Не умножайте price на общее число токенов. Цены за миллион входных, кешированных входных и выходных токенов показаны на странице каждой модели и в price.perMillionTokens публичного каталога.

Чтобы узнать сумму конкретного запроса нативного API до оплаты, используйте POST /api/v1/jobs/quote. Итоговые списания находятся в записи задачи (cost с settled: true) и в разделе Оплата.

Кеширование каталога

  • Ответ каталога можно кешировать до 60 секунд (Cache-Control: private, max-age=60). Храните его только в приватном кеше отдельного аккаунта: цены зависят от аккаунта, поэтому общий или публичный CDN-кеш использовать нельзя.
  • После этого не перечитывайте каталог перед каждой задачей. Чтение каталога расходует лимит чтения аккаунта независимо от отправки задач. Запрос каталога перед каждым вызовом всё равно расходует лишнюю квоту чтения; см. Ограничения частоты запросов.
  • Обновляйте его по таймеру (например, раз в несколько минут), после ошибки валидации 400 и при отклонении отправки запроса к этой модели.
  • Сохраняйте version и updatedAt использованной записи, чтобы понять, изменилось ли что-либо при последующем чтении.
  • Не распространяйте жёстко зафиксированный снимок схемы и никогда не подменяйте каталог статической страницей, если в актуальном каталоге нет доступной для вызова записи.

Общие правила Schema и Playground

inputSchema использует JSON Schema 2020-12. Стандартные ключевые слова управляют серверной валидацией; объект x-ui на уровне свойства описывает только порядок в Playground, отображение и подсказки об обновлении цены. Клиенты должны игнорировать неизвестные расширения отображения; они никогда не заменяют required, перечисления, границы и условные ветви. См. полный Контракт Spicy Schema.

Создайте собственную таблицу параметров модели

На этом сайте нет статической таблицы параметров для каждой модели. Дело не в нежелании её делать: модели и поля меняются каждую неделю, любая статическая копия устаревает, а чтение устаревшего параметра приводит к 400 или неверной цене. У параметров ровно один источник — inputSchema из каталога. Playground отображает тот же документ, а SDK проверяет данные по нему.

Как части схемы превращаются в таблицу

В схемеВ вашей таблице параметров
ключ propertiesназвание параметра. Имена полей едины между моделями: один смысл — одно имя везде
наличие в requiredобязательное или необязательное поле
anyOf / allOfусловно обязательное поле. anyOf = передайте хотя бы одно из этих полей; в allOf правило if → then = если передаёте A, нужно передать B
typeтип
descriptionподсказка. Это английский текст, который можно показывать без изменений; писать свой не требуется
enumдопустимые значения. При наличии x-ui.enum_labels используйте его для понятных человеку подписей
minimum / maximumдопустимый диапазон; x-ui.step — шаг, а x-ui.unit — суффикс (s, px)
minItems / maxItemsдля массива — допустимое число записей. Без этого строка ничего не говорит о лимите, с которым столкнётся читатель
itemsдля массива — формат одной записи. Когда items.type равно object, эта подсхема сама является полной схемой объекта: прочитайте её required, additionalProperties, а также type, description, диапазон и default каждого вложенного поля. Показывайте их вложенной таблицей, а не полем свободного текста
defaultзначение по умолчанию, применяемое при отсутствии поля
x-ui.orderключ сортировки: сначала большие значения. Не сортируйте по порядку ключей в properties — порядок ключей JSON-объекта не входит в контракт
x-ui.primaryзакрепить сверху как основное поле
x-ui.advancedможно свернуть в раздел дополнительных настроек
x-ui.affects_priceотметить влияние на цену; изменение значения требует нового расчёта
x-ui.widgetэлемент управления для редактируемой формы (тринадцать значений, см. Spicy Schema)

Условные ветви появляются только в корне inputSchema и имеют две формы: anyOf, где каждая ветвь перечисляет только поля required («передайте хотя бы одно из этих полей»), и allOf, где каждая ветвь — if → then («если передаёте A, нужно передать B»). Не выводите условия из семейства модели и не ожидайте oneOf, not или dependentSchemas. Новые эндпоинты следуют единым полям Spicy Schema; старые — своей актуальной схеме.

Получение схемы

const modelId = 'MODEL_ID_FROM_CATALOG'; // the exact `model` value from GET /api/v1/models
const res = await fetch(`https://api.spicyapi.ai/api/v1/models/${modelId}`, {
  headers: { Authorization: `Bearer ${process.env.SPICY_API_KEY}` },
});
const { data } = await res.json();
const schema = data.inputSchema;

const rows = Object.entries(schema.properties)
  .sort(([, a], [, b]) => (b['x-ui']?.order ?? 0) - (a['x-ui']?.order ?? 0))
  .map(([name, prop]) => ({
    name,
    required: (schema.required ?? []).includes(name),
    type: prop.type,
    description: prop.description,
    values: prop.enum
      ? prop.enum.map((v) => prop['x-ui']?.enum_labels?.[v] ?? v)
      : prop.minimum !== undefined || prop.maximum !== undefined
        ? [`${prop.minimum ?? ''}–${prop.maximum ?? ''}${prop['x-ui']?.unit ?? ''}`]
        : [],
    default: prop.default,
    affectsPrice: prop['x-ui']?.affects_price === true,
    advanced: prop['x-ui']?.advanced === true,
  }));

Этот пример намеренно ограничен скалярными значениями: для массива он оставляет values пустым, поскольку допустимое содержимое массива задаётся через items, minItems и maxItems, а не через enum или диапазон. Расширьте пример до использования в рабочем продукте: если строка показывает параметр лишь как array, без формата записи и ограничения длины, читатель не сможет по ней подготовить данные.

Запись одной модели также возвращает examples: каждый пример — полный набор входных данных, уже проверенный сервером по текущей схеме. Он подходит и как минимальный рабочий пример документации, и как тело запроса, которое можно отправить без изменений. Это надёжнее ручного составления: ручной пример устаревает при изменении схемы, а examples — нет.

Тот же код охватывает любую модель при замене modelId и не устаревает при добавлении новых моделей. Чтобы построить таблицу для всего каталога, переберите items из GET /api/v1/models?includeSchema=1 и кешируйте результат, как описано в разделе Кеширование каталога.

Более подробные пояснения, чем description

description — одно предложение, рассчитанное на подпись поля формы. Если параметру нужны дополнительные пояснения, публичный каталог содержит вторую заметку для каждого параметра:

curl "https://api.spicyapi.ai/console/v1/catalog/models/MODEL_ID_FROM_CATALOG?locale=en"

Запись одной модели возвращает paramNotes — объект с именами параметров в качестве ключей; через locale доступен текст на любом из десяти языков документации. Здесь объясняется то, что не помещается в подпись: относительно чего на самом деле измеряется значение, какая настройка заметно замедляет вызов и какие входные данные проходят схему, но всё равно дают неудовлетворительный результат. Для параметра, указывающего на размещённый вами файл, это обычно единственное место, где объясняется, загружаем ли мы его, когда именно и что будет, если файл окажется непригодным.

Две границы: paramNotes находится в публичном каталоге, не требующем ключа, а не в авторизованном GET /api/v1/models. Поэтому читайте его отдельно и кешируйте рядом со схемой. Кроме того, это поясняющий текст, а не правило: только inputSchema определяет, принимается ли запрос.

На этой странице