「API課金をゼロにした」と5ヶ月目の月次レポートに書いた。あれは正確ではない。パイプラインのAI呼び出しをGeminiに逃がしただけで、無料枠が切れた今は普通に課金が出ている。
- 無人の定期処理をClaude Code CLIのヘッドレス実行に置き換え、API従量課金をゼロにした。Windows特有の詰まりどころは「argv渡しで改行以降が落ちる」「cwdがプロジェクト内だとCLAUDE.mdを読み込む」の2つ。タスクスケジューラ経由の動作は実機で確認済み。

1回目のゼロ化は「安いモデルに逃がす」だった。2回目は「そもそも従量課金の経路を通らない」。後者のほうが素直だった。
SE歴20年で副業ブログを運営している筆者が、同じ問題をもう一度潰した。今度は方向を変えて、Claude CodeのCLIをタスクスケジューラから直接叩く。サブスクの範囲で動くので従量課金が発生しない。
ただしWindowsでは、素直に組むと2箇所で壊れる。プロンプトが途中で消えるのと、カレントディレクトリを間違えるとAIが勝手にリポジトリを漁り出すのと。どちらも再現手順つきで書く。
無人処理でだけAPI課金が出る
Claude CodeをPro/Maxプランで対話的に使っている限り、追加の従量課金は発生しない。困るのは無人で動かす処理のほうだ。
朝7時に自動で走る処理には、人間が座っていない。セッションが無いのでClaude Codeは使えない。そこでAnthropic APIやGemini APIを直接呼ぶことになり、そこだけ従量課金が乗る。
筆者のパイプラインでいうと、記事のタイトルとメタディスクリプションを生成する処理がこれに当たる。1回あたりは数円だが、毎週土曜に自動実行される。
1回目のゼロ化は失敗していた
2026年7月、筆者はこれをVertex AI経由のGeminiに置き換えた。当時は「無料トライアルクレジットが効いてゼロになった」と書いた。
これが誤認だった。GCPコンソールで確認し直すと、当てにしていたクレジットは対象プロダクトが限定されていて、素のGemini API呼び出しには適用されていない。実際に効いていたのは別枠の汎用クレジットで、2026年7月9日に期限切れ。以降は月数百円が普通に発生している。
単価の安いモデルに逃がしただけで、課金が乗る構造そのものは残っていた。経緯と数字は5ヶ月目の収支と出費の内訳に書いたとおりで、ここでは結論だけ引き継ぐ。
APIを呼ばず、CLIを呼ぶ
Claude CodeにはCLIから非対話で実行するモードがある。claude -p で起動すると、対話セッションを開かずにプロンプトを渡して結果だけ受け取れる。公式ドキュメントではヘッドレス実行と呼ばれている。
これをタスクスケジューラから叩けば、無人でも「Claude Codeを使っている」状態になる。APIキーは要らない。端末に入っているサブスクの認証情報で動く。
課金経路を切り替えるのではない。通らなくする。
Windowsで壊れる2箇所
プロンプトの改行以降が消える
最初はPythonから素直にこう書いた。
import subprocess, shutil
r = subprocess.run([shutil.which("claude"), "-p", prompt],
capture_output=True, text=True, encoding="utf-8")
これで多行のプロンプトを渡すと、答えが噛み合わない。指示は理解している。なのに、その後ろに並べたはずのデータが「渡されていない」と言われる。
最小構成で再現させた。39文字の3行プロンプトを投げる。
p = "次の2語を続けて返して。\n1語目: アルファ\n2語目: ベータ\n他は書かない。"
返答は「2語、どの2語か不明。指定して。」だった。1行目しか届いていない。
原因はclaudeの実体がバッチファイル(claude.CMD)であることだ。改行を含む引数がそこで切れる。Pythonのリストで渡しているので通常のシェル分割は起きないが、バッチのラッパーを経由する時点で改行が境界として扱われる。
厄介なのは、これがエラーにならないことだ。終了コードは0。監視していても気付かない。
改行を消しても、今度は長さで落ちる
では1行にすれば解決するか。しない。改行なし8,429文字のプロンプトを同じ方法で渡すと、今度は別の壊れ方をした。
rc=1
stderr: コマンド ラインが長すぎます。
Windowsのコマンドライン長制限に当たっている。実務で使うプロンプトは、参考データを添えると数千文字を簡単に超える。
2つの壊れ方を並べるとこうなる。

つまりargv渡しは二重に不安定だった。stdin経由に変えれば両方まとめて回避できる。
r = subprocess.run([shutil.which("claude"), "-p"], input=prompt,
capture_output=True, text=True, encoding="utf-8",
timeout=300)
-pの後ろにプロンプトを置かず、input=で流し込む。これで改行も長さも問題にならなくなった。
cwdがプロジェクト内だと勝手に働き出す
2つ目は気付きにくかった。プロンプトは正しく届いているのに、返答が明後日の方向を向く。
原因はカレントディレクトリだった。プロジェクト内で起動すると、Claude CodeはそのリポジトリのCLAUDE.mdとスキルを読む。そして従う。
実際に返ってきた答えがこれだ。
候補が貼られていない。メッセージ本文に素材が無い。
リポジトリ内を探した結果:drafts/.x_candidate_state.json→last_runのみoutput/x_drafts/→ 7/28で止まってる
候補を貼り直すか、どこにあるか教えて。
頼んでいないリポジトリ探索を始めている。さらに「API課金の有無を先に確認する必要がある」とまで言い出した。筆者のCLAUDE.mdに書いてあるコスト厳守ルールを読んで、律儀に従った結果だ。
無人処理でこれをやられると、余計なファイル読み込みでトークンを食う。意図しない副作用を起こす余地も残る。
対策は作業用の空ディレクトリをcwdにするだけでいい。
work = BASE_DIR / "drafts" / ".x_compose"
work.mkdir(parents=True, exist_ok=True)
r = subprocess.run([shutil.which("claude"), "-p"], input=prompt,
capture_output=True, text=True, encoding="utf-8",
timeout=300, env=env, cwd=str(work))
ユーザー階層に置いたスキルはこの状態でも読まれる。汎用の文体ルールなどは効かせたまま、プロジェクト固有の指示だけを切り離せる。
スケジューラから動くかを確かめる
手元のターミナルでclaude -pが動くことと、タスクスケジューラが起動したプロセスで動くことは別の話だ。認証情報を読めるとは限らない。
7時の定時実行を待たずに確かめる方法がある。トリガーを1年後に設定してタスクを登録し、手動で起動する。勝手には走らない。
$a = New-ScheduledTaskAction -Execute $py -Argument "`"$sc`""
$t = New-ScheduledTaskTrigger -Once -At (Get-Date).AddYears(1)
Register-ScheduledTask -TaskName 'ClaudeSchedProbe' -Action $a -Trigger $t -Force
Start-ScheduledTask -TaskName 'ClaudeSchedProbe'
呼び出す側は合言葉を返させるだけの短いスクリプトにして、結果をテキストに書き出させた。
2026年9月5日8時43分、書き出されたファイルの中身がこれだ。

ANTHROPIC_API_KEYが環境に無い状態で、終了コード0、指示どおりの応答。タスクスケジューラ経由でもサブスクの認証情報を読めている。
確認が済んだのでタスクは削除した。検証用のタスクを残すと、忘れた頃に動いて事故になる。
環境変数からAPIキーを外す
念のため、呼び出し側でAPIキーを環境から落としている。
env = {k: v for k, v in os.environ.items() if k != "ANTHROPIC_API_KEY"}
これは予防措置であって、残すと課金されることを実測で確かめたわけではない。ただ、無人処理で気付かないうちに従量課金の経路へ落ちるのが一番怖い。明示的に潰しておく。
組み込んだ処理と、割り切った点
筆者がこの仕組みを入れたのは、Xの投稿候補を毎朝拾い出す処理だ。Claude Codeのセッションログには、記事にならなかった実測や、調べたが解決しなかった話が残っている。そこから素材を拾い、投稿できる文面まで整えてSlackに流す。
- STEP1素材を拾う
セッションログを走査し、実測値・金額・未解決の記述を含む段落を候補として抽出する。ここはAIを使わない正規表現処理。
- STEP2文面にする
候補と抽出元の文脈をstdin経由で
claude -pに渡し、投稿文を作らせる。事実は文脈にあるものだけを使わせる。 - STEP3機械で検査する
出てきた文を文体チェックのlintにかけ、指摘が出たものだけ指摘を添えて1回書き直させる。
- STEP4Slackへ流す
1件1メッセージで送る。スマホの長押しコピーはメッセージ単位だ。1通に詰め込むと、1件だけ取り出せない。
サブスクの枠は普通に減る
無料で無限に回せる仕組みではない。枠は減る。
ヘッドレス実行も対話セッションと同じサブスクの利用枠を使う。5時間枠と週次枠を消費するので、無人処理を回しすぎると自分の対話作業が枠に当たる。どれくらい食うかは実測していないので、導入するなら/usageで前後を比べたほうがいい。
うちは1日1回・候補5件で、体感できる影響は出ていない。規模が小さいだけだ。
規約面をどう考えたか
サブスクで無人実行することの是非にはふれておく。筆者は問題ないと判断したが、根拠を書いておくので各自で確認してほしい。
-pはドキュメント化された正規のモードで、公式ドキュメントにヘッドレス実行として記載がある。加えてバージョン2.1.259では--permission-prompts noneが追加され、説明文に「無人ヘッドレスホスト向け」と明記されている。無人実行を想定した機能が公式に足されている。
そしてレート制限は回避していない。対話セッションと同じ枠を同じように消費する。
一方で、アカウントの共有、第三者のリクエストを裏で処理させる用途、複数アカウントでの制限回避は別問題になる。ここは規約本文を読んで判断してほしい。規約全文を精査したわけではないので、断定は避ける。
向かない場面
claude -pは一往復で終わる。途中で権限を尋ねられたり判断を求められたりする処理は止まる。2.1.259以降なら--permission-prompts noneで、プロンプトが出る場面を自動で拒否扱いにできる。無人処理では黙って止まるより、明示的に落としたほうが原因を追いやすい。
出力の形式も安定しない前提で組む。JSONを要求してもコードフェンスで包まれたり説明文が付いたりする。正規表現でJSON配列部分だけを抜き出し、パースに失敗したら素の抽出結果へフォールバックする形にした。無人処理で例外を投げて止まるより、品質を落として動き続けるほうが運用しやすい。
時間で起動するのではなく「条件を満たすまで自走させる」なら、/goalのほうが向いている。時間駆動と完了駆動の使い分けは完了条件を書いてAIに自走させる方法で扱った。あちらでは時間駆動の無人実行を「公式情報・筆者未検証」としていたが、今回それを実機で埋めた形になる。
よくある質問(FAQ)
- Qclaude -p はどのバージョンから安定して使えますか?
- A
筆者が確認したのは2.1.261です。ただしPythonの
subprocess.runからの起動については、公式の変更履歴によると2.1.144で「明示的なstdinなしでサブプロセスとして起動するとハングする」問題が修正されています。それ以前のバージョンで同じ組み方をすると止まる可能性があります。旧バージョンでの再現は試していません。
- Qプロンプトが途中で切れているかどうかを見分ける方法はありますか?
- A
返答の内容で判断できます。実際に返ってきたのは「候補が貼られていない。メッセージ本文に素材が無い」という答えでした。指示の1行目「素材は下の候補」だけが届き、後ろのデータが無い状態です。終了コードは0で返るため、監視しているだけでは気付けません。
- Qcwdを変えるとプロジェクトのスキルが使えなくなりませんか?
- A
プロジェクト直下の
.claude/配下のものは読まれなくなります。一方でユーザー階層(~/.claude/skills/)に置いたスキルは、cwdを変えても使えます。文体チェック用のスキルはユーザー階層に置いてあるため、プロジェクト固有の指示だけを切り離せました。
- Qタスクスケジューラの検証を定時まで待つ必要はありますか?
- A
ありません。トリガーを1年後に設定してタスクを登録し、
Start-ScheduledTaskで手動起動すれば同じ経路を通ります。この方法なら、登録から結果確認まで数分で済みます。検証が終わったらタスクは削除してください。残すと忘れた頃に走ります。
- QGemini経由に置き換える方法とどちらがいいですか?
- A
処理の量によります。2026年7月にGeminiへ逃がしたところ、無料枠が切れた後は月数百円が発生しています。単価が安いだけで課金構造は残ったままでした。Claude Code経由なら従量課金は発生しませんが、サブスクの利用枠を消費します。大量に回すならGemini、少量ならClaude Code、という分け方が現実的です。
- QWindowsのコマンドライン長制限は何文字ですか?
- A
手元の環境では8,429文字のプロンプトで「コマンド ラインが長すぎます。」というエラーが出ました。制限値そのものはシェルや呼び出し方で変わるため、この数字を境界として扱わないでください。stdin経由にすればこの制限自体を回避できます。
まとめ
やり直して得たものは3つある。
安いモデルに逃がすのは構造の解決ではなかった。2026年7月にGeminiへ移して「ゼロ化した」と書いたが、無料枠が切れれば元に戻る。課金経路を通らなくするほうが根本的だった。
Windowsでの起動方法はstdin一択だ。argv渡しは改行で切れ、長さでも落ちる。前者は3行39文字で再現し、後者は8,429文字で「コマンド ラインが長すぎます。」が出た。どちらもエラーの出方が分かりにくく、片方だけ直しても解決しない。
cwdはプロジェクト外に置く。中で起動するとCLAUDE.mdを読んで勝手に働き出す。
タスクスケジューラ経由の動作は2026年9月5日に実機で確認した。ただしサブスクの枠をどれだけ消費するかは測っていない。導入するなら/usageで前後を比べてほしい。
なお、セッション内のトークンを削る話はこれとは別の軸になる。モデルの使い分けやキャッシュ設計は3層スタックでコストを半減させる手順にまとめてある。

2回目でやっと「経路を通らなくする」に辿り着いた。1回目の失敗を書き残しておいてよかった、と今になって思う。


