연동 문서

OpenAI SDK 와 호환돼요. 바꾸는 건 base URL 과 API 키, 두 가지뿐이에요.

Base URL https://router.ethos.ai.kr/v1 인증 Authorization: Bearer ethos_live_…

빠른 시작

발급받은 키(ethos_live_…)를 넣고 base URL 을 바꿔요. 키는 발급하는 순간에 한 번만 보여요. 비밀번호 관리자에 보관하세요.

Python

from openai import OpenAI

client = OpenAI(
    base_url="https://router.ethos.ai.kr/v1",
    api_key="ethos_live_…",
)

res = client.chat.completions.create(
    model="anthropic/claude-sonnet-5",
    messages=[{"role": "user", "content": "안녕하세요"}],
    max_tokens=1000,
)
print(res.choices[0].message.content)

Node.js

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://router.ethos.ai.kr/v1",
  apiKey: process.env.ETHOS_ROUTER_KEY,
});

const res = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-5",
  messages: [{ role: "user", content: "안녕하세요" }],
});

curl

curl https://router.ethos.ai.kr/v1/chat/completions \
  -H "Authorization: Bearer $ETHOS_ROUTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "anthropic/claude-sonnet-5",
       "messages": [{"role": "user", "content": "안녕하세요"}]}'

모델 지정

모델 코드는 provider/model 형식이에요. 서빙 중인 전체 목록은 GET /v1/models 또는 단가표에서 볼 수 있어요.

예시설명
anthropic/claude-sonnet-5Claude Sonnet 5
openai/gpt-5.4GPT-5.4
google/gemini-3.7-flashGemini 3.7 Flash
upstage/solar-pro-4Solar Pro 4 (국산)
perplexity/sonar-pro웹검색이 들어 있어요. 토큰 말고 검색 건당 수수료가 따로 붙어요 (단가표)

버전 표기는 점(claude-sonnet-5, gpt-5.4)을 써요. 공급사마다 다른 표기는 라우터가 맞춰 줘요.

스트리밍

stream: true 를 그대로 써요. 마지막 usage 청크에 확정 비용이 실려요.

stream = client.chat.completions.create(
    model="anthropic/claude-sonnet-5",
    messages=[{"role": "user", "content": "안녕하세요"}],
    stream=True,
    stream_options={"include_usage": True},  # 마지막 청크에 usage.cost
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
    if chunk.usage:  # 마지막 청크
        print("\n비용:", chunk.usage.cost, "원")

stream_options.include_usage 를 안 보내도 라우터가 넣어 줘요. 스트리밍에서도 비용이 빠지지 않아요.

툴 호출 (function calling)

OpenAI 형식 그대로예요. 모델마다 안 맞는 부분(예를 들면 일부 모델의 reasoning_effort 와 툴 동시 사용 제약)은 라우터가 맞춰 주니까 클라이언트에서 따로 처리할 게 없어요.

res = client.chat.completions.create(
    model="openai/gpt-5.6-sol",
    messages=[{"role": "user", "content": "서울 날씨 알려줘"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "get_weather",
            "parameters": {"type": "object",
                "properties": {"city": {"type": "string"}}},
        },
    }],
)

이미지 입력

res = client.chat.completions.create(
    model="anthropic/claude-sonnet-5",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "이 이미지를 설명해줘"},
            {"type": "image_url",
             "image_url": {"url": "data:image/png;base64,…"}},
        ],
    }],
)

프리셋

모델과 파라미터, 시스템 프롬프트를 콘솔에서 묶어 두고 이름으로 불러요. 앱을 다시 배포하지 않고 지침만 바꿀 수 있어요.

res = client.chat.completions.create(
    model="preset/sales-bot",
    messages=[{"role": "user", "content": "환불 규정 알려줘"}],
)

프리셋의 시스템 프롬프트는 항상 맨 앞에 붙어요. 파라미터는 기본값이라 요청이 이기는데, 콘솔에서 재정의 방지를 켜면 프리셋 값이 이겨요.

키 관리 API

콘솔에서 매니지먼트 키를 발급하면 서빙 키를 코드로 만들고 지울 수 있어요. 고객사마다 키를 따로 내주는 구조라면 이걸로 자동화하세요.

curl -X POST https://router.ethos.ai.kr/manage/v1/keys \
  -H "Authorization: Bearer $ETHOS_MANAGE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"고객사 A","scope":"serve","monthlyLimitKrw":100000}'

매니지먼트 키로는 모델을 부를 수 없고, 매니지먼트 키를 또 만들 수도 없어요. 하나가 새더라도 그 하나만 버리면 되게 만들었어요.

명령줄 도구

키를 손으로 옮기지 않고 붙이는 길이에요. 한 번 로그인하면 코딩 도구가 에토스 라우터를 보게 돼요.

npm i -g ethos-router

ethos login     # 브라우저에서 승인하면 키가 저장돼요
ethos claude    # Claude Code 를 에토스 라우터로 실행
ethos codex     # Codex 를 에토스 라우터로 실행

승인하면 키가 두 개 저장돼요. 모델을 부르는 키와 사용 내역을 읽는 키가 따로예요. 하나가 새더라도 그 키로는 절반만 할 수 있어요. 콘솔의 API 키 목록에 CLI 로 뜨고, 거기서 정지할 수 있어요. ethos logout 은 설정만 지우는 게 아니라 그 두 키를 정지시켜요.

명령하는 일
ethos models [검색어]쓸 수 있는 모델과 단가
ethos chat "질문"한 번 물어보고 답과 든 비용을 봐요
ethos balance남은 크레딧
ethos usage --csv 내역.csv기간별 사용 내역을 엑셀 파일로
ethos env다른 도구에 넣을 환경변수를 찍어요
ethos doctor어디가 막혔는지 점검해요

브라우저가 없는 곳(CI)에서는 ethos login --with-key <키> 를 쓰거나 ETHOS_API_KEY 를 넣으세요.

Claude Code · Codex 연동

이 도구들은 OpenAI 형식이 아니라 앤트로픽 Messages 형식으로 말해요. 그래서 창구를 하나 더 열어 뒀어요. 바꾸는 건 여기도 base URL 과 키뿐이에요. 손으로 넣는 대신 명령줄 도구의 ethos claude 를 써도 돼요.

export ANTHROPIC_BASE_URL="https://router.ethos.ai.kr/claude"
export ANTHROPIC_AUTH_TOKEN="ethos_live_…"

claude          # 그대로 쓰면 돼요

직접 부를 때는 POST /claude/v1/messages 예요. 요청·응답 모두 앤트로픽 형식이고, stream: true 도 앤트로픽 이벤트로 나가요. 과금과 한도, 사용 기록은 /v1/chat/completions 와 똑같이 걸려요. 응답에 cost 를 하나 더 실어 주는데, 이 요청의 확정 원화 비용이에요.

이미지 생성

POST /v1/images/generations 도 OpenAI 호환이에요. 대화와 같은 키, 같은 원장을 써요.

res = client.images.generate(
    model="google/gemini-2.5-flash-image",
    prompt="한복 입은 고양이",
)
res.data[0].b64_json  # 이미지 바이트(base64)

이미지는 본문에 담아 돌려드려요. 저희가 파일을 보관하지 않아서 response_format="url" 은 받지 않고 오류로 알려드려요. 쓸 수 있는 모델은 단가표의 이미지에 있어요.

출력 토큰에 글자와 이미지가 섞여 오는 모델이라 토큰 단가로는 계산이 맞지 않아요. 공급사가 청구한 실제 금액을 그대로 환산해서 청구하고, 그 값이 응답의 cost 예요.

음성 받아쓰기

POST /v1/audio/transcriptions 도 OpenAI 호환이에요. 파일을 보내면 글자로 돌려드려요.

res = client.audio.transcriptions.create(
    model="google/gemini-3.8-flash",
    file=open("회의.wav", "rb"),
)
res.text

wav·mp3·m4a·flac·ogg·webm 을 받아요. response_format 은 json 과 text 를 지원하고, 자막(srt·vtt)은 아직 없어요. 고유명사가 많으면 prompt 에 미리 적어 주시면 인식률이 올라가요.

임베딩

POST /v1/embeddings 도 OpenAI 호환이에요. 대화와 같은 키, 같은 원장을 써요. 출력 토큰이 없어서 입력 토큰만 과금해요.

res = client.embeddings.create(
    model="openai/text-embedding-3-small",
    input=["문장 1", "문장 2"],
)
res.data[0].embedding  # [0.01, …]
res.usage.cost         # 확정 원화

서빙 중인 임베딩 모델은 GET /v1/models 응답의 kind 가 embedding 인 항목이에요.

비용 필드, usage.cost

모든 응답의 usage 에 이 요청의 확정 원화 비용이 실려요. 단가를 거꾸로 계산할 필요가 없어요.

{
  "usage": {
    "prompt_tokens": 3000,
    "completion_tokens": 500,
    "cost": 23.1,          // 원 (공급가액, VAT 별도)
    "upstream": "anthropic-direct"  // 실제 처리 경로
  }
}
비용은 매매기준율 × 공급사가 받는 값을 소수점 그대로 환산한 거예요. 적용 환율과 기준일, 출처는 GET /v1/prices 로 언제든 확인할 수 있어요. 캐시된 입력 토큰은 할인 단가로 계산해요.

비용 귀속 (부서·사용자 단위 집계)

요청 헤더로 비용을 어디에 달지 정할 수 있어요. 사용량 API 와 콘솔이 이 단위로 모아서 보여 줘요.

헤더용도
X-Ethos-Subject사용자·부서 식별자 (예: user-1234)
X-Ethos-Request-Id클라이언트 쪽 요청 식별자예요. 사용 기록에 그대로 남아요

이 헤더는 키에 허용된 경우에만 반영돼요 (키 발급할 때 정해요).

오류 코드

HTTP 상태와 error.code 로 구분해요. OpenAI SDK 의 예외 체계가 그대로 돌아가요.

HTTPcode의미 · 대응
401authentication_error키가 없거나 유효하지 않음 (정지·만료·허용되지 않은 위치 포함)
402prepaid_credit_exhausted워크스페이스 크레딧을 다 썼어요. 충전해야 해요
402credit_limit_exceeded일·월 한도 초과
403api_key_model_not_allowed이 키로 쓸 수 없는 모델
404model_not_found없는 모델 코드
429rate_limit_exceeded분당 한도를 넘었어요. Retry-After 헤더(초)만큼 기다렸다 다시 보내세요
503upstream_unavailable업스트림 경로가 전부 잠시 안 돼요. 조금 뒤에 다시 보내세요

업스트림에 장애가 나면 라우터가 먼저 다음 경로로 넘겨 보고, 그래도 안 되면 그때 503 을 줘요. 400 은 요청 자체의 문제이므로 재시도하지 마세요.

한도

축기본변경
분당 요청 (키)120 rpm키 발급 시 rpmLimit
일·월 크레딧 (키)없음dailyLimitKrw · monthlyLimitKrw
일·월 크레딧 (워크스페이스)없음워크스페이스 설정
IP 허용목록 (키)제한 없음ipAllow (단일 IP 또는 CIDR)

모든 요청은 호출하기 전에 잔액을 잡아 둬요. 크레딧이 모자라면 공급사를 부르지도 않고 402 로 거절하니까, 나중에 청구서가 따라오는 일이 없어요.

사용량 API

사용량 조회 전용 키(scope=usage)로 잔액과 사용량, 최근 요청을 봐요. 서빙 키로는 볼 수 없어요. 앱에 박히는 키로 회사 지출까지 읽히면 안 되니까요.

GET /usage/v1/billing                              # 잔액·이번 달 사용액
GET /usage/v1/usage?group_by=model&days=30         # 모델별 집계
GET /usage/v1/usage?group_by=usage_group&days=30   # 귀속(부서·사용자)별 집계
GET /usage/v1/logs?limit=20&hours=24               # 최근 요청 (실패 건 포함)
GET /usage/v1/logs?from=2026-09-05T00:00:00Z&to=2026-09-06T00:00:00Z  # 날짜로 좁혀 보기
GET /usage/v1/logs?limit=50&cursor=…                # 다음 쪽 (응답의 next_cursor)

기간은 hours 또는 from·to 로 줘요. 둘 다 주면 from·to 가 이겨요. 페이지는 응답의 next_cursor 를 cursor 로 다시 보내서 넘겨요. 로그는 보는 동안에도 위에 쌓이는데, 커서는 "이 지점보다 과거"라 이미 넘긴 쪽이 흔들리지 않아요.

같은 내용을 콘솔에서 화면으로도 볼 수 있어요.

키 발급

지금은 영업 문의로 워크스페이스와 키를 만들어 드려요. 문의: router@ethos.ai.kr