定額のサブスク(Claude のMaxプランやProプラン)を契約しているのに、claude -p(Claude Code の非対話モード)を自動化スクリプトから呼んでいたら、裏で API の従量課金を消費していた——という事故を踏みました。
しかも気づいたのは、API残高がゼロになって定時ジョブが全滅したときです。それまでは何のエラーも出ず、静かに課金が溶けていました。
原因はシンプルで、.env に置いていた ANTHROPIC_API_KEY が悪さをしていました。同じ構成で自動化している人は、けっこう踏みうる罠だと思うので、再現条件と直し方を残しておきます。
何が起きたか
ある朝、定時で回しているジョブ(記事生成やリサーチをまとめて通知に流す類のもの)が、軒並み失敗していました。ログを見ると、こう出ています。
Credit balance is too low
最初、意味が分かりませんでした。私が使っているのは Claude の定額サブスクプランです。当時は月額契約で動いていると思っていましたが、サブスクリプションにも利用上限があります。「クレジット残高」なんて概念は、サブスクには無いはずなんです。
「クレジット残高が低い」と言われるのは、今回の環境ではAPI キーによる従量課金(pay-as-you-go)の残高不足を示していました。つまり私のジョブは、サブスク認証ではなく、API キー経由の従量課金で claude -p を叩いていたということでした。
そして恐ろしいのは、残高があった数日間は、一切エラーを出さずに従量課金を消費し続けていたこと。残高がゼロになって初めて、Credit balance is too low で全部が止まり、ようやく異常に気づいたのです。
⚠️ 注意: サブスク契約だから安心、ではありません。
claude -pは条件次第でサブスクを無視して従量課金経路を選びます。そして残高がある間はエラーを出さないため、課金が発生していること自体に気づけません。
原因:ANTHROPIC_API_KEY がサブスクより優先される
Claude Code の認証には、ざっくり2つの経路があります。
1. サブスク認証:claude に一度ログイン(claude.ai アカウント)した状態。MaxやProの定額枠で動く
2. API キー認証:環境変数 ANTHROPIC_API_KEY を使い、ConsoleのAPI利用として課金される。ANTHROPIC_AUTH_TOKEN は別に扱う必要があり、gateway/proxyのBearer認証など接続先・契約によって請求先が変わる。名前だけでConsoleの従量課金とは判定できない
問題は優先順位です。環境変数に ANTHROPIC_API_KEY があると、Claude Code はそちらを優先して API キー認証で動きます。 サブスクにログイン済みでも、環境変数があれば従量課金経路に倒れます。
公式のAPIキー管理案内は、subscription利用時にANTHROPIC_API_KEYを未設定にし、/statusで認証方法を確認するよう説明しています(2026年10月9日確認)。管理者向け設定の一文だけから、すべての認証・課金経路を推定するのは避けます。
なぜ自動化ジョブだけが踏んだのか
対話的にターミナルで claude を使う分には、私の環境では ANTHROPIC_API_KEY をシェルに export していなかったので、ずっとサブスクで動いていました。
事故ったのは自動化ジョブの中だけです。理由はこれ。
- アプリ側のコードが、別用途のために
.envをload_dotenv()で読み込んでいた - その
.envにはANTHROPIC_API_KEYが入っていた(別の従量課金前提のモジュールが正当に使っている) load_dotenv()でos.environにANTHROPIC_API_KEYが乗るclaude -pをsubprocessで起動するとき、env={**os.environ}でまるごと子プロセスに継承していた
結果、子プロセスの claude -p が ANTHROPIC_API_KEY を見つけ、サブスクを無視して従量課金で動いていた、というわけです。
# 事故っていたコード(イメージ)
import os
from dotenv import load_dotenv
load_dotenv() # .env の ANTHROPIC_API_KEY が os.environ に乗る
subprocess.run(
["claude", "-p", prompt],
env={**os.environ, "TERM": "xterm-256color"}, # ← API キーごと継承してしまう
)
env={**os.environ} は、子プロセスに環境を渡す定番の書き方です。だからこそ気づきにくい。.env に API キーが1個あるだけで、意図せず従量課金に倒れます。
直し方:子プロセスの env から API キー系だけ外す
いちばん手堅いのは、claude -p を起動する瞬間だけ、ANTHROPIC_API_KEY と ANTHROPIC_AUTH_TOKEN を子プロセスの env から取り除くことです。不要なAPI認証の継承を避けられます。ログイン状態、apiKeyHelper、gatewayやクラウドプロバイダーの設定も別に確認し、同じ子プロセス環境でclaude auth statusと/statusを確認してください。
import os
import subprocess
def _subscription_env() -> dict:
"""claude -p をサブスク枠で回すための env。
ANTHROPIC_API_KEY があると CLI が従量課金経路を優先するため除去する。"""
env = {
k: v
for k, v in os.environ.items()
if k not in ("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN")
}
env["TERM"] = os.environ.get("TERM", "xterm-256color")
env["NO_COLOR"] = "1"
return env
subprocess.run(["claude", "-p", prompt], env=_subscription_env())
ポイントは、os.environ 自体はいじらないことです。.env の ANTHROPIC_API_KEY を従量課金モードで正当に使っている別モジュールがある場合、それは壊したくありません。だから「親プロセスの環境はそのまま」「claude -p に渡す子プロセスの env からだけ外す」という形にします。親プロセスの変数は変わりませんが、子プロセスが必要とするgateway認証まで除去しないよう、用途を確認します。
別の選択肢
状況によっては、こちらの方が早いこともあります。
- そもそも export しない:
claude -pを呼ぶ前のシェルでunset ANTHROPIC_API_KEYする。ただし.envを読むアプリ内からだと効きにくい forceLoginMethodで縛る:claude側の設定で API キー認証を起動時にブロックする。ログイン方式を制限する管理設定です。認証供給元による拒否の条件は公式仕様を確認してください。ただし API キーで動かしたい他用途まで巻き込むので、用途が混在する環境では注意- キーの置き場を分ける:従量課金で使いたいモジュール専用の名前(例:別の環境変数名で受けてコード内で
ANTHROPIC_API_KEYに橋渡し)にして、グローバルなANTHROPIC_API_KEYを環境に置かない
私は、従量課金で正当にキーを使うモジュールが同居していたので、「子プロセス env から外す」案を採りました。
いま課金経路がどっちか、を確認する方法
「自分のジョブはサブスクで動いている」と思い込むのが一番危ないので、確認しておくと安心です。
手軽なのは、claude -p を呼んでいるのと同じ環境変数の状態で、対話的に起動して状態を見ることです。
# 子プロセスに渡しているのと同じ env でログイン状態を確認
claude
# 起動後に /status を見ると、認証方法を確認できる
あるいは、API キーが環境にあるかをまず疑います。
python3 -c 'import os; print("\n".join(k for k in os.environ if k.startswith("ANTHROPIC")))'
ここに ANTHROPIC_API_KEY が出てくるなら、その状態で claude -p を叩いている限り、ConsoleのAPI認証を優先する可能性があります。変数の存在だけで判断を終えず、実際の認証状態と利用明細を確認してください。値は出力せず、共有ログへ認証情報を残さないようにします。
📝 メモ: API残高がある間はエラーが出ません。「動いているから正常」ではなく、「どの認証で動いているか」を一度は確認しておくのがおすすめです。
教訓:env={**os.environ} は便利だけど、認証情報まで素通しする
今回の根っこは、Claude Code 特有の話というより、subprocess に env={**os.environ} でまるごと環境を渡すという、ありふれた書き方にありました。
.env に何か1個、認証系の環境変数が増えるたびに、それを起動する全ての子プロセスが影響を受けます。普段は無害でも、今回のように「その環境変数を見て挙動を変えるツール」を呼んだ瞬間、意図しない経路に倒れます。
外部コマンドを subprocess で叩くときは、
- 本当にその子プロセスに必要な環境変数だけを渡す
- 少なくとも、認証系(APIキー・トークン)は意識して取捨選択する
これを癖にしておくと、今回みたいな「静かに課金が溶ける」系の事故を防ぎやすくなります。
最後に念のため。.env や API キーはログ・コミットに残さないよう、いつもどおり気をつけてください。今回のように「env を子プロセスへ素通し」していると、デバッグで env をダンプした拍子にキーが流出する、という二次災害も起こり得ます。
サブスクで Claude Code を回しているつもりが、いつの間にか従量課金で溶けていた——同じ構成の人は、一度 python3 -c 'import os; print("\n".join(k for k in os.environ if k.startswith("ANTHROPIC")))' で変数名だけを確認し、実際の認証状態も照合してください。



