spicyapi문서
SDK
본문

Go SDK

SpicyAPI 공식 Go SDK(github.com/spicyapi-ai/spicy-go) 안내입니다. 설치부터 견적과 첫 생성, 로컬 파일 업로드, 작업 기록 조회, 모든 오류 코드 처리까지 다룹니다.

spicy-go는 SpicyAPI의 공식 Go SDK입니다. 이미지·영상·오디오 생성을 *spicy.Client 하나 뒤에 놓습니다. API 키를 대신 붙여 주고, 응답 봉투를 타입이 있는 구조체로 디코딩하고, 상한이 있는 백오프로 작업을 기다리고, 로컬 파일을 한 번의 호출로 업로드하고, 모든 실패에 errors.As로 받아 낼 수 있는 단일 오류 타입을 줍니다.

Go 1.23 이상이 필요하고 표준 라이브러리만 씁니다. 일부러 그렇게 만들었습니다. 이 패키지는 내 의존성 그래프 안으로 들어가고, 이 패키지가 끌고 들어오는 모듈 하나하나가 내가 감사해야 할 대상을 하나씩 늘리기 때문입니다.

나에게 맞는 도구인가요?

하고 싶은 일쓸 도구
내 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이고, HTTP로는 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)

이것은 최적화일 뿐 보장이 아닙니다. nil 검사 없이 task.Task를 읽는 코드는 주어진 시간보다 느린 첫 생성이 나올 때까지는 잘 돌아가다가, 그 순간 프로덕션에서 패닉을 냅니다.

로컬 파일에서 시작하기

이미지-투-비디오, 피사체 교체, 이미지 편집은 모두 내 소재가 먼저 이쪽에 올라와 있어야 합니다. UploadFile은 티켓 발급, 스토리지로 PUT, 커밋이라는 세 단계 업로드를 한 번의 호출로 끝내고 spicy:// URI를 돌려줍니다. Input에 넣는 값이 바로 그 URI입니다.

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 달력 날짜이며, 최대 92일까지의 반열린 구간 [from, to)를 이룹니다. UTC 자정을 넘길 수 있는 순회에서는 From과 To를 반드시 고정하세요. 비워 두면 서버가 페이지마다 자기 구간을 다시 계산하고, 그때 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은 서로 다른 비즈니스 코드 세 개가 함께 씁니다. 그래서 상태만으로는 무엇을 해야 할지 정할 수 없습니다.

코드의미대응
40003업로드한 바이트가 티켓과 맞지 않습니다새 티켓을 받아 처음부터 다시 하세요
40004그 파라미터 조합을 처리할 배포가 없습니다메시지에 이름이 나온 파라미터를 바꾸세요
40201잔액이 모자랍니다충전하세요
40901작업이 만들어지기 전에 가격이 바뀌었습니다견적을 다시 받되 멱등 키는 그대로 두세요
503의존하는 구성 요소를 잠시 쓸 수 없습니다RetryAfter만큼 기다린 뒤 다시 시도하세요
50301지금 쓸 수 있는 배포나 가격이 없습니다카탈로그를 새로 읽으세요. 몰아치듯 재시도하지 마세요
50302동기 생성이 상위에서 실패했고 환불됐습니다새 멱등 키로 다시 시도하세요

모든 코드는 오류에 정리돼 있습니다. 지원팀에 문의할 때는 RequestID를 함께 알려 주세요.

돈을 아껴 주는 두 가지

제출 하나에 멱등 키 하나를 유지하세요. 다시 보낼 때는 시간이 초과된 뒤라도 그 키를 그대로 다시 쓰세요. 응답을 받지 못했다는 사실이 작업이 만들어지지 않았다는 증거는 아닙니다. 새 키를 쓰면 결과를 모르는 상태가 돈이 드는 두 번째 작업으로 바뀝니다. CreateTask가 키를 필수로 받는 이유가 바로 이것입니다. 멱등성을 보세요.

성공한 작업은 결과가 마음에 들지 않아도 과금됩니다. Quote는 아무것도 예약하지 않으므로, 가격이 중요하다면 먼저 견적을 받으세요.

이 패키지가 하지 않는 일

  • 텍스트 모델. 텍스트 모델은 OpenAI, Anthropic, Gemini의 통신 형식을 씁니다. 그 형식을 지원하는 기존 Go 클라이언트가 같은 키로 https://api.spicyapi.ai/v1에 대고 이미 그대로 작동합니다. 텍스트와 스트리밍을 보세요.
  • 클라이언트 쪽 애플리케이션. 배포하는 바이너리 안에 이 키를 담지 마세요. 내 서버에서 호출하세요.
  • 모델 고르기와 필드 채우기. SDK는 카탈로그와 모델별 스키마를 건네줄 뿐이고, 무엇을 보낼지는 직접 정합니다.
  • 작업 취소. 작업이 접수되면 끝까지 실행됩니다. context를 취소해도 멈추는 것은 내 쪽의 기다림뿐입니다.

소스

이 모듈은 MIT 라이선스이고 **github.com/spicyapi-ai/spicy-go**에서 공개적으로 개발됩니다. 저장소의 README는 이 클라이언트를 가장 짧게 한 바퀴 둘러보는 문서이고, 이 페이지는 그 주변 개념을 채워 줍니다.

다음 단계

이 페이지에서