Claude apps gatewayで開発者ごとの支出上限を設定する
Claude apps gatewayのAdmin APIで開発者・グループ・組織ごとに日/週/月の支出上限を設定し、429応答の中身、見積もり金額の決まり方、Postgres障害時の挙動までを扱います。
支出上限は何を防ぐ仕組みか
Claude apps gatewayを通る推論リクエストは、既定ではすべて1つの共有アップストリーム認証情報を経由します。プロバイダーの請求はこの共有クレデンシャルにまとめて計上され、誰がいくら使ったかは請求書から読めません。暴走したエージェントのフリートが1つ動くだけで、組織のコミット枠を丸ごと食いつぶします。
支出上限(spend limits)は、この共有請求の上に載る開発者単位の監視と遮断です。開発者・IdPグループ・組織全体の3段階で、日/週/月ごとの上限額を置けます。上限を超えた開発者は次のリクエストで429を受け取り、期間のリセットか管理者による引き上げまでブロックされます。
基本のセットアップがまだの場合は、先にOIDCとPostgresを用意してください。上限の判定は、Postgresに持つ累計支出を前提に動きます。
上限は誰に・いくら・どの期間で置くか
gateway.yamlのadmin:ブロックを設定すると、ゲートウェイは/v1/organizations/spend_limitsにAdmin APIを公開し、以降すべての推論リクエストでライブに上限を強制します。上限そのものはgateway.yamlに書かず、POST /v1/organizations/spend_limitsが{scope, amount, period}から上限を1件作成または置き換えます。
このAPIはAnthropicのAdmin API(支出上限エンドポイント)とワイヤ形式が同じです。そちら向けに書いたHTTPクライアントは、ベースURLを変えるだけでゲートウェイにも向けられます。
組織全体の既定として、開発者1人あたり月500ドルをかける例です。
curl -sS https://claude-gateway.internal.example.com/v1/organizations/spend_limits \
-H "x-api-key: $GATEWAY_ADMIN_WRITE_KEY" \
-H "Content-Type: application/json" \
-d '{"scope": {"type": "organization"}, "amount": "50000", "period": "monthly"}'contractorsグループのメンバーだけに、日100ドルというより厳しい上限を重ねる例です。
curl -sS https://claude-gateway.internal.example.com/v1/organizations/spend_limits \
-H "x-api-key: $GATEWAY_ADMIN_WRITE_KEY" \
-H "Content-Type: application/json" \
-d '{"scope": {"type": "rbac_group", "rbac_group_id": "contractors"}, "amount": "10000", "period": "daily"}'| フィールド | 値 | 説明 |
|---|---|---|
scope.type | 値user / rbac_group / organization | 説明userはIdPが割り当てる安定したユーザーID(OpenID Connect、OIDCのsub)をscope.user_idに渡して1人を指定。rbac_groupはIdPグループ名をscope.rbac_group_idに。organizationは組織全体の既定値 |
amount | 値USDセント単位の整数文字列、またはnull | 説明nullは無制限、"0"はすべてのリクエストを止めるゼロ上限 |
period | 値daily / weekly / monthly | 説明1つのscopeは期間ごとに1件の上限を持て、それぞれ独立して効く。いずれか1つでも超えていればブロックされる |
グループ上限や組織上限は共有プールではなく、メンバー1人あたりの既定値です。期間ごとに、各開発者の実効上限は次の順で決まります。
実効上限が決まる順序(期間ごと)
- 1
ユーザー個別の上書き
scope.type: userで置いた上限があれば、それが使われます。 - 2
該当するグループ上限のうち最も厳しいもの
複数グループに属していれば、最小のものが採用されます。
admin.group_limit_mode: maxにすると、最も緩いグループを採用する向きに反転します。 - 3
組織の既定値
どちらも無ければ、
organizationの上限です。 - 4
無制限
どの階層にも上限が無い期間は、制限なしで通ります。
ゲートウェイは3種のscopeをすべて受け付けますが、Anthropic公式APIの公開POSTが対応するのはuserだけです。公式クライアントをそのまま向けても、rbac_groupとorganizationの上限は作れません。
管理用キーの使い分け
Admin APIの認証は2系統です。1つはx-api-keyヘッダーで、admin.write_keysのキーは書き込みを含む全操作、admin.read_keysのキーはGET専用です。TerraformやCIのように変更が要る自動化にはwrite_keys、監視ダッシュボードのように参照だけの用途にはread_keysを割り当てる分け方になります。
キーはそれぞれidを持ち、監査ログにadmin-key:<id>として残ります。用途ごとに別のキーを発行すれば、どの自動化がいつ何を変更したかを後から追えます。gateway.yamlの書き方は次のとおりです。
admin:
write_keys:
- { id: terraform, key: "${GATEWAY_ADMIN_WRITE_KEY_TF}" }
- { id: ci, key: "${GATEWAY_ADMIN_WRITE_KEY_CI}" }
read_keys:
- { id: reporting, key: "${GATEWAY_ADMIN_READ_KEY}" }
admin_groups: [platform-finops]
blocked_message: request an increase at https://go.example.com/claude-limitsキーの値は32文字以上で、idはread_keysとwrite_keysをまたいで一意にします。キーは配列なので、新しいキーを足して利用側を切り替え、古いキーを外す順でローテーションできます。
もう1つの認証は、admin.admin_groupsのクレームを持つゲートウェイのベアラートークンです。常に全権限で、監査ログにはoidc:<sub>と残ります。人間の管理者向けです。
上限に達した開発者には何が返るか
/v1/messagesへのリクエストごとに、ゲートウェイは開発者の上限と期間内の累計支出を1回のPostgresクエリで参照します。いずれかの上限を超えていれば429が返り、error.typeはbilling_error、x-should-retry: falseヘッダーが付きます。
メッセージ本文には、spend limit reached (daily; resets 2026-08-08 00:00 UTC)のように超過した期間とリセット時刻が入ります。複数の上限を同時に超えた場合は、最後にリセットされる上限が名指しされます。admin.blocked_messageを設定していれば、メッセージの後ろに続けて表示されます。上のgateway.yamlの例のように申請先のURLまで書いておくと、ブロックされた開発者が次の動きを読み取れます。応答のretry-afterヘッダーには、リセットまでの秒数が入ります。
ゲートウェイサーバーがv2.1.225より前だと、メッセージは期間もリセット時刻もretry-afterも持たないspend limit reachedだけです。
リセットは協定世界時(UTC)の暦境界で起こります。日次は00:00 UTC、週次は月曜、月次は1日です。日本時間では日次が午前9時にあたります。/v1/messages/count_tokensは課金対象ではないため、上限に関わらずブロックされません。
見積もり金額はどう決まるか
応答が返るたびに、使用量メーターがトークン数を読み取り、日次・週次・月次のカウンターへ費用を加算します。この処理はクライアントへ送るバイト列に触れないので、メーターの失敗が応答を壊すことはありません。ここで出る金額はUSDの見積もりで、請求書ではなくサーキットブレーカーの数字です。
料金は次の順で当てはめられます。
メーターが料金を選ぶ順序
- 1
pricing.overridesの行
そのアップストリームに対して書いた行があれば、最優先です。
- 2
アップストリームのモデルIDの定価
Claude Codeのコスト表がそのIDを認識できる場合です。Anthropic・Amazon Bedrock・Google Cloud's Agent Platform・Microsoft Foundryの各ID形式に対応します。
- 3
マッピングしたmodels[].idの定価
Bedrockのapplication-inference-profile ARNやFoundryのデプロイ名のように、文字列自体にモデル名が入らない場合の経路です。
- 4
未知モデル向けの既定ティア
入力5ドル/出力25ドル(100万トークンあたり)です。位置づけられないIDが無料扱いになることはありません。起動時と、実行時はID単位で1回ずつ警告が出ます。
Bedrock自体の請求体系はBedrock経由のClaude料金にあります。直接APIの単価との差を知りたいときは、そちらが参考になります。
クライアント側の中断も課金対象です。ストリームがアップストリームの最終usageフレームを受け取らずに終わると、メーターは出力トークンあたり約4文字という下限見積もりで、すでにクライアントへ送ったテキスト分を課金します。途中で打ち切って上限を回避することはできません。
契約単価や社内チャージバックに合わせる
定価のままだと、割引契約を結んでいる組織では上限の減り方が実際の請求と合いません。pricingブロックで、メーターが使う単価を差し替えられます。admin:ブロックがあるか、v2.1.268以降でポリシーを1つ以上持つmanaged:ブロックがあるときだけ設定できます。読む側がいない状態では、ゲートウェイが起動を拒否します。
pricing:
multiplier: 0.85
overrides:
- upstream: bedrock-eu
model: claude-sonnet-4-6
input: 3.30
output: 16.50
cache_read: 0.33
cache_write: 4.125multiplier(既定1)は、定価でも上書き価格でも最後に全額へ掛かります。0.85なら85%分の課金として数えます。overridesの行は{upstream, model, input, output, cache_read, cache_write}で、単価はUSD/100万トークン、4つとも必須で0より大きく10000以下です。
行の当たり方には癖があります。
- 行が重なるときは、先頭ではなく最も具体的な行が選ばれる。アップストリームへ送る正確なモデル文字列、クライアントが送ったID、組み込みモデル名の順
- 組み込みのIDは、日付付き形式やBedrockのリージョン形式など、同じモデルとして価格付けされるID形をまとめて覆う
- fast modeの割増料金も置き換えるため、fastと標準のリクエストは同じ4単価で計上される
- ウェブ検索は定価の1回0.01ドルのまま。
multiplierだけは掛かる - 存在しないアップストリーム名や、同じモデルを指す行の重複(組み込みモデルの別表記を含む)は起動時に落ちる
リージョンごとに単価が違うなら、リージョンごとに名前付きアップストリームを分けて、アップストリームごとに1行を書きます。
multiplierはv2.1.271以降で1より大きく、10まで設定できます。プロバイダーの請求より多く数える設定で、内部チャージバックの料率に使えます。admin:ブロックがあれば上限にも効くので、120%なら開発者は上限へ早く届きます。起動時にその旨の警告が出ます。プロバイダー側の請求額は変わりません。
ゲートウェイサーバーがv2.1.271より前だと、1を超えるmultiplierを設定した時点で起動を拒否します。開発者のClaude Codeもv2.1.271以降でないと、1を超える値は無視されて上乗せ前の金額が表示されます。
managed:のポリシーに該当する開発者へは、pricingの単価がClaude Code側にも配られます(v2.1.268以降のゲートウェイ、v2.1.242以降のクライアント)。/usage、ステータスライン、OpenTelemetryの数字が契約単価になります。ポリシーに該当しない開発者には届かず、定価のままです。
ポリシーのcliブロックでmodelPricingを{}にすると、そのポリシーの開発者は定価のままになります。ポリシーが自前のmodelPricingを持っていれば、ゲートウェイは単価を加えません。
Postgresが落ちたとき上限はどうなるか
上限チェックは2秒のタイムアウトでPostgresに問い合わせます。ストアに届かないときの動きは、enforcement.fail_closed_on_errorで分かれます。
Postgres障害時の2つの振る舞い
フェイルオープン
リクエストはそのまま通り、警告がログに出ます。応答にはanthropic-ratelimit-unified-*ヘッダーが付きません。推論が止まらない代わりに、その間は上限が事実上無効です。
フェイルクローズ
同じ429 billing_errorが返りますが、メッセージはspend limit unavailableで、期間もリセット時刻もretry-afterも付きません。未計測の支出は出ない代わりに、上限を超えていない開発者も止まります。
fail_closed_on_error: trueは、admin:ブロックが無いと起動を拒否されます。支出の強制自体がadminの設定時にだけ動くからです。
フェイルオープンが効くのは、ロードバランサーやオーケストレーターがゲートウェイへトラフィックを流し続ける間だけです。store.readiness_grace_seconds(0〜3600の整数、既定0)を設定すると、Postgresが応答しなくなっても/readyzがその秒数だけ「ready」を返し続け、短いフェイルオーバーでレプリカが外されるのを防げます。ただし猶予の間は未計測の支出が出るため、値はフェイルオーバーに要る長さにとどめます。フェイルクローズでは、レプリカがreadyのままでも推論は止まります。
このキーはゲートウェイサーバーがv2.1.282以降のときだけ使え、古いバージョンはキーの存在で起動を拒否します。障害中のレプリカの扱いはデプロイと運用の障害時の挙動の節にあります。
開発者の画面にはどう見えるか
Claude Codeは、最も消費が多い上限の使用率が75%を超えた時点と95%を超えた時点の2段階で警告を出します。ブロックされたときは429のメッセージをそのまま表示し、admin.blocked_messageも含まれます。上限は/usageと、開発者のステータスラインのスクリプトにも渡ります。
警告とパーセンテージは、ゲートウェイが成功応答に付けるanthropic-ratelimit-unified-*ヘッダーから出ています。上限を持つ開発者の/v1/messagesの成功応答すべてに載り、中身は常にその開発者自身の上限です。上流プロバイダーが返す、組織全体の共有クォータを表すレート制限ヘッダーは、ゲートウェイが取り除いて転送しません。
表示がどこまで細かくなるかは、開発者のマシンとゲートウェイサーバー、両方のバージョンで決まります。
| 開発者に見えるもの | 開発者のマシン | ゲートウェイサーバー |
|---|---|---|
| 75%と95%での警告 | 開発者のマシンv2.1.225以降 | ゲートウェイサーバーv2.1.225以降 |
/usageのSpend limitバー(使用率とリセット時刻)と、ステータスライン入力のrate_limits.spend_limit | 開発者のマシンv2.1.251以降 | ゲートウェイサーバーv2.1.225以降 |
バー上の見積もり支出と上限のドル金額(「$271.40 / $500.00 spent this month」の形)と、ステータスライン入力のrate_limits.spend_limit.used_usd・limit_usd・period(欠ける場合あり) | 開発者のマシンv2.1.284以降 | ゲートウェイサーバーv2.1.284以降 |
ドル金額は、ヘッダーとは別にゲートウェイへ送る追加リクエストで取得されます。開発者のマシンにCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICを設定している場合、Claude Codeはこのリクエストを送りません。この変数が設定されているか、ゲートウェイサーバーがv2.1.284より古いと、バーとステータスラインはパーセンテージだけになります。表示される支出は、ゲートウェイが上限の強制に使うのと同じ見積もりで、プロバイダーの請求額ではありません。
バージョンごとに何が使えるようになったか
支出上限まわりは、ゲートウェイサーバー側のバージョンで使える機能が少しずつ増えてきました。ここまでに出てきた必要バージョンを時系列に並べます。
支出上限まわりの必要バージョン
- v2.1.218マッピングしたモデルIDで価格が引ける
アップストリームの文字列にモデル名が入らないIDが、
models[].id経由で定価に当たります。 - v2.1.225429にリセット時刻が入り、使用率ヘッダーが付く
メッセージに期間とリセット時刻が入り、
retry-afterとanthropic-ratelimit-unified-*ヘッダーが付きます。 - v2.1.227pricing.overridesと/protocolの追加
契約単価の上書きと、
<public_url>/protocolでの429のヘッダー・本文の仕様が使えます。 - v2.1.268managedポリシーへの単価の配信
pricingの単価がmanagedポリシー経由でClaude Codeへ届きます。 - v2.1.271multiplierが1より大きくできる
上乗せ料率で数えられるようになります。
- v2.1.281・v2.1.282AWS別セッションとreadiness猶予
開発者ごとのAWSセッション名と、
readiness_grace_secondsが使えます。 - v2.1.284ドル金額の表示
/usageとステータスラインに、見積もり支出と上限が金額で出ます。
AWS側の請求と開発者ごとに突き合わせる
共有認証情報のままだと、AWSの請求は1つのIAMプリンシパルにまとまります。Bedrockを使うなら、assume_roleにsession_name: email(またはsub)を足す選択肢があります。ゲートウェイが開発者ごと・1時間ごとにsts:AssumeRoleを呼び、開発者のメールをセッション名にして署名し直すので、AWS側には開発者ごとのセッションとして現れます。ゲートウェイサーバーがv2.1.281以降のときに使えます。
upstreams:
- provider: bedrock
region: us-east-1
auth: {}
assume_role:
role_arn: arn:aws:iam::123456789012:role/claude-gateway-bedrock-user
session_name: email支出上限のメーターとは別の計測系なので、両者の数字が一致する前提にはなりません。特に抜け道が1つあります。クライアントが打ち切ったリクエストのトークン数を、ゲートウェイは上限の精度のために数え直します。この呼び出しとフォールバックの1トークンのリクエストは、共有のclaude-apps-gatewayセッションで署名されるため、AWSはこの分を開発者ではなくclaude-apps-gatewayに帰属させます。
厳密に開発者ごとの帰属が要るなら、列挙したすべてのBedrockアップストリームにsession_nameつきのassume_roleを置きます。無いアップストリームは、自分自身の認証情報で署名します。
開発者のトークンにemailクレームが無いとき、そのリクエストはこのアップストリームへ送られず、運用ログがsubへの切り替えかoidc.email_claimの設定を促します。セッション名に使えない文字は=XX形式の16進に置き換えられ、64文字を超える分は先頭部分とハッシュに縮められるので、セッション名は常に有効で一意になります。
Admin APIリファレンス
エンドポイントはすべて/v1/organizations/spend_limits配下です。
| メソッドとパス | 説明 |
|---|---|
GET /v1/organizations/spend_limits | 説明設定済みの上限を一覧。scope_type(organization/rbac_group/user)で絞り込み可 |
POST /v1/organizations/spend_limits | 説明{scope, period}に対する上限を作成または置き換え |
GET /v1/organizations/spend_limits/{id} | 説明spl_接頭辞のIDで1件取得 |
DELETE /v1/organizations/spend_limits/{id} | 説明1件削除。{type: "spend_limit_deleted", id}を返す |
GET /v1/organizations/spend_limits/effective | 説明主体(principal)ごと・期間ごとの、実際に効いている上限と累計支出 |
GET /v1/organizations/spend_limits/audit | 説明管理操作の変更履歴を新しい順で |
規約はAnthropicのAdmin APIに合わせています。すべてのオブジェクトにtypeが付き、IDはspl_接頭辞、金額はUSDセント単位の整数文字列です。POSTは他のcurrencyを400で拒否し、エラーは{type: "error", error: {type, message}, request_id}の形で返ります。request-idヘッダーは成功・失敗を問わずすべての管理API応答に付きます。すべての変更操作は同一トランザクションでadmin_auditに変更前後の行を書き込み、admin-key:<id>またはoidc:<sub>が紐づきます。
実効上限と累計支出を引く
/effectiveはAnthropicのSpendSummaryスキーマを返します。各行が主体と期間の組で、解決済みの上限・期間内の累計支出・actorオブジェクトを持ちます。グループ由来の上限も、強制と同じgroup_limit_modeのタイブレークで解決されるため、画面に出る値が実際に効いている値です。ゲートウェイ固有の違いは4つあります。
user_idはOIDCのsubそのものactor.nameとactor.email_addressは、その主体がゲートウェイ経由で最初の推論リクエストを送るまでnull。ゲートウェイはユーザーディレクトリを持たず、各ユーザー自身のセッションJWTから最終確認値を記録するだけだから- 各行に
groups配列(その主体が最後に確認されたIdPグループ)が付く。管理UIが適用中の上限階層をすべて表示するための拡張で、Anthropic向けのクライアントは無視してよい user_ids[]を指定しないと、支出記録のある主体だけが並ぶ。ゲートウェイは組織メンバー全員を列挙できないため
絞り込みのクエリパラメータは次のとおりです。
| パラメータ | 説明 |
|---|---|
user_ids[] | 説明繰り返し可。OIDCのsubで主体を絞る |
period[] | 説明繰り返し可。daily / weekly / monthlyの行だけに絞る |
sort | 説明spend_descで支出の多い順。period[]をちょうど1つ指定する必要がある |
q | 説明OIDCのsub・最終確認メール・表示名への大文字小文字を区別しない部分一致 |
limit / page | 説明ページサイズ(1〜1000、既定20)と、前回応答のnext_pageの不透明トークン |
月次の上位支出者を引く例は、次の形になります。
curl -sS "https://claude-gateway.internal.example.com/v1/organizations/spend_limits/effective?period[]=monthly&sort=spend_desc&limit=10" \
-H "x-api-key: $GATEWAY_ADMIN_READ_KEY"AnthropicのAdmin APIとの違い
ゲートウェイの実装は、ワイヤ形式を揃えているだけでAnthropicのAdmin APIそのものではありません。
/auditはゲートウェイ独自のエンドポイントで、公式のワイヤ形式を踏襲していない- 公式Admin APIの
spend_limit_increase_requestsキューのような他の管理面は、ゲートウェイには無い。提供するのは支出上限のエンドポイントだけ
scopeの種類とgroups配列の違いは、上の表と/effectiveの節にあるとおりです。
ページングにも違いがあります。生の一覧は相互排他なspl_接頭辞のIDであるafter_idとbefore_idでページングし、作成順に並んでhas_moreがたどっている方向を反映します。/effectiveは不透明なnext_pageトークンを?page=として渡し、支出が記録され続ける間もページが安定するよう主体を昇順で並べます。両方ともlimitは1〜1000、既定20です。/auditは前ページ最後のイベントの数値idであるafter_idでページングし、limitの既定は100です。
データはいつまで保持されるか
ゲートウェイは支出関連のテーブルを4つ持ち、1時間ごとの掃除処理が保持期間を強制します。
| テーブル | 内容 | 保持期間 |
|---|---|---|
spend | 内容主体ごとの期間内累計(セント単位) | 保持期間admin.spend_retention_months、既定13か月 |
spend_limits | 内容設定済みの上限 | 保持期間API経由で削除するまで |
admin_audit | 内容変更履歴 | 保持期間admin.audit_retention_days、既定365日 |
principal_emails | 内容各主体の最終確認メール・表示名・IdPグループ。個人情報を含む | 保持期間最終アクティビティからadmin.identity_retention_days、既定90日 |
identity_retention_daysがspend_retention_monthsより短いのは、退職した人の識別情報が先に消え、匿名の支出カウンターだけが年次比較用に残るようにするためです。Postgresのスキーマとバックアップ方針はデプロイと運用にあります。支出上限を使う構成では、この4テーブルもバックアップの対象に含めておくのが安全です。
開発者が離職したら、DELETE /v1/organizations/spend_limits/{id}でユーザー個別の上限を削除します。支出と識別情報の行は、上の保持期間で自然に消えます。オフボーディングやデータ主体アクセス要求(DSAR)で即座に1人分を消したいなら、ゲートウェイのデータベースに直接DELETE FROM principal_emails WHERE principal = '<sub>'を実行します。メール・表示名・グループを持つ唯一のテーブルが消えます。spendとadmin_auditの行は仮名化されたOIDCのsubしか参照しておらず、それぞれの保持期間で消えていきます。
支出上限が変えるのは、コスト管理の主体をどこに置くかだ
プロバイダー側の請求だけを見ている間、コスト超過は月末に「先月いくら使ったか」を知る話にとどまります。支出上限は、この判断を月次レビューから、リクエストが届く瞬間のゲートウェイへ移します。しかも粒度は開発者・グループ・組織の3段階で、契約者チームだけを厳しく縛る運用も1本のAPIで組めます。
実質的な制約はPostgresの可用性です。ストアが落ちても推論を止めない既定の動きは、その間だけ上限が効かないことと表裏一体です。フェイルクローズに倒せば未計測の支出は出ませんが、ストア障害がそのまま開発チーム全体の推論停止になります。readiness_grace_secondsはフェイルオープン側で、短い障害によるレプリカの離脱を防ぎます。フェイルクローズの推論停止までは吸収しません。
まとめ
支出上限を動かすには、admin:ブロックの有効化とPostgres、そしてwrite_keysとread_keysの用途別発行が前提になります。金額は見積もりに基づく回路遮断なので、請求との突き合わせは別に必要です。割引契約があるならpricing、AWS側の帰属が要るならassume_roleのセッション名が、数字を合わせる手段になります。
既存のLLMゲートウェイを運用している組織が、この機能のためだけに乗り換えるかは、選び方の比較の評価軸と並べて判断する順序が手戻りを減らします。