現実の現場では だけでなく OpenAI / Gemini / OpenAI / Bedrock / 自社 が混在します。各プロバイダの usage はバラバラなので、統一スキーマで取り込んで横断比較 できるようにします。
プロバイダ別 usage 取得
| プロバイダ | API | 粒度 |
|---|---|---|
| OpenAI | `/v1/usage` | 日次・モデル別 |
| Anthropic | `/v1/organizations/usage_report/messages` | 日次・key 別・モデル別 |
| Google Gemini ( Studio) | Cloud Billing API + Vertex AI | プロジェクト別・モデル別 |
| Azure OpenAI | Cost Management API | リソース別 |
| AWS Bedrock | Cost Explorer + CloudWatch | リソース別 |
| self-hosted (vLLM 等) | OpenTelemetry / 自前メトリクス | 個別実装 |
統一スキーマ
BigQuery / Snowflake の統合テーブル設計
YAML
# llm_usage 統合テーブルcolumns: - timestamp # UTC - provider # "anthropic" | "openai" | "gemini" | "azure_oai" | "bedrock" | "self_hosted" - model # "claude-opus-4-7" | "gpt-5" | "gemini-2-flash" 等 - api_key_id # 個人/用途識別子(プロバイダ非依存) - person # マッピング後の個人 ID - team # チーム - input_tokens - output_tokens - cache_read_tokens # 0 if not supported - cache_creation_tokens - cost_usd # プロバイダの単価で計算 - request_count - latency_p50_ms # 任意 - latency_p99_ms # 任意OpenAI usage 取得
OpenAI の Usage API
Python
import os, requests
OPENAI_KEY = os.environ["OPENAI_API_KEY"]
def fetch_openai_usage(date_str: str) -> dict: """日次の usage(admin key required)""" r = requests.get( "https://api.openai.com/v1/organization/usage/completions", headers={"Authorization": f"Bearer {OPENAI_KEY}"}, params={"start_time": int(date_str_to_epoch(date_str)), "bucket_width": "1d", "group_by": ["api_key_id", "model"]}, ) return r.json(): 各プロバイダ → 統合テーブル
プロバイダごとの adapter で統合形式に
Python
from dataclasses import dataclassfrom datetime import datetime
@dataclassclass UsageRow: timestamp: datetime provider: str model: str api_key_id: str input_tokens: int output_tokens: int cache_read_tokens: int = 0 cache_creation_tokens: int = 0 request_count: int = 0
def normalize_anthropic(raw: dict) -> list[UsageRow]: """Anthropic レスポンス → UsageRow""" rows = [] for bucket in raw["data"]: ts = datetime.fromisoformat(bucket["starting_at"].replace("Z", "+00:00")) for r in bucket["results"]: rows.append(UsageRow( timestamp=ts, provider="anthropic", model=r["model"], api_key_id=r["api_key_id"], input_tokens=r["uncached_input_tokens"], output_tokens=r["output_tokens"], cache_read_tokens=r.get("cache_read_input_tokens", 0), cache_creation_tokens=r.get("cache_creation_input_tokens", 0), )) return rows
def normalize_openai(raw: dict) -> list[UsageRow]: # ... OpenAI 用の adapter pass
# 全プロバイダから取り込みall_rows = []all_rows.extend(normalize_anthropic(get_anthropic_usage(...)))all_rows.extend(normalize_openai(fetch_openai_usage(...)))# ... 他
# BigQuery / Snowflake へ insertload_to_dwh(all_rows)ダッシュ設計の核
- プロバイダ別の費用比率: 「Claude が 60%、OpenAI が 30%、Gemini が 10%」のような円グラフ
- モデル別の費用効率: 同じユースケースで OpenAI と Anthropic を比較
- 個人別の利用パターン: 「Aさんは Claude Opus 中心、Bさんは Gemini Flash 中心」が見える
- 自社 LLM の比率: コスト削減のためのオンプレ比率
- 異常検知: 1 人が 1 日に通常の 10 倍消費したらアラート
次の話
EP.05 では CI/CD のコスト ── Actions / Vercel / CircleCI の使用量を扱います。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。