claude -p を自動化に組み込んだら、サブスク契約なのに従量課金が静かに溶けていた

サブスク契約の裏で従量課金が静かに漏れていくイメージ 解説・入門

定額のサブスク(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 がサブスクより優先される

envのAPIキーが子プロセスに継承され従量課金メーターへ流れるイメージ

Claude Code の認証には、ざっくり2つの経路があります。

1. サブスク認証claude に一度ログイン(claude.ai アカウント)した状態。MaxやProの定額枠で動く

2. API キー認証:環境変数 ANTHROPIC_API_KEY(または ANTHROPIC_AUTH_TOKEN)を使う。API の従量課金で動く

問題は優先順位です。環境変数に ANTHROPIC_API_KEY があると、Claude Code はそちらを優先して API キー認証で動きます。 サブスクにログイン済みでも、環境変数があれば従量課金経路に倒れます。

これは公式ドキュメントの記述からも読み取れます。forceLoginMethod という管理者向け設定の説明に、こうあります。

sessions authenticated by ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, or apiKeyHelper are blocked at startup

Claude Code settings より)

つまり Claude Code は、ANTHROPIC_API_KEY が存在すればそれを「認証方法」として採用するわけです。サブスクとは別経路として扱われている、ということです。

なぜ自動化ジョブだけが踏んだのか

対話的にターミナルで claude を使う分には、私の環境では ANTHROPIC_API_KEY をシェルに export していなかったので、ずっとサブスクで動いていました。

事故ったのは自動化ジョブの中だけです。理由はこれ。

  • アプリ側のコードが、別用途のために .envload_dotenv() で読み込んでいた
  • その .env には ANTHROPIC_API_KEY が入っていた(別の従量課金前提のモジュールが正当に使っている)
  • load_dotenv()os.environANTHROPIC_API_KEY が乗る
  • claude -psubprocess で起動するとき、env={<strong>os.environ} でまるごと子プロセスに継承**していた

結果、子プロセスの claude -pANTHROPIC_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 キー系だけ外す

envからキーをフィルタで除去しサブスク経路へ寄せるイメージ

いちばん手堅いのは、claude -p を起動する瞬間だけ、ANTHROPIC_API_KEYANTHROPIC_AUTH_TOKEN を子プロセスの env から取り除くことです。これでサブスク認証に確実に寄せられます。


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 自体はいじらないことです。.envANTHROPIC_API_KEY を従量課金モードで正当に使っている別モジュールがある場合、それは壊したくありません。だから「親プロセスの環境はそのまま」「claude -p に渡す子プロセスの env からだけ外す」という形にします。これなら他のジョブには無害です。

別の選択肢

状況によっては、こちらの方が早いこともあります。

  • そもそも export しないclaude -p を呼ぶ前のシェルで unset ANTHROPIC_API_KEY する。ただし .env を読むアプリ内からだと効きにくい
  • forceLoginMethod で縛るclaude 側の設定で API キー認証を起動時にブロックする。サブスク固定を強制したいときに有効。ただし API キーで動かしたい他用途まで巻き込むので、用途が混在する環境では注意
  • キーの置き場を分ける:従量課金で使いたいモジュール専用の名前(例:別の環境変数名で受けてコード内で ANTHROPIC_API_KEY に橋渡し)にして、グローバルな ANTHROPIC_API_KEY を環境に置かない

私は、従量課金で正当にキーを使うモジュールが同居していたので、「子プロセス env から外す」案を採りました。

いま課金経路がどっちか、を確認する方法

「自分のジョブはサブスクで動いている」と思い込むのが一番危ないので、確認しておくと安心です。

手軽なのは、claude -p を呼んでいるのと同じ環境変数の状態で、対話的に起動して状態を見ることです。


# 子プロセスに渡しているのと同じ env でログイン状態を確認
claude
# 起動後に /status を見ると、ログイン方法(サブスク or API キー)が分かる

あるいは、API キーが環境にあるかをまず疑います。


env | grep ANTHROPIC

ここに ANTHROPIC_API_KEY が出てくるなら、その状態で claude -p を叩いている限り、従量課金で動いていると考えてよいです。

📝 メモ: API残高がある間はエラーが出ません。「動いているから正常」ではなく、「どの認証で動いているか」を一度は確認しておくのがおすすめです。

教訓:env={**os.environ} は便利だけど、認証情報まで素通しする

今回の根っこは、Claude Code 特有の話というより、subprocessenv={</strong>os.environ} でまるごと環境を渡す**という、ありふれた書き方にありました。

.env に何か1個、認証系の環境変数が増えるたびに、それを起動する全ての子プロセスが影響を受けます。普段は無害でも、今回のように「その環境変数を見て挙動を変えるツール」を呼んだ瞬間、意図しない経路に倒れます。

外部コマンドを subprocess で叩くときは、

  • 本当にその子プロセスに必要な環境変数だけを渡す
  • 少なくとも、認証系(APIキー・トークン)は意識して取捨選択する

これを癖にしておくと、今回みたいな「静かに課金が溶ける」系の事故を防ぎやすくなります。

最後に念のため。.env や API キーはログ・コミットに残さないよう、いつもどおり気をつけてください。今回のように「env を子プロセスへ素通し」していると、デバッグで env をダンプした拍子にキーが流出する、という二次災害も起こり得ます。

サブスクで Claude Code を回しているつもりが、いつの間にか従量課金で溶けていた——同じ構成の人は、一度 env | grep ANTHROPIC を確認してみてください。

コメント