Go SDK
SpicyAPI 公式の Go SDK(github.com/spicyapi-ai/spicy-go)のガイドです。インストール、見積もりと最初の生成、ローカルファイルのアップロード、タスク履歴の読み取り、すべてのエラーコードの扱い方までをまとめています。
spicy-go は、SpicyAPI 公式の Go SDK です。画像・動画・音声の生成を、1 つの *spicy.Client の後ろにまとめます。API キーを付ける、応答のエンベロープを型付きの構造体にデコードする、上限付きのバックオフでタスクを待つ、ローカルファイルを 1 回の呼び出しでアップロードする、そしてあらゆる失敗を errors.As で判別できる単一のエラー型にまとめる。こうした作業を、すべて引き受けます。
必要なのは Go 1.23 以降だけで、使っているのは標準ライブラリだけです。これは意図した設計です。このパッケージは、あなたの依存グラフに入ります。引きずり込むモジュールが 1 つ増えるたびに、あなたが監査すべき対象も 1 つ増えます。
このツールで合っている?
| やりたいこと | 使うもの |
|---|---|
| 自分の Go のコードから SpicyAPI を呼び出す | この SDK |
| Node.js や TypeScript から呼び出す | TypeScript SDK |
| Python、PHP、Java から呼び出す | Python、PHP、Java の各 SDK |
| コードを書かずに、ターミナルから生成する | CLI |
| AI アシスタントに、SpicyAPI を代わりに呼び出してもらう | MCP サーバーと Agent Skill |
| チャットモデルやテキストモデルを使う | OpenAI、Anthropic、Gemini 形式に対応した、定評のある Go 用クライアント。テキストとストリーミングを参照 |
インストール
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 は仮の値です。実際の ID は、認証付きのカタログ(client.ListModels、つまり GET /api/v1/models)から取得してください。記憶や古い Web ページから写してはいけません。モデルは追加・一時停止・終了されるからです。Input は、そのモデル自身の inputSchema(ListModels が返します)に従って組み立ててください。受け付けるフィールドは、モデルごとに違います。
金額は米ドルの十進文字列で、浮動小数点数ではありません。2 進数の浮動小数点数では、ほとんどの十進の金額を正確に保持できないからです。この 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)これは最適化であって、保証ではありません。 nil の確認をせずに task.Task を読むコードは、待ち時間より遅い生成が初めて来るまでは動きます。そして本番で panic します。
ローカルのファイルから始める
画像から動画を作る、被写体を差し替える、画像を編集する。どれも、まず素材を当社側に置く必要があります。UploadFile は、チケットの発行、ストレージへの PUT、確定という 3 つのステップを 1 回の呼び出しで行い、spicy:// の URI を返します。Input に入れるのは、この URI です。
uploaded, err := client.UploadFile(ctx, "/path/to/reference.png", "")
// uploaded.URI -> "spicy://f/fil_..."公開されている HTTPS の URL なら、アップロードは一切必要なく、そのまま入力に入れられます。画像は 10 MiB まで、音声と動画は 90 MiB までです。メディアを参照してください。
残高・利用状況・履歴
この 3 つは、いずれも呼び出しに使った API キーの範囲に限られます。同じアカウントのほかのキーや、Web コンソールから始めた生成は含まれません。含まれていないことは、不具合ではありません。
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 日です。UTC の 0 時をまたぎうる走査では、From と To を必ず固定してください。 空のままにすると、サーバーはページごとに自分で期間を計算し直します(to は UTC の翌日です)。23:59 に始まって 00:01 に続く走査は、2 つの異なる問いを投げ、その答えをつなぎ合わせてしまいます。それを知らせるものは、どこにもありません。
GetUsage は突き合わせのための機能であって、進捗を見るためのものではありません。この呼び出しは、すべてのキーで共有されるアカウント単位の予算の後ろにあり、その制限はフェイルクローズで働きます。つまり、ポーリングすると、ほかのすべてのキーが自分の利用状況を読めなくなることがあります。1 件のタスクを追うには、代わりに 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 は、3 つの異なるビジネスコードで共有されています。 そのため、ステータスだけを見ても、次に何をすべきかは決められません。
| コード | 意味 | 対処 |
|---|---|---|
40003 | アップロードされたバイト列が、チケットと一致しない | チケットを取り直して、最初からやり直します |
40004 | そのパラメーターの組み合わせに対応できる構成がない | メッセージに書かれているパラメーターを変更します |
40201 | 残高が足りない | チャージします |
40901 | タスクが作成される前に価格が変わった | 見積もりを取り直します。冪等キーは同じものを使ってください |
503 | 依存している何かが、短時間だけ利用できない | RetryAfter の秒数だけ待ってから再試行します |
50301 | いま使える構成も価格もない | カタログを取り直します。連打しないでください |
50302 | 同期的な生成が上流で失敗し、返金された | 新しい冪等キーで再試行します |
すべてのコードはエラーに一覧があります。サポートに問い合わせるときは RequestID を伝えてください。
お金を守る 2 つのこと
1 回の送信につき、冪等キーは 1 つに保ってください。 タイムアウトのあとも含め、送り直すときは毎回同じキーを使います。応答が失われたことは、タスクが作成されなかったことの証明にはなりません。新しいキーを使うと、「結果が分からないだけ」だったものが、課金される 2 つ目のタスクに変わります。CreateTask がキーを必須にしているのは、まさにこのためです。冪等性を参照してください。
成功したタスクは、結果が期待外れでも課金されます。 Quote は何も確保しないので、価格が気になるときは先に見積もりを取ってください。
このパッケージが扱わないこと
- テキストモデル。 OpenAI、Anthropic、Gemini のリクエスト形式に対応しています。それらに対応した定評のある Go 用クライアントが、同じキーのまま
https://api.spicyapi.ai/v1に対してそのまま動きます。テキストとストリーミングを参照してください。 - クライアント側のアプリケーション。 このキーを、配布するバイナリの中に入れないでください。呼び出しは、必ず自分のサーバーから行ってください。
- モデルを選んだり、そのフィールドを埋めたりすること。 SDK が渡すのは、カタログと各モデルのスキーマです。何を送るかは、あなたが決めます。
- タスクのキャンセル。 いったん受理されたタスクは、最後まで実行されます。
contextをキャンセルしても、止まるのはあなたの側の待機だけです。
ソースコード
このモジュールは MIT ライセンスで、github.com/spicyapi-ai/spicy-go で公開のまま開発しています。README は、クライアント全体をいちばん短く見渡せる資料です。このページは、その周りにある考え方を補います。

