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.yamladmin というオプション項目を記述します。(具体的な項目の説明は、公式ドキュメントを参照してください)

今回のケースでは次のように設定しています。最低でも 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.yamladmin 項目を追記して、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.typeuser / rbac_group / organizationuser は OIDC の sub の値であり、ユーザー 1 名に対する制限
rbac_group は IpP から連携されるグループに対する制限
organization はこの Gateway 全体の制限
scope.user_idOIDC の sub の値scope.typeuser の場合に必須
scope.rbac_group_idIdP から連携されるグループの名前scope.typerbac_group の場合に必須
amount整数の文字列。単位はUSセント / nullnull は無制限、"0" はすべてブロック
perioddaily / weekly / monthly日、週、月の期間

これらを複数設定することができ、標準では次の順序で評価されるそうです。

  1. ユーザーに設定されたもの
  2. ユーザーが属するグループの中で一番制限が厳しい上限
  3. 組織のデフォルト
  4. 制限無し

また、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"}'

グループの制限方法(週次の上限例)

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 というテーブルでメールアドレスや名前を確認することができます。この例の場合 period2026-08 となっているのはその月の使用量、2026-08-07 が利用した日の使用量、2026-W32 が利用した週の使用量を示すレコードになっていて、現在までにどれくらいのコストをかけているのかは cents で表されています。

この例では、既に制限の 80 セントを超えているので、この状態で Claude Code と会話を続けようとすると、429 エラーが返ってきます。

まとめ

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