spicyapi문서
API로 개발하기
본문

견적과 프로토콜 호환성

돈을 내기 전에 요청 하나의 정확한 가격을 받고, 그 가격으로 제출하고, 응답을 잃어버렸을 때 복구하는 방법, 그리고 텍스트·영상 호환 API에 가격이 적용되는 방식을 설명합니다.

요청 하나의 정확한 견적 받기

견적은 돈을 내기 전에 요청 하나의 정확한 가격을 알려 줍니다. 견적은 무료이며, 작업을 만들지 않고 금액을 예약하지도 않습니다. 그다음 같은 요청에 견적을 붙여 제출하면, 그 요청은 화면에서 본 가격으로만 접수됩니다.

아래 명령은 macOS나 Linux 터미널용이며, 빠른 시작에서처럼 SPICY_API_KEY가 설정되어 있다고 가정합니다. MODEL_ID_FROM_CATALOG는 자리표시자입니다. 실시간 카탈로그(GET /api/v1/models)에 있는 model 값을 쓰고, input의 필드는 모두 그 모델의 스키마에서 가져오세요.

보낼 요청을 파일로 저장하기

request.json이라는 파일을 만들고, 실제로 제출할 model, input, 그리고 필요하면 callBackUrl을 그대로 적으세요.

request.json
{
  "model": "MODEL_ID_FROM_CATALOG",
  "input": { "prompt": "A cinematic night portrait" }
}

가격 묻기

curl https://api.spicyapi.ai/api/v1/jobs/quote \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @request.json
{
  "code": 200,
  "msg": "success",
  "data": {
    "quoteId": "QUOTE_ID_FROM_RESPONSE",
    "model": "MODEL_ID_FROM_CATALOG",
    "estimatedCost": "0.008",
    "maxCharge": "0.008",
    "currency": "USD",
    "quantity": "1",
    "unit": "per_image",
    "expiresAt": "2026-09-17T10:05:00Z"
  },
  "request_id": "req_01k3m8x9q2z4v7n5p6r8s0t1w2"
}

금액은 미국 달러 기준이며, 소수점이 있는 문자열로 옵니다. maxCharge는 이 요청에 청구될 수 있는 최대 금액이고, 지금은 항상 estimatedCost와 같습니다. quantity와 unit은 무엇을 세는지 보여 줍니다. 예를 들면 영상의 초 수입니다. 지출을 승인하는 사람에게 금액과 expiresAt을 보여 주세요.

견적은 실제 제출과 똑같은 검사를 거치므로, 이 단계에서 이미 실패할 수 있습니다. 400(입력이 모델의 스키마와 맞지 않음), 40201(잔액 부족), 40202(키에 걸린 지출 한도나 플랫폼 할당량에 걸림), 403(이 키로는 그 모델을 쓸 수 없음)이 그런 경우입니다.

같은 요청에 견적을 붙여 제출하기

5분 안에, 바꾸지 않은 요청을 createTask로 보내되 필드 두 개를 더하세요. 견적에서 받은 quoteId, 그리고 견적의 estimatedCost 값을 넣은 expectedCost입니다. 이 단계는 비용이 듭니다.

submit.json
{
  "model": "MODEL_ID_FROM_CATALOG",
  "input": { "prompt": "A cinematic night portrait" },
  "quoteId": "QUOTE_ID_FROM_RESPONSE",
  "expectedCost": "0.008"
}
SPICY_IDEMPOTENCY_KEY="$(uuidgen)"   # 새 작업마다 한 번 만듭니다. 바로 이 요청을 다시 보낼 때만 재사용하세요

curl https://api.spicyapi.ai/api/v1/jobs/createTask \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $SPICY_IDEMPOTENCY_KEY" \
  --data-binary @submit.json

응답은 빠른 시작에서 설명한 일반적인 202 Accepted 작업 응답입니다. 견적이 Idempotency-Key를 대신하지는 않으니 둘 다 보내세요.

견적이 묶여 있는 대상견적이 더는 통하지 않게 되는 경우
내 계정, 그리고 견적을 요청한 API 키다른 키로 제출할 때
정확한 model, input, callBackUrl그중 하나라도 바뀔 때. 프롬프트가 한 글자만 달라져도 해당됩니다. 필드 순서와 공백은 상관없습니다
그 시점의 가격5분이 지나거나 가격이 바뀔 때

40901은 견적이 더 이상 이 요청과 맞지 않는다는 뜻입니다

입력이 바뀌었거나, API 키가 다르거나, 견적이 만료되었거나, 가격이 바뀌었거나, expectedCost가 견적과 다른 경우는 모두 똑같이 드러납니다. 접수가 40901로 실패하고, 아무것도 과금되지 않습니다. 견적을 다시 받고 다시 확인을 받으세요. 가격 확인 없이 요청을 밀어 넣으려고 quoteId와 expectedCost를 빼지 마세요.

네이티브 텍스트 스트림인 POST /api/v1/jobs/stream도 같은 방식으로 견적을 받습니다. 텍스트와 스트리밍을 보세요.

응답을 잃어버렸을 때 복구하기

제출하다가 시간이 초과되었거나 연결이 끊겼다면, 요청이 접수되었는지 알 수 없습니다. createTask, retry, POST /v1/videos에서는 이렇게 하세요.

  1. 원래 요청을 원래 Idempotency-Key와 함께, 같은 API 키로 다시 보내세요. 원래 작업이 현재 상태 그대로 돌아오며, 두 번 과금되지 않습니다.
  2. 새 멱등 키로 작업을 하나 더 만들지 마세요. 새 키는 새로운 생성이자 새로운 과금을 뜻합니다.
  3. 이미 접수된 요청을 다시 보내는 것은 견적이 만료된 뒤에도 유효합니다. 만료는 접수 여부를 가를 뿐, 접수로 이미 만들어진 작업에는 영향을 주지 않습니다.
  4. 409가 "만들어지지 않았다"는 뜻은 아닙니다. 요청이 바뀌었거나(새 키가 필요한 별개의 작업입니다), 같은 계정의 다른 API 키로 보냈을 수 있습니다(원래 키로 다시 보내세요). 멱등성을 보세요.
  5. 최종 과금액은 recordInfo의 cost와 settled에서 읽으세요.

텍스트와 스트리밍 요청은 이 방법으로 복구할 수 없습니다

텍스트 요청(/api/v1/jobs/stream, 그리고 /v1과 /v1beta 아래의 텍스트 엔드포인트)의 답변은 생성되는 대로 전송될 뿐 저장되지 않습니다. 이미 접수된 키로 다시 보내면 답변 대신 오류가 돌아옵니다. 본문이 같으면 400, 다르면 409입니다. 추가로 과금되지는 않지만, 잃어버린 답변은 가져올 수 없습니다. 답변을 다시 받으려면 새 키로 요청을 보내고 그 비용을 내세요. 이 헤더를 읽는 엔드포인트는 멱등성을 보세요.

예상 금액은 정산 결과가 아닙니다

estimatedCost는 저희가 청구할 것으로 예상하는 금액입니다. 스트림의 텍스트 끝 표시는 데이터가 더 이상 오지 않는다는 뜻일 뿐입니다. 둘 중 어느 것도 과금이 확정되었다는 증거가 아닙니다. 작업이라면 recordInfo에서 cost와 함께 오는 settled: true가 그 증거입니다. 텍스트 호출은 작업 ID를 돌려주지 않습니다. 같은 API 키로 작업 목록을 조회하거나(GET /api/v1/jobs) 콘솔 → 로그에서 과금액을 찾고, settled가 true가 된 뒤에만 cost를 최종 금액으로 보세요.

공식 도구에서의 견적

CLI와 MCP 서버는 과금되는 호출을 하기 전마다 견적을 받고 사람에게 묻습니다. 그러니 이 단계를 직접 만들 필요가 없습니다.

  • CLI: spicyapi tasks quote는 요청의 가격을 계산합니다. spicyapi tasks create는 견적을 다시 받아 금액을 출력하고, 작업을 만들기 전에 묻습니다. 비용, 확인, 안전을 보세요.
  • MCP: 어시스턴트는 견적을 얼마든지 받을 수 있지만, 작업을 만들 때는 AI 클라이언트를 통해 가격을 보여 주며 나에게 묻습니다. 비용, 확인, 안전을 보세요.
  • SDK: quoteTask가 견적을 돌려줍니다. 그 quoteId와 estimatedCost를 createTask나 run에 넘기세요.

에이전트가 사용자 대신 확인하면 안 됩니다

확인 절차는 사람이 결정하도록 하려고 있는 것입니다. 이 확인에 스스로 답하는 에이전트나 자동 승인으로 설정된 클라이언트는, 그 경로에서 지출을 막는 유일한 경계를 없애 버립니다.

텍스트와 영상 호환

SpicyAPI는 텍스트 모델에 대해 OpenAI, Anthropic, Google Gemini 형식도 지원하고, 영상에 대해서는 OpenAI Videos 형식도 지원합니다. 전체 엔드포인트 목록은 API 레퍼런스에 있고, 프로토콜별 요청, 스트림, 오류는 텍스트와 스트리밍에 있습니다.

베이스 URL. OpenAI 형식은 https://api.spicyapi.ai/v1을 씁니다. Anthropic과 Google GenAI SDK는 /v1이나 /v1beta를 스스로 붙이므로, 이 두 SDK의 베이스 URL은 https://api.spicyapi.ai로 설정하세요.

인증 정보. 모든 형식에 API 키 하나를 씁니다. 키는 Authorization: Bearer 헤더에 넣습니다. Anthropic SDK가 쓰는 x-api-key 헤더와 Google GenAI SDK가 쓰는 x-goog-api-key 헤더도 받습니다. URL에 넣은 ?key=는 어떤 경우에도 받지 않습니다.

모델. 실시간 카탈로그에서 정확한 모델을 고르세요. 도구, 이미지 메시지, 생성 옵션이 작동하는지는 고른 프로토콜이 아니라 그 모델의 스키마가 정합니다.

응답 형태. 각 프로토콜은 플랫폼의 {code, msg, data, request_id} 외피가 아니라 자기 고유의 외피로 응답합니다.

추론 필드

추론 텍스트를 제공하는 채팅 모델은 message.reasoning_content에, 스트리밍할 때는 delta.reasoning_content에 선택적인 문자열을 담아 반환할 수 있습니다. 도구 호출 뒤에 대화를 이어 갈 때, 실시간 모델 스키마가 지원한다면 이 값을 해당하는 어시스턴트 메시지에 보존할 수 있습니다.

usage.completion_tokens_details.reasoning_tokens는 completion_tokens에 이미 포함된 추론 부분을 나타내므로 다시 더하지 마세요. 모든 모델이 추론 텍스트나 이런 토큰 세부 정보를 반환하지는 않습니다.

만들기 전에 알아 둘 두 가지 형태

  • Responses에는 전체 대화를 input에 담아 보내야 합니다. previous_response_id는 지원하지 않습니다.
  • Videos는 model, prompt, input_reference, seconds, size가 담긴 JSON을 받으며, 이 값들은 그 모델 고유의 필드로 옮겨집니다. 반환된 영상 id는 recordInfo에 넣을 수 있는 작업 ID이기도 합니다. 영상 콘텐츠 엔드포인트는 HTTP 302로, 짧은 시간만 유효한 서명된 URL로 리디렉션합니다.

호환 API는 접수 시점의 가격을 적용합니다

quoteId와 expectedCost는 jobs/createTask와 jobs/stream에만 적용됩니다. /v1과 /v1beta는 요청이 접수될 때 적용되는 가격을 쓰고 이 두 필드를 거부하므로, 이 경로에서는 호출 한 번 단위로 가격을 확인받을 수 없습니다. 워크플로에서 청구 한도를 반드시 확인받아야 한다면 네이티브 작업 API를 쓰세요. 키 하나가 쓸 수 있는 전체 금액에 상한을 두려면 인증에 설명된 대로 키의 일·월·누적 한도를 거세요.

공식 OpenAI, Anthropic, Google 클라이언트로 채팅을 스트리밍하는 예제는 공식 SDK 쓰기를 보세요. CLI와 MCP 서버는 네이티브 작업을 제출하고 추적할 뿐, 채팅 토큰을 스트리밍하지는 않습니다.

이 페이지에서