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

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

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

Claude APIのrefusal(安全性分類器による拒否)を自前のリトライで処理しているなら、モデルを切り替えた瞬間にキャッシュの書き込み料金が二重にかかっている可能性があります。原因はキャッシュがモデル単位で管理されている点です。fallback_credit_token は、この二重払いを打ち消すためのフィールドです。前払い残高の失効や返金の条件はClaude APIの前払いクレジットの規約にまとめています。

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

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

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

相殺されるのはキャッシュの書き込み差額だけです。拒否そのものの課金は別で、出力が始まる前の拒否でも stop_details.category が "bio"、"frontier_llm"、"reasoning_extraction" のものは通常のリクエストと同じ単価で課金されます。それ以外のカテゴリや null の拒否は出力前なら課金されません。ストリーミングの途中で拒否された場合は、入力トークンと送出済みの出力が課金されます。フォールバックを使うと、課金対象の拒否に加えてリトライ分も課金される形です。

使い方の4ステップ

手順

拒否からリトライまで

  1. 1

    ベータヘッダーでopt-inする

    最初のリクエストに anthropic-beta: fallback-credit-2026-07-01 を付けます。

  2. 2

    拒否レスポンスから2つのフィールドを読む

    stop_reason: "refusal" のとき、stop_details に fallback_credit_token(トークン文字列)と fallback_has_prefill_claim(真偽値)が入ります。相殺できるクレジットがない拒否では両方とも null です。

  3. 3

    fallback_has_prefill_claimでリトライボディの形を選ぶ

    拒否されたリクエストのボディを起点に、model をリトライ先へ変え、トップレベルに fallback_credit_token を足します。ボディの形は次の比較のとおりです。

  4. 4

    同じベータヘッダーを付けて送る

    トークンを引き換えるには、リトライ側にも同じヘッダーが必要です。

くらべる

fallback_has_prefill_claimの値とリトライボディ

続きから書かせる

true

拒否されたリクエストの末尾に assistant メッセージを1つ追加し、拒否レスポンスの content をそのままechoします。リトライ先は拒否モデルが止まった位置から続きを書き、完了済みのサーバーツール呼び出しは再実行されません。

最初からやり直す

false

拒否されたリクエストのボディを、追加なしでそのまま使います。

fallback_has_prefill_claim が null なのは、トークン自体が null のときです。例外はAmazon Bedrock、Google Cloud、Microsoft Foundryで、対応が順次広がる途中のためトークンがあっても欠けることがあります。その場合は false とみなさず、まず assistant メッセージを追加した形で送り、400で拒否されたら次節の手順で元のボディへ落とします。

実際のフローは次のようになります。最初のリクエストで拒否されたらトークンを取り出し、同じボディにトークンを足してOpus 4.8へ送ります。

response=$(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"}]
  }')
 
token=$(jq -r '.stop_details.fallback_credit_token // empty' <<<"$response")
 
if [[ -n "$token" ]]; then
  response=$(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 "$(jq -n --arg token "$token" '{
      model: "claude-opus-4-8",
      max_tokens: 1024,
      messages: [{"role": "user", "content": "Hello, Claude"}],
      fallback_credit_token: $token
    }')")
fi
 
jq -c '{stop_reason, model}' <<<"$response"

このサンプルは fallback_has_prefill_claim を見ず、元のボディにトークンを足した形で送っています。true が返る拒否では、続きから書かせる形なら完了済みのサーバーツール呼び出しが再実行されません。

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

トークンを使ったリトライも、それ自体が拒否されることがあります。拒否は400エラーで返り、次の順に形を簡単にしていきます。

手順

縮退の順序

  1. 1

    続きから書かせる形が拒否される

    assistant メッセージを追記したリトライが400で返ってきたら、追記なしの元のボディへ戻し、トークンは付けたまま再送します。

  2. 2

    トークンそのものが拒否される

    元のボディに戻しても400が返り、エラーメッセージに fallback_credit_token の名前が含まれるなら、トークンを諦めてリトライだけを送ります。相殺は失われますが、リトライ自体は通ります。

「redemption temporarily unavailable」というエラーは、この順序を進める合図ではありません。一時的なエラーなので、同じ形・同じトークンのまま、トークンの有効期限内に再送します。

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

効いたかどうかは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ブロックをechoしたリトライが拒否されます。

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

引き換え時には、拒否されたリクエストとリトライを突き合わせて、プロンプトを形づくるフィールドが完全に一致しているかを見ます。

区分フィールド
完全一致が必要フィールドsystem / messages / tools / tool_choice / thinking / cache_control、使っていれば output_config / mcp_servers / context_management / container
リトライで変えてよいフィールドmodel / max_tokens / stop_sequences / temperature / top_p / top_k / stream / metadata / service_tier

messages の例外は、続きから書かせる形で末尾に assistant メッセージを1つ足すことだけです。また、通常のトークンなしリトライでは以前のターンの thinking / redacted_thinking ブロックを取り除くことがありますが、引き換え時は取り除かないでください。ボディが拒否時と一致している必要があり、前のモデルのthinkingブロックの扱いはサーバー側が引き受けます。

一致の対象は anthropic-beta ヘッダーにも及びます。片方のリクエストにだけ付いているベータヘッダーがあると、ボディが同一でも一致しないことがあります。厄介なのは、このエラーが request body ... does not match というボディ不一致と同じメッセージで返る点です。ボディを何度見直しても差分が無いなら、ヘッダー側の差分を疑います。モデルによってヘッダーを足したり外したりする実装も、この不一致の原因になります。

例外は2系統だけです。server-side-fallback-* ヘッダーはリトライ時に fallbacks パラメータを外す必要があり、このヘッダーを一緒に外しても不一致にはなりません。逆に fallback-credit-* ヘッダーは、トークンを引き換えるために両方のリクエストで維持します。

1Mトークンのコンテキストウィンドウが既定のモデルでは、context-1m-2025-08-07 ヘッダーに効果がありません。対象はClaude Fable 5.1・Fable 5・Opus 5.5・Opus 5・Opus 4.8・Sonnet 5.5・Sonnet 5です。片方にだけ付けると不一致になるので、両方から外して揃えます。

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

リトライ先は、拒否したモデルの許可リストに含まれるモデルに限られます。Claude APIとClaude Platform on AWSでは、server-side-fallback-2026-07-01 ベータヘッダーを付けたリクエストでModels APIの各モデルのエントリに allowed_fallback_models として許可リストが公開されます。fallback-credit-* ヘッダー単独ではまだこのリストが見えず、Amazon Bedrock・Google Cloud・Microsoft Foundryでは公開されていません。

いつどの仕組みを使うか

実装手段fallback_credit_tokenの扱い向いている場面
サーバーサイドフォールバック(fallbacks パラメータ)fallback_credit_tokenの扱い自動適用、意識不要向いている場面1リクエスト1レスポンスで済ませたい(Claude API)
SDKミドルウェアfallback_credit_tokenの扱い自動適用、意識不要向いている場面Anthropic SDKを使っていて、どのプラットフォームでも自動でリトライしたい
生HTTP / 自前のリトライロジックfallback_credit_tokenの扱い本記事の手順が必須向いている場面リトライの挙動を細かく制御したい

Message Batches APIの結果ではrefusalが succeeded として返り、fallback_credit_token は発行されません。バッチのリクエストに付けたトークンは受理されたうえで無視されます。バッチ内のrefusalを拾い直すときは、拒否された項目を集め、Claude Fable 5.1またはFable 5の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-4-8)とClaude Opus 5(claude-opus-5)です。
  • サーバーツール実行後に拒否が起き、かつ output_config.format や、ツール使用を強制する tool_choice を使っている場合、トークンはどちらの形でも引き換えられません。追記する形はこの2つが妨げ、元のボディの形は、サーバーツールの再実行を防ぐ制約で使えません。元のボディでのリトライが「部分応答を続ける形で引き換える必要がある」という趣旨の400で拒否されたら、トークンは捨てます。トークンなしのリトライは通りますが、完了済みのサーバーツールを再実行して再課金するので、そのコストかエラーを呼び出し元へ伝えます。

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

まとめ

自前でリトライを組むなら、拒否レスポンスのトークンをそのまま使い捨てにせず、ボディとベータヘッダーを拒否時と揃えて送り返すのが前提です。サーバーサイドフォールバックかSDKミドルウェアに寄せれば、この作業ごと不要になります。

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