spicyapiДокументация
SDK
Основное содержимое

Python SDK

Официальный SDK SpicyAPI для Python (spicyapi): установка, расчёт стоимости и первая генерация, загрузка локального файла, проверка вебхуков и разбор всех кодов ошибок.

spicyapi — официальный SDK SpicyAPI для Python. Он прячет генерацию изображений, видео и звука за одним объектом-клиентом: подставляет ваш API-ключ, превращает конверт ответа в обычные словари, дожидается завершения задачи с разумно растущими паузами, загружает локальные файлы одним вызовом и проверяет подписи вебхуков за постоянное время.

Ему нужен Python 3.11 или новее, и у него нет зависимостей времени выполнения. Так задумано: этот пакет попадает в дерево зависимостей, за которое отвечаете вы, и каждое ограничение, которое он туда приносит, — это ещё одна возможность конфликта.

Тот ли это инструмент?

Вы хотите…Что использовать
Вызывать SpicyAPI из собственного кода на PythonЭтот SDK
Вызывать его из Node.js или TypeScriptTypeScript SDK
Вызывать его из Go, PHP или JavaSDK для Go, PHP или Java
Генерировать из терминала, не написав ни строчки кодаCLI
Чтобы ИИ-ассистент вызывал SpicyAPI за васMCP-сервер и Agent Skill
Пользоваться чат-моделью или текстовой модельюОфициальная библиотека openai, anthropic или google-genai, направленная на SpicyAPI, — см. Текст и потоковые ответы

Установка

pip install spicyapi

Затем положите ключ в переменную окружения, чтобы он никогда не попадал ни в ваш код, ни в файл истории команд оболочки:

export SPICY_API_KEY="sk-spicy-..."

Первый результат

Узнайте цену, примите решение, создайте задачу, дождитесь её и заберите файлы. Расчёт стоимости бесплатен и ничего не резервирует.

import os, uuid
from spicyapi import SpicyClient, output_assets

client = SpicyClient()  # читает SPICY_API_KEY из окружения

model = client.get_model("MODEL_ID_FROM_CATALOG")
quote = client.quote_task(model=model["model"], input_data={"prompt": "a lantern in fog"})
print(quote["estimatedCost"], quote["maxCharge"])   # решайте до того, как потратите

task = client.create_task(
    model=model["model"],
    input_data={"prompt": "a lantern in fog"},
    idempotency_key=str(uuid.uuid4()),
    quote_id=quote["quoteId"],
    expected_cost=quote["estimatedCost"],
)
final = client.wait_for_terminal(task["taskId"])
for asset in output_assets(final):                  # функция уровня модуля, а не метод
    print(asset["url"])

MODEL_ID_FROM_CATALOG — это заглушка. Настоящий идентификатор берите из каталога, доступного по ключу, — это client.list_models(), то есть GET /api/v1/models, — и никогда по памяти или со старой страницы: модели добавляются, приостанавливаются и снимаются с публикации. Поля input_data собирайте по inputSchema самой модели: его возвращает list_models(include_schema=True), и каждая модель принимает свой набор полей.

Суммы приходят десятичными строками в долларах США, а не числами с плавающей точкой, потому что двоичная плавающая точка не хранит большинство десятичных сумм точно. Сравнивайте и складывайте их через decimal.Decimal. Ни кредитов, ни баллов в этом API нет нигде.

wait_for_terminal возвращает управление при любом конечном состоянии. Конечное состояние — не значит успешное: прежде чем читать результат, проверьте final["state"].

Некоторые эндпоинты отвечают текстом, а не файлами. Если модель выдаёт расшифровку или подпись, сначала читайте output_text(final): клиент, который обходит только список файлов, сообщит, что совершенно успешная задача ничего не произвела.

Начать со своего файла

Для «изображения в видео», замены персонажа и редактирования изображений ваш материал сначала должен оказаться на нашей стороне. upload_file проводит загрузку из трёх шагов — разрешение, PUT в хранилище, commit — за один вызов и возвращает URI вида spicy://. Именно этот URI и подставляется во входные данные.

uploaded = client.upload_file("/path/to/reference.png")
task = client.create_task(
    model="MODEL_ID_FROM_CATALOG",
    input_data={"image": uploaded["uri"], "prompt": "slow dolly in"},
    idempotency_key=str(uuid.uuid4()),
)

Публично доступный HTTPS-адрес можно подставить во входные данные напрямую, загружать его никуда не нужно. Изображения принимаются до 10 MiB, аудио и видео — до 90 MiB; остальное описано на странице Медиа.

Вебхуки

verify_webhook — функция уровня модуля, поэтому обработчику запроса не нужно создавать клиент. Она сравнивает за постоянное время и проверяет метку времени только после того, как подпись признана верной.

from spicyapi import verify_webhook

delivery = verify_webhook(
    raw_body=request.body,                      # ровно те байты, до любого разбора
    signature=request.headers["X-Webhook-Signature"],
    timestamp=request.headers["X-Webhook-Timestamp"],
    payload_version=request.headers["X-Webhook-Payload-Version"],
    secret=os.environ["SPICY_WEBHOOK_SECRET"],
)

Проверяйте исходные байты. json.loads, а следом json.dumps их меняют, и подпись больше никогда не совпадёт. Доставки повторяются, поэтому отсеивайте дубликаты по идентификатору доставки. Полный формат доставки и расписание повторов — на странице Вебхуки.

Ошибки

Любая неудача поднимает SpicyApiError с полями status, code, request_id и retry_after_seconds. Ветвитесь по code и никогда по тексту сообщения: сообщения переводятся, коды — нет.

За HTTP 503 стоят три разных бизнес-кода, поэтому одного HTTP-статуса мало, чтобы решить, что делать:

codeЗначениеЧто делать
40003загруженные байты не совпадают с выданным разрешениемначните заново, с нового разрешения
40004ни одна конфигурация не обслуживает такое сочетание параметровизмените параметр, названный в сообщении
40201на балансе недостаточно средствпополните баланс
40901цена изменилась до того, как задача была созданаполучите расчёт заново, сохранив тот же ключ идемпотентности
503зависимость ненадолго недоступнавыдержите паузу по Retry-After
50301у модели сейчас нет ни рабочей конфигурации, ни ценыобновите каталог и не забрасывайте сервис запросами
50302синхронная генерация завершилась ошибкой на стороне исполнителя, средства возвращеныповторите с новым ключом идемпотентности

err.recovery возвращает ту же подсказку во время выполнения. Обращаясь в поддержку, указывайте request_id. Все коды перечислены на странице Ошибки и повторы.

Две вещи, которые сэкономят вам деньги

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

Успешная задача оплачивается, даже если результат разочаровал. Когда цена важна, сначала получите расчёт: quote_task ничего не резервирует и ничего не создаёт.

Чего этот пакет не делает

  • Текстовые модели. Они говорят в форматах OpenAI, Anthropic и Gemini, поэтому официальные библиотеки openai, anthropic и google-genai уже работают — направьте их на https://api.spicyapi.ai/v1 с тем же ключом. См. Текст и потоковые ответы.
  • Приложения для браузера, телефона и рабочего стола. Никогда не встраивайте этот ключ в приложение: ключ, вшитый в клиент, — это публичный ключ. Вызывайте со своего сервера.
  • Выбор модели и заполнение её полей. Пакет отдаёт вам каталог и схему каждой модели; что именно отправлять, решаете вы.
  • Отмена задачи. Принятая задача выполняется до конца. Остановка вашей программы останавливает только ваше ожидание.

Исходный код

Пакет распространяется под лицензией MIT и разрабатывается открыто по адресу github.com/spicyapi-ai/spicy-python. Его README — самая короткая полная экскурсия по клиенту; эта страница добавляет к ней понятия вокруг.

Что дальше

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