(3/3) LLM API 비용 추적 — 공식 비용 API 대사와 예산 알림 자동화
- 과금 구조와 usage 필드 — 4사 가격표 한눈에
- 파이썬으로 4사 통합 비용 트래커 만들기
- 공식 비용 API 대사와 예산 알림 자동화 ← 지금 글
Summary
2편의 트래커는 우리 쪽에서 관측한 추정치예요. 잘 만들어도 청구서와 몇 % 는 어긋날 수 있습니다. 반올림 정책, 서버 도구(웹 검색·코드 실행) 과금, 프로모션 크레딧 차감 같은 건 응답 usage 에 안 보이거든요. 그래서 마지막 편은 각 사가 공식으로 집계해주는 데이터와 대사(reconciliation) 하고, 사람이 안 챙겨도 굴러가는 알림 루틴을 만드는 이야기입니다.
💡 이 글에서 다루는 것
- Anthropic Usage & Cost Admin API — 토큰과 달러를 조직 단위로 뽑기
- OpenAI Usage / Costs API — 프로젝트별 집계
- 공식 API 가 없는 Gemini · Upstage 는 어떻게 하나
- 추정치 ↔ 공식치 대사 스크립트
- LiteLLM 게이트웨이로 키별 예산 상한 걸기
- Slack 일일 비용 리포트 + 예산 초과 알림
- 비용 절감 체크리스트 (캐싱 · 배치 · 모델 라우팅)
1. 왜 응답 usage 만으로는 부족한가
2편 방식(응답 usage 합산)의 빈틈을 먼저 인정하고 갑니다.
| 빈틈 | 내용 |
|---|---|
| 서버 도구 과금 | 웹 검색·코드 실행은 토큰 외 과금이라 usage 합산에 안 잡힘 |
| 누락 호출 | 트래커를 안 거친 호출(동료의 실험, 콘솔 Workbench)은 기록에 없음 |
| 크레딧·할인 | 프로모션 크레딧, 계약 단가는 우리 가격표와 다름 |
| 반올림 | 각 사 내부 집계와 우리 계산의 미세한 차이 |
그래서 운영 루틴은 이렇게 굴러갑니다 — 실시간 감시는 2편 트래커로, 정산은 공식 API 로. 둘의 차이가 일정 범위(저는 2%)를 넘으면 그때 원인을 조사해요.
2. Anthropic — Usage & Cost Admin API
네 회사 중 가장 잘 갖춰진 쪽이에요. 조직 단위 사용량과 달러 금액까지 API 로 줍니다.
먼저 준비물이 하나 있어요. 일반 API 키가 아니라 Admin API 키(sk-ant-admin01-... 형태)가 필요합니다. Console → Settings → Organization 에서 조직을 만들어야 발급되고, 개인 계정에는 없어요.
사용량 — usage_report
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2026-06-29T00:00:00Z&\
ending_at=2026-07-06T00:00:00Z&\
group_by[]=model&\
bucket_width=1d" \
--header "anthropic-version: 2023-06-01" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY"
응답은 시간 버킷별로 비캐시 입력 · 캐시 생성 · 캐시 읽기 · 출력 토큰이 나뉘어 옵니다. group_by 에 model 외에 workspace_id, api_key_id, service_tier 를 줄 수 있어서 “어느 워크스페이스가 얼마 썼나” 같은 조직 질문에 바로 답해요. 버킷 폭은 1m/1h/1d 세 가지고, 1d 는 한 번에 최대 31버킷까지 줍니다.
비용 — cost_report
curl "https://api.anthropic.com/v1/organizations/cost_report?\
starting_at=2026-06-01T00:00:00Z&\
ending_at=2026-07-01T00:00:00Z&\
group_by[]=description" \
--header "anthropic-version: 2023-06-01" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY"
이쪽은 아예 USD 금액을 돌려줘요(센트 단위 십진 문자열). 웹 검색·코드 실행 비용도 description 그룹으로 잡히기 때문에, 2편 트래커가 못 보는 빈틈이 여기서 메워집니다. 데이터는 호출 후 보통 5분 안에 반영되고, 폴링은 분당 1회 정도가 권장이에요. 상세 스펙은 Usage & Cost API 문서에 있습니다.
3. OpenAI — Usage / Costs API
OpenAI 도 조직 단위 집계 API 를 제공합니다. 이쪽도 일반 키가 아니라 Admin 키가 필요해요 (Settings → Organization → Admin keys).
# 토큰 사용량 (모델별, 일 버킷)
curl "https://api.openai.com/v1/organization/usage/completions?\
start_time=1751068800&bucket_width=1d&group_by=model" \
--header "Authorization: Bearer $OPENAI_ADMIN_KEY"
# 달러 비용 (프로젝트별)
curl "https://api.openai.com/v1/organization/costs?\
start_time=1751068800&bucket_width=1d&group_by=project_id" \
--header "Authorization: Bearer $OPENAI_ADMIN_KEY"
Anthropic 과 구조가 거의 같은데 두 가지가 달라요. 시간 파라미터가 ISO 문자열이 아니라 유닉스 타임스탬프이고, 사용량은 usage/completions 외에 usage/embeddings, usage/images 처럼 상품별 엔드포인트가 분리돼 있습니다. costs 는 일 단위 버킷만 지원해요. 상세 스펙은 OpenAI API 레퍼런스에서 확인하세요.
4. Gemini · Upstage — 공식 API 가 없을 때
여기서부터 온도차가 납니다.
Gemini 는 AI Studio 키 기준으로는 조직 사용량 API 가 따로 없어요. 콘솔의 사용량 페이지를 눈으로 보거나, Vertex AI 경유라면 Google Cloud 결제 데이터를 BigQuery 로 내보내서 SQL 로 집계하는 경로가 정석입니다. 프로젝트 라벨을 잘 달아두면 결제 데이터에서 서비스별 분리가 돼요.
Upstage 는 콘솔의 사용량 화면이 기본이고, 집계 API 는 제공 목록에서 못 찾았습니다(2026년 7월 기준).
그래서 이 두 곳은 결론이 명확해요 — 2편의 응답 기반 로깅이 사실상 유일한 상시 관측 수단입니다. 공식 API 가 없는 프로바이더일수록 호출 시점 기록을 빠뜨리면 복구할 방법이 없어요. 월 1회 콘솔 화면의 월 합계와 트래커 합계를 수동으로 맞춰보는 정도가 현실적인 대사입니다.
5. 추정치 ↔ 공식치 대사 스크립트
Anthropic 기준으로 “어제 하루” 를 맞춰보는 스크립트예요. 2편의 SQLite 와 cost_report 를 나란히 놓습니다.
import os, sqlite3, requests
from datetime import date, timedelta
def anthropic_official_cost(day):
"""cost_report 에서 특정 날짜의 총비용(USD)을 가져온다."""
r = requests.get(
"https://api.anthropic.com/v1/organizations/cost_report",
params={
"starting_at": f"{day}T00:00:00Z",
"ending_at": f"{day + timedelta(days=1)}T00:00:00Z",
},
headers={
"anthropic-version": "2023-06-01",
"x-api-key": os.environ["ANTHROPIC_ADMIN_KEY"],
},
)
r.raise_for_status()
cents = sum(
float(item["amount"])
for bucket in r.json()["data"]
for item in bucket["results"]
)
return cents / 100
def tracked_cost(day, provider="anthropic"):
with sqlite3.connect("llm_usage.db") as conn:
row = conn.execute(
"SELECT COALESCE(SUM(cost_usd), 0) FROM llm_usage "
"WHERE provider = ? AND substr(ts, 1, 10) = ?",
(provider, str(day)),
).fetchone()
return row[0]
yesterday = date.today() - timedelta(days=1)
official = anthropic_official_cost(yesterday)
tracked = tracked_cost(yesterday)
diff_pct = abs(official - tracked) / official * 100 if official else 0
print(f"{yesterday} 공식 ${official:.2f} vs 추정 ${tracked:.2f} (차이 {diff_pct:.1f}%)")
2026-07-05 공식 $7.12 vs 추정 $6.94 (차이 2.5%)
차이 2.5% — 제 경우 원인은 웹 검색 도구 과금이었어요. 이렇게 차이의 원인을 한 번 규명해두면, 그 다음부터는 같은 수준의 차이는 무시하고 갑자기 벌어질 때만 조사하면 됩니다.
6. 게이트웨이로 예산 상한 걸기 — LiteLLM
추적이 되면 다음 욕심은 통제예요. “이 키는 월 $50 까지만” 같은 상한은 애플리케이션 코드가 아니라 게이트웨이 계층에서 거는 게 깔끔합니다. 오픈소스 LiteLLM 프록시가 이 용도로 가장 널리 쓰여요.
4사를 한 프록시 뒤에 두는 설정은 이렇게 생겼습니다. Upstage 는 OpenAI 호환이라 base_url 지정으로 붙어요.
# litellm_config.yaml
model_list:
- model_name: claude-opus
litellm_params:
model: anthropic/claude-opus-4-8
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: gpt-mini
litellm_params:
model: openai/gpt-5.4-mini
api_key: os.environ/OPENAI_API_KEY
- model_name: gemini-flash
litellm_params:
model: gemini/gemini-2.5-flash
api_key: os.environ/GEMINI_API_KEY
- model_name: solar-pro
litellm_params:
model: openai/solar-pro3 # OpenAI 호환 모드
api_base: https://api.upstage.ai/v1
api_key: os.environ/UPSTAGE_API_KEY
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: os.environ/DATABASE_URL # 지출 기록용 Postgres
프록시를 띄운 뒤 팀원·서비스별 가상 키를 만들면서 예산을 겁니다.
curl -s http://localhost:4000/key/generate \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{"key_alias": "doc-summary-batch", "max_budget": 50.0, "budget_duration": "30d"}'
이 키로 들어오는 호출은 LiteLLM 이 자체 가격표로 지출을 집계하다가, $50 을 넘는 순간 요청을 거절해요. “월말에 알게 되는 초과”가 “그 자리에서 막히는 초과”로 바뀌는 것이 게이트웨이의 가장 큰 가치입니다. 부수 효과로 4사 호출이 한 지점을 지나니까 2편의 로깅도 게이트웨이 한 곳으로 모을 수 있어요.
⚠️ 다만 게이트웨이는 운영 부담(가용성, 버전 업, 가격표 최신화)이 생기는 선택이에요. 혼자 쓰는 프로젝트라면 2편 트래커 + 이 글의 알림으로 충분하고, 팀·조직 규모에서 키 관리가 필요해질 때 도입하는 걸 추천드립니다.
7. Slack 일일 리포트 + 예산 알림
마지막 부품이에요. 2편의 SQLite 를 읽어 아침마다 Slack 으로 쏘고, 일일 예산을 넘으면 경고를 붙입니다.
# daily_slack_report.py
import os, sqlite3, requests
from datetime import date, timedelta
DAILY_BUDGET_USD = 15.0
WEBHOOK = os.environ["SLACK_WEBHOOK_URL"]
yesterday = str(date.today() - timedelta(days=1))
with sqlite3.connect("llm_usage.db") as conn:
rows = conn.execute(
"SELECT provider, ROUND(SUM(cost_usd), 2), COUNT(*) FROM llm_usage "
"WHERE substr(ts, 1, 10) = ? GROUP BY provider ORDER BY 2 DESC",
(yesterday,),
).fetchall()
total = sum(r[1] for r in rows)
lines = [f"*{yesterday} LLM 비용: ${total:.2f}*"]
if total > DAILY_BUDGET_USD:
lines[0] += f" 🚨 일일 예산 ${DAILY_BUDGET_USD:.0f} 초과!"
lines += [f"• {prov}: ${usd:.2f} ({calls}건)" for prov, usd, calls in rows]
requests.post(WEBHOOK, json={"text": "\n".join(lines)})
Slack 에는 이렇게 도착해요.
2026-07-06 LLM 비용: $7.60
• anthropic: $6.95 (142건)
• google: $0.58 (203건)
• upstage: $0.07 (67건)
crontab 에 아침 9시로 걸어두면 끝입니다.
0 9 * * * cd /path/to/project && python3 daily_slack_report.py
여기에 5장의 대사 스크립트를 주 1회 cron 으로 더하면, 사람이 하는 일은 “알림이 이상할 때만 들여다보기”로 줄어요.
8. 비용 절감 체크리스트
추적 시스템이 잡아준 데이터로 실제 비용을 줄이는 레버들이에요. 효과가 컸던 순서로 정리합니다.
- 프롬프트 캐싱 — 시스템 프롬프트·도구 정의처럼 매 호출 반복되는 앞부분을 캐시하세요. 읽기 단가가 약 1/10이라 대화형 워크로드에서 입력 비용이 크게 줄어요. 캐시가 실제로 붙는지는 usage 의 캐시 필드로 확인하면 됩니다.
- 모델 라우팅 — 분류·추출 같은 쉬운 작업을 플래그십에서 mini/nano/Flash/Solar 급으로 내리기. 2편 리포트에서 “비싼 모델로 가는 쉬운 트래픽”을 찾는 게 출발점이에요.
- 배치 API — 실시간이 필요 없는 대량 작업은 Anthropic·OpenAI 모두 50% 할인. 야간 배치로 미루는 것만으로 반값입니다.
- 출력 다이어트 —
max_tokens상한과 “간결하게” 지시. 출력 단가가 입력의 5~6배라는 걸 기억하세요. - 추론 강도 조절 — thinking/effort 를 작업 난이도에 맞게. 쉬운 작업에 높은 추론 강도는 보이지 않는 출력 토큰을 태워요.
9. 시리즈 정리
3부작이 만든 시스템을 한 그림으로 요약하면 이렇습니다.
| 계층 | 도구 | 답하는 질문 |
|---|---|---|
| 구조 이해 (1편) | 가격표 · usage 필드 | 뭐가 얼마인가 |
| 상시 관측 (2편) | track() + SQLite |
지금 어디에 쓰고 있나 |
| 정산 대사 (3편) | 공식 Usage/Cost API | 청구서와 맞나 |
| 통제 (3편) | 게이트웨이 예산 | 초과를 막을 수 있나 |
| 알림 (3편) | Slack 리포트 | 사람이 안 봐도 되나 |
처음의 질문 — “이번 달 LLM 에 총 얼마 썼지?” — 이제 아침 Slack 메시지가 대신 답해줍니다. 🎉
일단 오늘은 여기까지….. 다음에는 이 비용 데이터를 Grafana 대시보드로 시각화하거나, 모델 라우팅을 자동화하는 이야기로 이어가볼게요.