Claude apps Gateway の Spend Limit を検証する
こんにちはエヌデーデーの関口です。
前々回、前回と Claude apps Gateway を Google Cloud 上に構築してみましたが、今回は Claude apps Gateway の Spend Limit について検証をしました。
Spend Limits (支出制限)
Claude apps Gateway には、日、週、月ごとに、利用者の支出金額の上限を設定できる機能が存在します。上限に達すると、利用者にはコード 429 を返して、期間がリセットされるまでは使えなくなります。
サブスクリプション契約のリミットだと物足りない、またはトークン数が多すぎて価格が高すぎる場合もあるでしょう。一方、API キー利用などだと従量課金で高額請求されてしまうので、それを防ぐために別の仕組みを作らなければならないという煩わしさがあります。Spend Limits を使えば、その組織独自に上限を設定できるので、これらの課題を解決できる仕組みと言えます。
事前準備
Spend Limits を有効にするためには、Gateway の設定ファイル gateway.yaml に admin というオプション項目を記述します。(具体的な項目の説明は、公式ドキュメントを参照してください)
今回のケースでは次のように設定しています。最低でも write_keys を記述することで、管理機能が有効化されて Spaned Limits が利用できるようになります。
admin:
# GATEWAY_ADMIN_WRITE_KEY は Secret Manager に保存して参照
# キーの値は、少なくとも 32 文字必要
write_keys:
- { id: gateway-admin, key: "${GATEWAY_ADMIN_WRITE_KEY}" }gateway.yaml を適用する前に、$GATEWAY_ADMIN_WRITE_KEY を Secret Manager に登録し、サービスアカウントが参照できるようにロールを与えます。
export GATEWAY_ADMIN_WRITE_KEY="$(openssl rand -hex 32)"
# Secret Manager に登録
printf '%s' "$GATEWAY_ADMIN_WRITE_KEY" | \
gcloud secrets create gateway-admin-key --data-file=-
export GATEWAY_SA="claude-gateway@${PROJECT_ID}.iam.gserviceaccount.com"
# シークレット アクセサー ロールを与える
gcloud secrets add-iam-policy-binding gateway-admin-key \
--member="serviceAccount:${GATEWAY_SA}" \
--role="roles/secretmanager.secretAccessor"gateway.yaml に admin 項目を追記して、Secret Manager を更新します。
gcloud secrets versions add gateway-config --data-file=gateway.yaml既に Artifact Registry に登録済みの Gateway のイメージは変わらないので、$GATEWAY_ADMIN_WRITE_KEY のシークレット追加と gateway.yaml を読み込ませるために、Cloud Run を再度デプロイします。
gcloud run deploy "$SERVICE_NAME" \
--image="$IMAGE_URI" \
--region="$INFRA_REGION" \
--service-account="$GATEWAY_SA" \
--min-instances=1 \
--max-instances=3 \
--timeout=3600 \
--port=8080 \
--ingress=internal-and-cloud-load-balancing \
--network="$VPC_NETWORK" \
--subnet="$SUBNET" \
--vpc-egress=private-ranges-only \
--set-secrets="/etc/claude/gateway.yaml=gateway-config:latest" \
--set-secrets="GATEWAY_JWT_SECRET=gateway-jwt-secret:latest" \
--set-secrets="OIDC_CLIENT_SECRET=gateway-oidc-client-secret:latest" \
--set-secrets="GATEWAY_POSTGRES_URL=gateway-postgres-url:latest" \
--set-secrets="GATEWAY_ADMIN_WRITE_KEY=gateway-admin-key:latest" \
--no-invoker-iam-checkデプロイが上手くいっているか、前回の記事で作成済みの踏み台 VM から確認してみます。
JSON が戻ってくればOKです。
curl -sS "${GATEWAY_URL}/.well-known/oauth-authorization-server" | jq
{
"issuer": "https://claude-gateway.private.example.co.jp",
"device_authorization_endpoint": "https://claude-gateway.private.example.co.jp/oauth/device_authorization",
"token_endpoint": "https://claude-gateway.private.example.co.jp/oauth/token",
"grant_types_supported": [
"urn:ietf:params:oauth:grant-type:device_code",
"refresh_token"
],
"response_types_supported": [],
"token_endpoint_auth_methods_supported": [
"none"
],
"scopes_supported": [
"openid",
"profile",
"email"
],
"gateway_protocol_version": 1
}Spend Limits の設定方法
Spend Limits は /v1/organizations/spend_limits が API エントリポイントとなっていて、設定については次の項目の JSON を POST して設定します。
| フィールド | 値 | 説明 |
|---|---|---|
scope.type | user / rbac_group / organization | user は OIDC の sub の値であり、ユーザー 1 名に対する制限rbac_group は IpP から連携されるグループに対する制限organization はこの Gateway 全体の制限 |
scope.user_id | OIDC の sub の値 | scope.type が user の場合に必須 |
scope.rbac_group_id | IdP から連携されるグループの名前 | scope.type が rbac_group の場合に必須 |
amount | 整数の文字列。単位はUSセント / null | null は無制限、"0" はすべてブロック |
period | daily / weekly / monthly | 日、週、月の期間 |
これらを複数設定することができ、標準では次の順序で評価されるそうです。
- ユーザーに設定されたもの
- ユーザーが属するグループの中で一番制限が厳しい上限
- 組織のデフォルト
- 制限無し
また、2 や 3 の設定は、グループ全体や組織全体という意味ではなく、あくまでそのグループや組織に入っているユーザー毎の上限になっています。
組織の制限方法(月次の上限例)
curl -sS https://claude-gateway.private.example.co.jp/v1/organizations/spend_limits \
-H "x-api-key: $GATEWAY_ADMIN_WRITE_KEY" \
-H "Content-Type: application/json" \
-d '{"scope": {"type": "organization"}, "amount": "10000", "period": "monthly"}'グループの制限方法(週次の上限例)
scope.type=rbac_group を設定する時の注意事項
IdP に Entra ID を採用して、scope.type=rbac_group を設定する場合は、前々回の記事で紹介したオブジェクト ID を指定しても有効になりませんので、次の設定が必要になります。
1. Entra ID でアプリ ロールを作成して対象のグループに設定しておく
こちらのドキュメントに沿って、Entra ID でアプリ ロールというものを作成し、Claude Code を利用するグループに、アプリ ロールを割り当てます。
今回は次のようなアプリ ロールを作成して割り当てました。
| 項目 | 値 |
|---|---|
| 表示名 | Claude Code Users |
| 許可されたメンバーの種類 | ユーザーまたはグループ |
| 値 | claude-code-users |
| 説明 | 任意の説明文 |
| このアプリ ロールを有効にしますか? | ON |
2. gateway.yaml を編集する
gateway.yaml を次のように編集します。編集したファイルは Secret Manager に新バージョンとして追加し、その後 Gateway が動いている Cloud Run をデプロイし直して、ファイルを読み込ませてください。
odic:
allowed_groups:
- claude-code-users # グループのオブジェクト ID から アプリ ロールの値に変更
groups_claim: roles # グループ判定を roles に変更(デフォルトは、groups)curl -sS https://claude-gateway.private.example.co.jp/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": "claude-code-users"}, "amount": "10000", "period": "weekly"}'ユーザー毎の制限設定方法(日次の上限例)
curl -sS https://claude-gateway.private.example.co.jp/v1/organizations/spend_limits \
-H "x-api-key: $GATEWAY_ADMIN_WRITE_KEY" \
-H "Content-Type: application/json" \
-d '{"scope": {"type": "user", "user_id": "user001@example.co.jp"}, "amount": "10000", "period": "daily"}'そして、これらを登録すると、PostgreSQL の spend_limits テーブルにレコードが作成されます。

Spand Limits の検証
試しにグループの日次の制限を 80 にして、個人と組織は 1000 という制限にして試してみました。
チャットを続けていくと、次のように spend_limit というテーブルの金額が更新されていきます。

principal がユーザーを示していて、principal_emails というテーブルでメールアドレスや名前を確認することができます。この例の場合 period が 2026-08 となっているのはその月の使用量、2026-08-07 が利用した日の使用量、2026-W32 が利用した週の使用量を示すレコードになっていて、現在までにどれくらいのコストをかけているのかは cents で表されています。
この例では、既に制限の 80 セントを超えているので、この状態で Claude Code と会話を続けようとすると、429 エラーが返ってきます。

まとめ
冒頭にも書いたように、従量課金時の予想外の請求というリスクを減らすことができる機能なので、Claude apps Gateway を利用するならぜひ設定しておきたいですね。
















