Claude Media
fallback_credit_tokenで二重課金を防ぐ — Claude API

fallback_credit_tokenで二重課金を防ぐ — Claude API

refusalの再試行で別モデルに切り替えるとプロンプトキャッシュが書き直しになり課金が二重になります。fallback_credit_tokenで相殺する手順を扱います。

Claude APIのrefusal(安全性分類器による拒否)を自前のリトライロジックで処理しているなら、モデルを切り替えた瞬間に課金が二重になっている可能性があります。原因はプロンプトキャッシュがモデル単位で管理されている点です。fallback_credit_token は、この二重払いを打ち消すためのフィールドです。

fallback_credit_tokenは何を相殺する仕組みか

プロンプトキャッシュはモデルごとに別々に持たれます。あるモデルで拒否された会話を別モデルへリトライすると、それまで最初のモデル用に貯めていたキャッシュはそのまま使えず、新しいモデルのキャッシュをゼロから書き込み直すことになります。キャッシュの書き込みは読み込みより単価が高いため、リトライのたびにこの差額を余計に払う形になります。

fallback_credit_token はこの差額を相殺するトークンです。拒否レスポンスにトークンが乗って返ってくるので、それをリトライのリクエストに乗せて送り返すと、あたかも会話が最初からリトライ先のモデルで進んでいたかのような単価で課金されます。この仕組みが必要なのは、生のHTTPリクエストや自前のリトライロジックでフォールバックを組む場合だけです。サーバーサイドフォールバックや公式SDKのミドルウェアを使っているなら、この処理は自動で行われます。

使い方の4ステップ

curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: fallback-credit-2026-07-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-fable-5",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello, Claude"}]
  }'
  1. ベータヘッダーでopt-inする: リクエストに anthropic-beta: fallback-credit-2026-07-01 を付けます。server-side-fallback-2026-07-01 を付けている場合も同じフィールドが返るので二重に指定する必要はありません。
  2. 拒否レスポンスから2つのフィールドを読む: stop_reason: "refusal" のとき、stop_detailsfallback_credit_token(トークン文字列)と fallback_has_prefill_claim(真偽値)が入ります。相殺できる余地がない拒否では両方とも null です。
  3. fallback_has_prefill_claim の値でリトライの形を選びます。この値がリトライボディの組み立て方を決めます(次表)。
  4. リトライには同じベータヘッダーを付けて送ります。トークンを引き換えるにはリトライ側にも同じヘッダーが必要です。
fallback_has_prefill_claimリトライボディの形
trueリトライボディの形拒否されたリクエストをそのまま使い、末尾に assistant メッセージを1つ追加して拒否モデルの出力をそのままechoする。リトライ先のモデルは途中から続きを書き、完了済みのサーバーツール呼び出しは再実行されない
falseリトライボディの形拒否されたリクエストボディをそのまま使う(追加なし)

トークンはリクエストのトップレベルに fallback_credit_token として渡し、model をリトライ先に変えるだけです。会話履歴の system / messages / tools / tool_choice / thinking などプロンプトを構成するフィールドは、拒否されたリクエストと完全一致させる必要があります。逆に model / max_tokens / temperature / stream などは変更してかまいません。

リトライが拒否されたときの3段階の縮退

トークンを使ったリトライも、それ自体が拒否されることがあります。その場合は次の順で縮退させます。

  1. continuation形が拒否される: fallback_has_prefill_claim: true に従ってassistantメッセージを追記したリトライが400で返ってきたら、追記なしの元のボディへ戻し、トークンは付けたまま再送します。
  2. トークンそのものが拒否される: 元のボディに戻しても400が返り、エラーメッセージに fallback_credit_token の名前が含まれるなら、トークンを諦めてリトライだけを送ります。相殺は失われますが、リトライ自体は通ります。
  3. redemption temporarily unavailable」は縮退の合図ではありません。これは一時的なエラーなので、同じ形・同じトークンのままトークンの有効期限内に再送します。

なお、拒否が発生するまでにサーバーツール(web検索やコード実行など)が実行済みだった場合、トークンなしのリトライはそのツール呼び出しを再実行し、再び課金します。この場合は静かにトークンなしリトライへ切り替えず、エラーを呼び出し元へ伝える設計にしておくと、意図しない再課金を避けられます。

効いたかどうかはusageで確認する

相殺が適用されたかは、リトライレスポンスの usage で確認できます。トークンなしで同じリクエストを送った場合と比べて、cache_creation_input_tokens(キャッシュ書き込み分)が減り、その分だけ cache_read_input_tokens(キャッシュ読み込み分)が増えます。差分がゼロなら、トークン自体は受理されたものの相殺すべき差額が元々なかった(リトライ先のキャッシュが既に温まっていた等)ということです。

拒否レスポンスのcontentをそのままechoするときの注意

fallback_has_prefill_claim: true のとき、拒否レスポンスの content には拒否モデル自身の出力だけが入っており、拒否の理由は stop_details.explanation 側に別で入っています。そのため content はそのままリトライのassistantメッセージへechoして問題ありませんが、送る前に2点だけ調整が必要です。

  • 送る最後のブロックが text ブロックなら、末尾の空白を削る
  • 対応する tool_result を持たないクライアント側の tool_use ブロックは省く

echoした内容に、それ以前のサーバーサイドフォールバックによる fallback ブロックが含まれている場合は、その位置をそのまま保持します。APIはこのブロックの位置を使って前後のthinkingブロックを検証するため、位置をずらしたり省いたりすると、その前後のthinkingブロックを含むリトライごと拒否されます。

ベータヘッダーも完全一致が必要

一致チェックの対象は system / messages / tools などのボディだけではありません。拒否されたリクエストとリトライで送る anthropic-beta ヘッダーも完全に揃える必要があります。片方だけに付いているベータヘッダーがあると、ボディが完全に同じでも400エラーになります。厄介なのは、このエラーメッセージがボディ不一致のときと同じ request body ... does not match になる点です。リトライが400で落ちたとき、ボディを何度見直しても差分が無いなら、まずヘッダー側の差分を疑います。

例外は2系統だけです。server-side-fallback-* ヘッダーはリトライ時に fallbacks パラメータを外す必要があり、このヘッダーを一緒に外しても不一致にはなりません。逆に fallback-credit-* ヘッダーはトークンを引き換えるために両方のリクエストで維持する必要があります。また、Claude Fable 5.1やOpus 5のように1Mトークンコンテキストウィンドウが既定になっているモデルでは context-1m-2025-08-07 ヘッダー自体が意味を持たないため、片方にだけ付けるのではなく、両方から外して揃えておくのが安全です。

許可されているリトライ先モデルを動的に調べる

リトライ先はどのモデルでも良いわけではなく、拒否したモデルの許可リストに含まれるモデルに限られます。server-side-fallback-2026-07-01 ベータヘッダーを付けたリクエストでは、Models APIの各モデルのエントリに allowed_fallback_models として許可リストが公開されます。fallback-credit-* ヘッダー単独ではまだこのリストは公開されず、Amazon Bedrock・Google Cloud・Microsoft Foundryでも非公開です。ハードコードで運用する場合も、モデル世代が交代するたびに許可リストが変わりうる前提で、allowed_fallback_models を都度確認し直す設計にしておきます。

いつどの仕組みを使うか

実装手段fallback_credit_tokenの扱い向いている場面
サーバーサイドフォールバック(fallbacks パラメータ)fallback_credit_tokenの扱い自動適用、意識不要向いている場面1リクエスト1レスポンスで完結させたい
SDKミドルウェアfallback_credit_tokenの扱い自動適用、意識不要向いている場面複数の言語SDKで横断的にリトライを組みたい
生HTTP / 自前のリトライロジックfallback_credit_tokenの扱い本記事の手順が必須向いている場面Message Batchesを使わない直接呼び出しで、リトライの挙動を細かく制御したい

Message Batches APIの結果ではrefusalが succeeded として返り、fallback_credit_token はそもそも発行されません。バッチ内のrefusalを拾い直す場合は、thinkingブロックを取り除いたうえで新規バッチか直接リクエストとして再送する形になります。

なお同じ「フォールバック」という言葉でも、Claude Codeの fallbackModel 設定は過負荷時にCLI側が自動でモデルを切り替える別機能で、fallback_credit_token とは対象レイヤーもAPI面も異なります。混同しないよう区別しておきます。

トークンが効かない境界条件

  • 有効期限は拒否から5分。それを過ぎたらトークンなしで送るしかありません。トークン自体はステートレスで、発行後にサーバー側で内容を照会・失効させる手段はありません。
  • スコープは組織とワークスペース単位(Microsoft Foundryでも同様)。Amazon BedrockとGoogle Cloudにはワークスペース概念がないため、代わりに呼び出し元のIDに紐づきます。
  • リトライ先は拒否したモデルの許可リストに含まれるモデルに限られます。Claude Fable 5.1とFable 5の場合はClaude Opus 4.8とClaude Opus 5です。
  • output_config.format や特定ツールを強制する tool_choice を使っていて、かつ拒否がサーバーツール実行後に起きた場合、continuation形は使えず(ツール強制が邪魔をする)、元のボディへの縮退もできません(サーバーツールの再実行を防げない)。この組み合わせに当たったらトークンは諦め、再実行のコストか失敗をそのまま呼び出し元へ伝えるのが安全です。

fallback_credit_token の詳しい検出条件や、stop_reason: "refusal" 自体の見分け方はClaude APIのrefusal stop_reasonを検出してリセットする方法で扱っています。SDKのミドルウェアで横断的にリトライを組む場合の登録方法はClaude SDKのミドルウェアで共通処理を挟むを参照してください。

まとめ

fallback_credit_token は、refusalリトライで別モデルに切り替えたときのプロンプトキャッシュ書き込みコストを相殺する仕組みです。使うのは生HTTPや自前リトライロジックを組んでいる場合に限られ、サーバーサイドフォールバックとSDKミドルウェアはこれを自動で処理します。実装するときは、fallback_has_prefill_claim に応じたボディの組み立て、拒否時の3段階の縮退、そしてサーバーツール実行後の境界条件を押さえておけば、二重課金を避けながらリトライを組めます。

この記事を共有:XはてブLinkedIn