공식 SDK
5개 언어를 지원하는 공식 SDK
패키지 하나를 설치하면 내 서버에서 이미지, 영상, 오디오 모델을 호출할 수 있습니다. 인증과 결과 대기는 SDK가 처리하니, 요청을 작성하고 응답을 읽기만 하면 됩니다.
SDK가 대신 해 주는 일
함수를 한 번 호출하면 됩니다. 요청 서명, 파일 업로드, 결과 대기는 SDK가 처리하고, 결과는 타입이 지정된 데이터로 돌려줍니다.
- 내 코드함수 호출 한 번
- 공식 SDK요청 서명, 파일 업로드, 결과 대기
- 이미지·영상·오디오 모델웹사이트와 같은 모델, 같은 가격
이런 용도에 적합합니다
- 백엔드 서비스내 API나 작업 큐 안에서 미디어를 생성하세요.
- 스크립트와 일괄 작업스프레드시트나 예약 작업에서 작업을 여러 개 만드세요.
- 서버가 있는 앱키는 서버에 두고, 완성된 파일만 사용자에게 보내세요.
3단계로 시작하기
- 1단계
API 키 만들기
콘솔에서 키를 만들고 서버에 SPICY_API_KEY로 저장하세요.
- 2단계
패키지 설치
아래에서 사용하는 언어를 고르고 설치 명령을 실행하세요.
- 3단계
작업을 만들고 결과 읽기
프롬프트를 보내고 작업이 끝나길 기다린 뒤, 돌려받은 파일을 저장하세요.
언어 고르기
모든 SDK는 각 언어의 주요 레지스트리에 배포되며 같은 API와 통신합니다. 탭을 바꾸면 설치 명령과 전체 요청 예제를 볼 수 있습니다.
npm install @spicyapi/sdk
export SPICY_API_KEY="injected from your secret manager"import { randomUUID } from "node:crypto";
import { SpicyClient } from "@spicyapi/sdk";
const client = new SpicyClient();
const model = await client.getModel("MODEL_ID_FROM_CATALOG");
const task = await client.run(
{ model: model.model, input: { prompt: "A cinematic night portrait" } },
{ idempotencyKey: randomUUID() },
);
console.log(task.state, task.output?.assets?.[0]?.url);pip install spicyapi
export SPICY_API_KEY="injected from your secret manager"import uuid
from spicyapi import SpicyClient, output_assets
client = SpicyClient() # reads SPICY_API_KEY from the environment
model = client.get_model("MODEL_ID_FROM_CATALOG")
quote = client.quote_task(
model=model["model"],
input_data={"prompt": "A cinematic night portrait"},
)
print(quote["estimatedCost"], quote["maxCharge"]) # decide before you spend
task = client.create_task(
model=model["model"],
input_data={"prompt": "A cinematic night portrait"},
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"])Go
github.com/spicyapi-ai/spicy-goGo 1.23+ 필요
표준 라이브러리만 씁니다. 모듈 그래프에 새로 들어오는 게 없고, go get으로 지금 바로 됩니다.
go get github.com/spicyapi-ai/spicy-go
export SPICY_API_KEY="injected from your secret manager"client, err := spicy.New(spicy.Options{}) // reads SPICY_API_KEY from the environment
if err != nil {
return err
}
request := spicy.CreateTaskInput{
Model: "MODEL_ID_FROM_CATALOG",
Input: map[string]any{"prompt": "A cinematic night portrait"},
}
quote, err := client.Quote(ctx, request)
if err != nil {
return err
}
request.QuoteID = quote.QuoteID
request.ExpectedCost = quote.EstimatedCost
// Store this next to your own record of the job before sending it: its whole
// purpose is to still be there after the crash that lost the response.
key, err := spicy.NewIdempotencyKey()
if err != nil {
return err
}
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)
}composer require spicyapi/spicyapiuse SpicyApi\SpicyClient;
$client = new SpicyClient(); // reads SPICY_API_KEY from the environment
$quote = $client->quote('MODEL_ID_FROM_CATALOG', ['prompt' => 'A cinematic night portrait']);
printf("at most %s\n", $quote['maxCharge']); // decide before you spend
$task = $client->createTask(
model: $quote['model'],
input: ['prompt' => 'A cinematic night portrait'],
idempotencyKey: SpicyClient::idempotencyKey(),
quoteId: $quote['quoteId'],
expectedCost: $quote['estimatedCost'],
);
$final = $client->waitForTerminal($task['taskId']);
foreach (SpicyClient::readyAssets($final) as $asset) {
echo $asset['url'], "\n";
}Java
ai.spicyapi:spicyapi-java:0.1.0Java 17+, Jackson 필요
레코드와 Jackson, 그리고 생성과 대기를 한 번에 처리하는 run().
<dependency>
<groupId>ai.spicyapi</groupId>
<artifactId>spicyapi-java</artifactId>
<version>0.1.0</version>
</dependency>SpicyClient client = SpicyClient.builder().apiKeyFromEnvironment().build();
CreateTaskRequest request = CreateTaskRequest.of(
"MODEL_ID_FROM_CATALOG",
Map.of("prompt", "A cinematic night portrait"));
TaskQuote quote = client.quote(request); // free; reserves nothing
System.out.println("this will cost at most " + quote.maxCharge());
TaskRecord task = client.run( // createTask + waitForTerminal
request.withQuote(quote), UUID.randomUUID().toString());
// Transcription and similar endpoints answer in text and return no files at all.
task.outputText().ifPresent(System.out::println);
for (TaskAsset asset : task.assets()) {
asset.url().ifPresent(System.out::println);
}요청을 보낸 뒤에는
이미지와 영상 생성은 생성 호출이 끝난 뒤에도 계속됩니다. 작업 ID를 저장해 두고, 완료 여부는 Webhook이나 횟수를 제한한 상태 조회로 확인하세요.
- createTask멱등 키와 함께 보내면 같은 생성이 두 번 접수되지 않습니다.
- taskId응답은 즉시 돌아오고 작업은 뒤에서 계속 실행됩니다.
- Webhook / recordInfo서명된 웹훅이 우선이고 폴링은 복구 수단입니다.
- output.assets바로 쓸 수 있는 URL이 옵니다. 보관할 파일은 직접 복사하세요.
브라우저나 모바일 앱에서 호출할 때
클라이언트 앱에 넣은 키는 공개된 키나 마찬가지입니다. 키는 서버에 두세요. @spicyapi/proxy가 프런트엔드의 요청에 인증 정보를 붙여 주므로 앱은 키를 볼 수 없습니다.
- 앱 안에 키를 넣은 경우누구나 키를 읽고 사용자의 잔액을 사용할 수 있습니다.
- 키가 들어 있는 앱
- SpicyAPI
- 서버에 키를 둔 경우앱은 내 서버와 통신하고, 프록시가 키를 붙여 줍니다.
- 브라우저 또는 휴대폰
- 내 서버 + 프록시
- SpicyAPI
출시 전 체크리스트
- 01모델의 현재 사용 가능 상태를 확인합니다.
- 02모델 페이지의 정확한 필드로 검증합니다.
- 03API 키는 서버에만 둡니다.
- 04제출 전에 예상 비용을 보여 줍니다.
- 05taskId를 브라우저 세션 밖에 저장합니다.
- 06webhook 서명 또는 상태 조회 한도를 확인합니다.
- 07수락된 작업은 취소 불가로 처리합니다.
자주 만나는 오류 해결
처음 사용할 때 겪는 문제는 대부분 이 세 가지 중 하나입니다. 각 카드에서 증상과 바꿔야 할 항목을 확인하세요.
- 401
잘못된 API 키
키가 없거나 잘못된 키가 API에 전달되었습니다. 코드가 실행되는 환경에 SPICY_API_KEY를 설정하세요.
- 대기
작업이 아직 실행 중
영상은 몇 분 걸릴 수 있습니다. 시간 제한을 두고 기다리거나 Webhook을 쓰세요. 같은 Idempotency-Key 없이 다시 보내면 유료 작업이 하나 더 만들어집니다.
- 링크
결과 링크 만료
다운로드 링크는 유효 시간이 짧습니다. 예전 링크를 저장해 두지 말고, 작업에서 새 링크를 받으세요.
