채팅은 대화를 바탕으로 텍스트를 생성합니다. Sakuranbo AI에는 채팅 엔드포인트가 두 개 있습니다. 두 엔드포인트는 같은 모델을 실행하고 같은 방식으로 과금하며, 요청과 응답의 형식만 다릅니다.
POST /v1/chat/completions엔드포인트는 OpenAI Chat Completions API와 호환됩니다.POST /v1/messages엔드포인트는 Anthropic Messages API와 호환됩니다.
두 엔드포인트 모두 Authorization: Bearer로 보내는 API 키가 필요합니다. 인증을 참고하세요. POST /v1/messages 엔드포인트는 Anthropic SDK가 키를 담아 보내는 x-api-key 헤더로 보낸 키도 받습니다.
이 페이지의 예시에서 사용하는 모델은 sakuranbo/cherrytext-1.0-flash입니다. 텍스트 모델을 찾으려면 task=chat으로 GET /v1/models 엔드포인트를 호출하세요. 모델 API를 참고하세요. 텍스트 모델이 아닌 모델을 지정하면 404와 model_not_found가 반환됩니다.
공식 SDK 사용하기
섹션 제목: “공식 SDK 사용하기”OpenAI와 Anthropic의 공식 SDK를 사용할 수 있습니다. SDK가 Sakuranbo AI를 가리키도록 설정하고 Sakuranbo AI API 키를 지정하세요. OpenAI SDK는 /v1을 포함한 기본 URL을 기대합니다. Anthropic SDK는 /v1/messages를 스스로 덧붙이므로 기본 URL에 /v1을 포함하지 않습니다.
TypeScript용 OpenAI SDK:
import OpenAI from "openai";
const apiKey = process.env.SAKURANBO_API_KEY;if (!apiKey) throw new Error("Set the SAKURANBO_API_KEY environment variable.");
const client = new OpenAI({ baseURL: "https://api.sakuranbo.ai/v1", apiKey,});
const completion = await client.chat.completions.create({ model: "sakuranbo/cherrytext-1.0-flash", messages: [ { role: "system", content: "You are a concise assistant." }, { role: "user", content: "Summarize the benefits of prepaid API credits in two lines." } ], max_tokens: 2048});
console.log(completion.choices[0]?.message.content);Python용 OpenAI SDK:
import os
from openai import OpenAI
client = OpenAI( base_url="https://api.sakuranbo.ai/v1", api_key=os.environ["SAKURANBO_API_KEY"],)
completion = client.chat.completions.create( model="sakuranbo/cherrytext-1.0-flash", messages=[ { "role": "system", "content": "You are a concise assistant.", }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ], max_tokens=2048,)
print(completion.choices[0].message.content)TypeScript용 Anthropic SDK:
import Anthropic from "@anthropic-ai/sdk";
const apiKey = process.env.SAKURANBO_API_KEY;if (!apiKey) throw new Error("Set the SAKURANBO_API_KEY environment variable.");
// Pass the key as authToken too, so the SDK does not send ANTHROPIC_AUTH_TOKEN from the environment.const client = new Anthropic({ baseURL: "https://api.sakuranbo.ai", apiKey, authToken: apiKey,});
const message = await client.messages.create({ model: "sakuranbo/cherrytext-1.0-flash", max_tokens: 2048, system: "You are a concise assistant.", messages: [ { role: "user", content: "Summarize the benefits of prepaid API credits in two lines." } ]});
console.log(message.content);Python용 Anthropic SDK:
import os
import anthropic
api_key = os.environ["SAKURANBO_API_KEY"]
# Pass the key as auth_token too, so the SDK does not send ANTHROPIC_AUTH_TOKEN from the environment.client = anthropic.Anthropic( base_url="https://api.sakuranbo.ai", api_key=api_key, auth_token=api_key,)
message = client.messages.create( model="sakuranbo/cherrytext-1.0-flash", max_tokens=2048, system="You are a concise assistant.", messages=[ { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ],)
print(message.content)API는 서버에서 호출하세요. API 키를 브라우저에서 실행되는 코드에 넣지 마세요.
OpenAI 형식
섹션 제목: “OpenAI 형식”POST /v1/chat/completions 엔드포인트는 messages 목록을 받아 모델의 다음 메시지를
반환합니다.
curl -X POST "https://api.sakuranbo.ai/v1/chat/completions" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $SAKURANBO_API_KEY" \ -d '{ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant." }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ], "max_tokens": 2048}'const response = await fetch("https://api.sakuranbo.ai/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.SAKURANBO_API_KEY}`, }, body: JSON.stringify({ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant." }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ], "max_tokens": 2048 }),});const data = await response.json();console.log(data);import os
import requests
response = requests.post( "https://api.sakuranbo.ai/v1/chat/completions", headers={ "Authorization": f"Bearer {os.environ['SAKURANBO_API_KEY']}", }, json={ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant.", }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ], "max_tokens": 2048, },)print(response.json())생성된 메시지는 choices[0].message에 있고, usage에는 토큰 수와 비용이 들어 있습니다. 사용량과 비용을 참고하세요.
요청 필드
섹션 제목: “요청 필드”| 이름 | 타입 | 필수 | 기본값 | 제약 |
|---|---|---|---|---|
model | string | 예 | ||
messages | object[] | 예 | 1개 이상 | |
max_tokens | integer | 아니요 | ||
max_completion_tokens | integer | 아니요 | ||
temperature | number | 아니요 | 0~2 | |
top_p | number | 아니요 | 0~1 | |
stop | string | string[] | 아니요 | string[]: 최대 4개 | |
seed | integer | 아니요 | 1 이상 | |
user | string | 아니요 | 최대 512자 | |
tools | object[] | 아니요 | 최대 128개 | |
tool_choice | string | object | 아니요 | string: 다음 중 하나: none, auto, required | |
response_format | object | 아니요 | ||
n | number | 아니요 | 다음 중 하나: 1 | |
stream | boolean | 아니요 | ||
stream_options | object | null | 아니요 |
파라미터의 범위와 기본값은 모델마다 다릅니다. 이 표는 예시 모델의 것입니다.
| 이름 | 타입 | 필수 | 기본값 | 제약 | 설명 |
|---|---|---|---|---|---|
messages | object[] | 예 | OpenAI Chat Completions 형식의 대화 메시지. | ||
max_tokens | integer | 아니요 | 4096 | 1~131,072 | 추론에 사용되는 토큰을 포함한 최대 생성 토큰 수. 값이 너무 작으면 답변을 쓰기 전에 응답이 잘릴 수 있습니다. 잔액 홀드는 이 값으로 계산됩니다. |
temperature | number | 아니요 | 1 | 0~2 | 샘플링 온도. |
stream | boolean | 아니요 | false | 토큰을 Server-Sent Events로 스트리밍합니다. |
max_tokens가 범위를 벗어나면 400과 invalid_request가 반환됩니다. error.details.minimum과 error.details.maximum에 허용 범위가 담깁니다. 요청에 max_tokens와 max_completion_tokens가 모두 있으면 max_completion_tokens를 사용합니다.
함수 도구(tools와 tool_choice)와 response_format을 지원합니다. 도구 호출은 choices[0].message.tool_calls로 돌아옵니다.
메시지에는 텍스트만 담을 수 있습니다. 이미지처럼 다른 유형의 콘텐츠 파트는 400을 반환하며, error.param이 해당 파트를 가리킵니다. 필드 표에 없는 파라미터도 400을 반환하며, error.param은 그 파라미터의 이름입니다.
assistant 메시지에 텍스트도 tool_calls 항목도 없거나 content 배열이 비어 있으면 400을 반환합니다. 추론 도중 출력이 끊기면 텍스트도 도구 호출도 없는 응답이 올 수 있으므로, 그런 응답은 대화 기록에 넣지 말고 max_tokens를 늘려서 다시 요청하세요.
Anthropic 형식
섹션 제목: “Anthropic 형식”POST /v1/messages 엔드포인트는 Anthropic Messages API 형식의 대화를 받습니다.
curl -X POST "https://api.sakuranbo.ai/v1/messages" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $SAKURANBO_API_KEY" \ -d '{ "model": "sakuranbo/cherrytext-1.0-flash", "max_tokens": 2048, "system": "You are a concise assistant.", "messages": [ { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ]}'const response = await fetch("https://api.sakuranbo.ai/v1/messages", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.SAKURANBO_API_KEY}`, }, body: JSON.stringify({ "model": "sakuranbo/cherrytext-1.0-flash", "max_tokens": 2048, "system": "You are a concise assistant.", "messages": [ { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ] }),});const data = await response.json();console.log(data);import os
import requests
response = requests.post( "https://api.sakuranbo.ai/v1/messages", headers={ "Authorization": f"Bearer {os.environ['SAKURANBO_API_KEY']}", }, json={ "model": "sakuranbo/cherrytext-1.0-flash", "max_tokens": 2048, "system": "You are a concise assistant.", "messages": [ { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ], },)print(response.json())요청 필드
섹션 제목: “요청 필드”| 이름 | 타입 | 필수 | 기본값 | 제약 |
|---|---|---|---|---|
model | string | 예 | ||
messages | object[] | 예 | 1개 이상 | |
system | string | object[] | 아니요 | ||
max_tokens | integer | 예 | ||
temperature | number | 아니요 | 0~1 | |
top_p | number | 아니요 | 0~1 | |
stop_sequences | string[] | 아니요 | 최대 4개 | |
stream | boolean | 아니요 | ||
tools | object[] | 아니요 | 최대 128개 | |
tool_choice | object | 아니요 |
OpenAI 형식과 다른 점은 다음과 같습니다.
max_tokens는 필수입니다.system은 최상위 필드입니다. 역할이system인 메시지는400을 반환합니다.temperature의 허용 범위는 Anthropic API를 따르므로POST /v1/chat/completions엔드포인트의 범위와 다릅니다.anthropic-version헤더는 필요하지 않습니다. SDK가 보내더라도 서버는 무시합니다.- 메시지에는 텍스트와 도구 블록만 담을 수 있습니다.
image블록은400을 반환합니다.top_k나thinking처럼 필드 표에 없는 파라미터도400을 반환합니다. assistant메시지에는 비어 있지 않은 문자열content, 또는text나tool_use블록이 있는content배열이 필요합니다. 추론 도중 출력이 끊겨content가 빈 배열로 온 응답을 대화 기록에 넣으면400을 반환하므로,max_tokens를 늘려서 다시 요청하세요.- 모든 오류 본문은 Anthropic 형식을 따릅니다.
type은error이고, 자세한 내용은error에 들어 있으며,request_id는 최상위 필드입니다.401,404,405도 마찬가지입니다.
스트리밍
섹션 제목: “스트리밍”stream을 true로 설정하면 응답을 server-sent events로 받습니다. 첫 토큰은 모델이 만들어 내는 즉시 도착합니다.
curl -N -X POST "https://api.sakuranbo.ai/v1/chat/completions" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $SAKURANBO_API_KEY" \ -d '{ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant." }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ], "max_tokens": 2048, "stream": true, "stream_options": { "include_usage": true }}'const response = await fetch("https://api.sakuranbo.ai/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.SAKURANBO_API_KEY}`, }, body: JSON.stringify({ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant." }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines." } ], "max_tokens": 2048, "stream": true, "stream_options": { "include_usage": true } }),});const reader = response.body!.getReader();const decoder = new TextDecoder();while (true) { const { done, value } = await reader.read(); if (done) break; process.stdout.write(decoder.decode(value, { stream: true }));}import os
import requests
response = requests.post( "https://api.sakuranbo.ai/v1/chat/completions", headers={ "Authorization": f"Bearer {os.environ['SAKURANBO_API_KEY']}", }, json={ "model": "sakuranbo/cherrytext-1.0-flash", "messages": [ { "role": "system", "content": "You are a concise assistant.", }, { "role": "user", "content": "Summarize the benefits of prepaid API credits in two lines.", }, ], "max_tokens": 2048, "stream": True, "stream_options": { "include_usage": True, }, }, stream=True,)for line in response.iter_lines(decode_unicode=True): if line: print(line)OpenAI 형식에서는 각 이벤트가 chat.completion.chunk 객체를 담은 data: 줄이며, choices[].delta에 새 텍스트가 들어 있습니다. 스트림은 data: [DONE]으로 끝납니다. 비용이 담긴 usage 객체는 choices 배열이 빈 마지막 청크로 도착하며, stream_options.include_usage를 true로 설정한 경우에만 도착합니다.
Anthropic 형식에서는 각 이벤트에 event: 줄과 data: 줄이 있습니다. 이벤트는 다음 순서로 도착합니다.
message_start- 콘텐츠 블록마다
content_block_start, 하나 이상의content_block_delta이벤트,content_block_stop stop_reason과 비용을 포함한usage가 들어 있는message_deltamessage_stop
Anthropic SDK는 자신이 아는 필드만으로 최종 메시지를 구성하므로 usage.cost는 최종 메시지에 들어 있지 않습니다. 비용을 읽으려면 message_delta 이벤트의 usage를 읽으세요.
스트림이 시작되기 전의 실패(인증 오류나 검증 오류 등)는 HTTP 상태와 함께 일반 JSON 오류를 반환합니다. 스트림이 시작된 뒤의 실패는 상태를 바꿀 수 없으므로 상태는 200으로 유지됩니다. OpenAI 형식에서는 스트림이 error 객체가 담긴 data: 줄을 보낸 다음 data: [DONE]을 보냅니다. Anthropic 형식에서는 event: error 이벤트를 보내며 message_stop은 보내지 않습니다.
사용량과 비용
섹션 제목: “사용량과 비용”스트리밍이 아닌 응답은 비용을 두 곳에, 토큰 수를 usage에 담아 보고합니다.
usage.cost에는 micro USD 단위의 금액이amount_micro_usd로, 십진수 문자열 금액이amount_usd로 들어 있습니다.X-Usage-Cost-Usd헤더에 같은 USD 문자열이 들어 있습니다.usage에는 토큰 수가 들어 있습니다. OpenAI 형식에서는prompt_tokens,completion_tokens,total_tokens,prompt_tokens_details.cached_tokens이고, Anthropic 형식에서는input_tokens,output_tokens,cache_read_input_tokens입니다.input_tokens에는 캐시된 토큰이 포함되지 않습니다.
스트리밍 응답에는 X-Usage-Cost-Usd 헤더가 없습니다. 대신 마지막 usage 객체에서 비용을 읽으세요.
요청을 실행하기 전에 Sakuranbo AI는 잔액에서 일정 금액을 확보합니다. 확보 금액은 입력의 크기와 max_tokens로 예상합니다. 요청이 끝나면 실제 토큰 수로 청구액을 계산하고, 확보했던 금액의 나머지는 잔액으로 돌아옵니다. 출력을 하나도 생성하기 전에 모델 쪽에서 실패한 요청은 과금되지 않습니다.
- 확보 금액은
max_tokens를 사용해 계산하므로, 실제 비용은 잔액으로 충분하더라도 요청이402로 거부될 수 있습니다.max_tokens는 필요한 길이로 설정하세요. - 모델이 출력을 생성하기 시작한 뒤 스트림이 끊기면, 스트림이 오류로 끝나더라도 요청은 과금됩니다. 출력에는 추론 출력이 포함되며, 이용자에게 도달했는지와 관계없이 과금됩니다. Anthropic 형식은 추론 출력을 반환하지 않으므로, 텍스트를 하나도 받지 못했더라도 과금될 수 있습니다. 최종 토큰 수가 도착하지 않았다면 청구액을 추정해 계산합니다. 입력은 확보에 사용한 상한으로, 출력은 추론 출력과 도구 호출을 포함해 이미 생성된 출력의 크기로 계산하므로 청구액이 실제 사용량보다 높을 수 있습니다. 확보한 금액을 넘지는 않습니다.
- 스트림이 끝나기 전에 연결을 닫아도 요청은 끝까지 실행되며, 생성된 출력 전체가 과금됩니다.
- 채팅 요청은
Idempotency-Key를 사용하지 않습니다. 재시도한 요청은 새 요청이며 다시 과금됩니다.
가격은 모델 카탈로그의 모델 페이지와 요금 페이지에서 확인하거나, 요청을 실행하기 전에 POST /v1/estimate 엔드포인트로 계산할 수 있습니다. 비용 계산 API를 참고하세요. 잔액을 확인하려면 잔액과 API 키 사용 한도를 참고하세요.
| 코드 | HTTP 상태 | 유형 | 설명 |
|---|---|---|---|
invalid_request | 400 | invalid_request_error | 요청 형식이 올바르지 않거나 값이 허용 범위를 벗어났습니다. 유효한 값을 알 수 있는 경우 details.allowed에 담깁니다. |
unauthorized | 401 | authentication_error | API 키가 없거나, 형식이 올바르지 않거나, 존재하지 않거나, 폐기되었거나, 만료되었습니다. 응답은 그중 어느 경우인지 알려 주지 않습니다. |
balance_insufficient | 402 | insufficient_balance | 사용 가능한 잔액이 이 요청에 확보해야 하는 금액보다 적습니다. error.details.limit_source는 balance입니다. |
key_limit_exceeded | 402 | insufficient_balance | 이 요청을 실행하면 API 키가 사용 한도를 넘게 됩니다. error.details.limit_source는 key이고, error.details.resets_at에 카운터가 다시 시작되는 시각이 담깁니다. |
account_suspended | 403 | permission_error | 계정이 정지되어 이미지, 영상, 채팅 요청이 거부됩니다. 지원팀에 문의하세요. 잔액, 입금, API 키, 작업은 계속 사용할 수 있습니다. |
model_not_found | 404 | not_found_error | 카탈로그에 없는 모델 ID입니다. 채팅 엔드포인트는 텍스트 모델이 아닌 모델에도 이 코드를 반환합니다. |
payload_too_large | 413 | invalid_request_error | 요청 본문이 허용 크기를 초과했습니다. error.details.max_bytes가 허용 크기입니다. |
rate_limited | 429 | rate_limit_error | 모델 제공업체가 요청을 제한하고 있습니다. POST /v1/reports는 같은 주소에서 신고가 너무 많이 들어올 때도 이 코드를 반환합니다. Retry-After 헤더에 표시된 시간이 지난 뒤 다시 시도하세요. |
upstream_error | 502 | upstream_error | 업스트림 제공사가 오류를 반환했습니다. |
upstream_unavailable | 503 | overloaded_error | 업스트림 제공사의 용량이 일시적으로 부족합니다. 잠시 후 다시 시도하세요. |
POST /v1/messages 엔드포인트는 같은 코드와 HTTP 상태를 Anthropic 오류 형식으로
반환합니다. error.type은 Anthropic API를 따르므로 유형 열과 다를 수 있습니다.
balance_insufficient와 key_limit_exceeded는 billing_error, payload_too_large는
request_too_large, content_policy_violation은 invalid_request_error, upstream_error는
api_error입니다. 분기에는 error.type이 아니라 error.code를 사용하세요.
429나 503 응답을 받으면 Retry-After 헤더에 표시된 초만큼 기다린 뒤 다시 시도하세요. 402 응답에는 balance 또는 key인 error.details.limit_source가 있습니다. 잔액과 API 키 사용 한도를 참고하세요. 전체 코드 목록은 오류를 참고하세요.
모든 가격은 USD 기준이며, 표시 가격에는 당사의 마진이 포함되어 있습니다.