Go SDK
Официальный SDK SpicyAPI для Go (github.com/spicyapi-ai/spicy-go): установка, расчёт стоимости и первая генерация, загрузка локального файла, обход истории задач и разбор всех кодов ошибок.
spicy-go — официальный SDK SpicyAPI для Go. Он прячет генерацию изображений, видео и звука за одним *spicy.Client: подставляет ваш API-ключ, разбирает конверт ответа в типизированные структуры, дожидается задачи с ограниченными сверху паузами, загружает локальные файлы одним вызовом и сводит любую неудачу к единственному типу ошибки, который ловится через errors.As.
Ему нужен Go 1.23 или новее, и он обходится одной стандартной библиотекой. Так задумано: этот пакет попадает в ваш граф зависимостей, и каждый модуль, который он тянет за собой, — это ещё одна вещь, которую вам придётся проверять самим.
Тот ли это инструмент?
| Вы хотите… | Что использовать |
|---|---|
| Вызывать SpicyAPI из собственного кода на Go | Этот SDK |
| Вызывать его из Node.js или TypeScript | TypeScript SDK |
| Вызывать его из Python, PHP или Java | SDK для Python, PHP или Java |
| Генерировать из терминала, не написав ни строчки кода | CLI |
| Чтобы ИИ-ассистент вызывал SpicyAPI за вас | MCP-сервер и Agent Skill |
| Пользоваться чат-моделью или текстовой моделью | Проверенный клиент на Go для форматов OpenAI, Anthropic или Gemini — см. Текст и потоковые ответы |
Установка
Go разрешает модули прямо из их репозиториев, поэтому никакого реестра тут нет и ждать нечего. Эта команда работает уже сегодня:
go get github.com/spicyapi-ai/spicy-goЗатем положите ключ в переменную окружения, чтобы он никогда не попадал ни в ваш код, ни в файл истории команд оболочки:
export SPICY_API_KEY="sk-spicy-..."Первый результат
Узнайте цену, примите решение, создайте задачу, дождитесь её и заберите файлы. Расчёт стоимости бесплатен и ничего не резервирует.
client, err := spicy.New(spicy.Options{}) // читает SPICY_API_KEY из окружения
if err != nil {
return err
}
request := spicy.CreateTaskInput{
Model: "MODEL_ID_FROM_CATALOG",
Input: map[string]any{"prompt": "a lantern in fog"},
}
quote, err := client.Quote(ctx, request)
if err != nil {
return err
}
log.Printf("this will cost at most %s", quote.MaxCharge)
// Сохраните этот ключ рядом с собственной записью о заказе ещё до отправки: весь
// его смысл в том, чтобы пережить сбой, из-за которого ответ потерялся.
key, err := spicy.NewIdempotencyKey()
if err != nil {
return err
}
request.Model = quote.Model
request.QuoteID = quote.QuoteID
request.ExpectedCost = quote.EstimatedCost
task, err := client.CreateTask(ctx, request, key, spicy.CreateTaskOptions{})
if err != nil {
return err
}
final, err := client.WaitForTerminal(ctx, task.TaskID, spicy.WaitOptions{})
if err != nil {
return err
}
for _, asset := range final.ReadyAssets() {
log.Println(asset.URL)
}MODEL_ID_FROM_CATALOG — это заглушка. Настоящий идентификатор берите из каталога, доступного по ключу, — это client.ListModels, то есть GET /api/v1/models, — и никогда по памяти или со старой страницы: модели добавляются, приостанавливаются и снимаются с публикации. Поле Input собирайте по inputSchema самой модели: его возвращает ListModels, и каждая модель принимает свой набор полей.
Суммы передаются десятичными строками в долларах США, а не числами с плавающей точкой, потому что двоичная плавающая точка не хранит большинство десятичных сумм точно. Ни кредитов, ни баллов в этом API нет нигде.
Конечное состояние — не значит успешное: ожидание заканчивают и failed, и canceled, и expired. Проверяйте final.State.
Обойтись без первого опроса
WaitSeconds просит сервис подержать соединение открытым, пока задача не завершится; на быстрой модели это значит, что результат придёт вместе с отправкой.
seconds := 30
task, err := client.CreateTask(ctx, request, key, spicy.CreateTaskOptions{WaitSeconds: &seconds})
if err != nil {
return err
}
// Task равен nil всякий раз, когда время ожидания вышло. Это обычный исход, а не
// ошибка: генерация всё ещё идёт, так что дальше опрашивайте статус.
final := task.Task
if final == nil {
final, err = client.WaitForTerminal(ctx, task.TaskID, spicy.WaitOptions{})
if err != nil {
return err
}
}
log.Println(final.State, final.Cost)Это оптимизация, а не гарантия. Код, который читает task.Task без проверки на nil, исправно работает ровно до первой генерации, которая не уложилась в отведённое время, — а потом падает в рабочей среде.
Начать со своего файла
Для «изображения в видео», замены персонажа и редактирования изображений ваш материал сначала должен оказаться на нашей стороне. UploadFile проводит загрузку из трёх шагов — разрешение, PUT в хранилище, commit — за один вызов и возвращает URI вида spicy://. Именно этот URI и подставляется в Input.
uploaded, err := client.UploadFile(ctx, "/path/to/reference.png", "")
// uploaded.URI -> "spicy://f/fil_..."Публично доступный HTTPS-адрес можно подставить во входные данные напрямую, загружать его никуда не нужно. Изображения принимаются до 10 MiB, аудио и видео — до 90 MiB; см. Медиа.
Баланс, расход и история
Все три относятся к вызывающему API-ключу: соседние ключи того же аккаунта и генерации, запущенные в веб-консоли, сюда не попадают, и их отсутствие — не сбой.
balance, err := client.GetBalance(ctx)
if err != nil {
return err
}
log.Printf("%s available, %s held by accepted tasks", balance.Available, balance.Held)
usage, err := client.GetUsage(ctx, spicy.UsageOptions{From: "2026-09-01", To: "2026-09-08"})
if err != nil {
return err
}
log.Printf("%d calls, %s settled over [%s,%s)", usage.TotalCalls, usage.TotalSpend, usage.From, usage.To)
// EachTask повторяет каждый фильтр на каждой странице и двигает только курсор.
if err := client.EachTask(ctx, spicy.ListTasksOptions{From: "2026-09-01", To: "2026-09-08"},
func(task spicy.TaskListItem) bool {
log.Println(task.TaskID, task.State, task.Cost)
return true // верните false, чтобы остановиться раньше
}); err != nil {
return err
}Даты — это календарные дни по UTC в полуоткрытом интервале [from, to) длиной не более 92 дней. Задавайте From и To явно при любом обходе, который может пересечь полночь по UTC. Если оставить их пустыми, сервер пересчитывает собственное окно — to равно завтрашнему дню по UTC — на каждой странице, и обход, начатый в 23:59 и продолженный в 00:01, задаёт два разных вопроса и склеивает ответы. Никто об этом не сообщит.
GetUsage нужен для сверки, а не для отслеживания хода работ. За ним стоит общий для всего аккаунта бюджет запросов, который делят между собой все ключи, и этот ограничитель при сбое закрывается: опрашивая его, можно лишить отчётности все остальные ключи. Следите за задачей через WaitForTerminal. К тому же окончательное списание может прийти с задержкой, поэтому цифра, прочитанная сегодня, завтра ещё может измениться.
Ошибки
Неудачи возвращаются как *spicy.APIError и достаются через errors.As:
var apiErr *spicy.APIError
if errors.As(err, &apiErr) {
log.Println(apiErr.Status, apiErr.Code, apiErr.RequestID, apiErr.RetryAfter)
}Ветвитесь по Code и никогда по тексту сообщения: сообщения переводятся, коды — нет. За HTTP 503 стоят три разных бизнес-кода, поэтому одного статуса мало, чтобы решить, что делать:
| Code | Значение | Что делать |
|---|---|---|
40003 | загруженные байты не совпадают с выданным разрешением | начните заново, с нового разрешения |
40004 | ни одна конфигурация не обслуживает такое сочетание параметров | измените параметр, названный в сообщении |
40201 | на балансе недостаточно средств | пополните баланс |
40901 | цена изменилась до того, как задача была создана | получите расчёт заново, сохранив тот же ключ идемпотентности |
503 | зависимость ненадолго недоступна | выдержите паузу по RetryAfter |
50301 | сейчас нет ни рабочей конфигурации, ни цены | обновите каталог и не забрасывайте сервис запросами |
50302 | синхронная генерация завершилась ошибкой на стороне исполнителя, средства возвращены | повторите с новым ключом идемпотентности |
Все коды перечислены на странице Ошибки и повторы. Обращаясь в поддержку, указывайте RequestID.
Две вещи, которые сэкономят вам деньги
На одну отправку — один ключ идемпотентности. Используйте его при каждой повторной отправке, в том числе после таймаута. Потерянный ответ не доказывает, что задача не создана: новый ключ превращает неизвестный исход во вторую платную задачу. Именно поэтому CreateTask требует ключ. См. Идемпотентность.
Успешная задача оплачивается, даже если результат разочаровал. Quote ничего не резервирует, так что, когда цена важна, сначала получите расчёт.
Чего этот пакет не делает
- Текстовые модели. Они говорят в форматах OpenAI, Anthropic и Gemini — проверенные клиенты на Go для этих форматов уже работают с
https://api.spicyapi.ai/v1по тому же ключу. См. Текст и потоковые ответы. - Клиентские приложения. Никогда не встраивайте этот ключ в двоичный файл, который вы раздаёте. Вызывайте со своего сервера.
- Выбор модели и заполнение её полей. Пакет отдаёт вам каталог и схему каждой модели; что именно отправлять, решаете вы.
- Отмена задачи. Принятая задача выполняется до конца. Отмена
contextостанавливает только ваше ожидание.
Исходный код
Модуль распространяется под лицензией MIT и разрабатывается открыто по адресу github.com/spicyapi-ai/spicy-go. Его README — самая короткая полная экскурсия по клиенту; эта страница добавляет к ней понятия вокруг.
Что дальше
Каталог моделей и схемы
Как читать inputSchema модели и выбирать допустимые входные данные
Асинхронные задачи
Состояния задач, опрос и значение каждого поля
Расчёт цены и совместимость
Точная цена запроса ещё до того, как вы за него заплатите
Вебхуки
Формат доставки, расписание повторов и проверка подписи
Ошибки
Все коды ошибок и какие из них стоит повторять
Python SDK
Тот же процесс на Python, без зависимостей времени выполнения
Python SDK
Официальный SDK SpicyAPI для Python (spicyapi): установка, расчёт стоимости и первая генерация, загрузка локального файла, проверка вебхуков и разбор всех кодов ошибок.
PHP SDK
Официальный SDK SpicyAPI для PHP (spicyapi/spicyapi): установка через Composer, расчёт стоимости и первая генерация, загрузка локального файла, безопасная проверка вебхуков и разбор всех кодов ошибок.

