Anthropic says it is resuming charges for some requests that its safeguards block before Claude produces an answer. The change applies to selected categories that the company says have low false-positive rates, rather than to every Claude refusal.
In a post on X, @ClaudeDevs names biology, distillation attacks, and frontier LLM development as the affected areas. The announcement also says that coordinated attacks on Anthropic’s systems contributed to the decision. Source on X
The Claude refusal and fallback documentation uses a different set of labels for its billing table: bio, frontier_llm, and reasoning_extraction. Developers should use the category returned by the API and consult the current documentation rather than assume that the announcement’s descriptions and the documentation’s labels are interchangeable.
What Anthropic reported about frequency
The announcement says that 99.7% of accounts using Claude Code, Claude.ai, or Cowork did not encounter any of the newly billable blocks in recent testing. It also says the classifiers used for those blocks were tuned to a false-positive rate below 0.1%.
Those figures describe Anthropic’s reported testing results. The post acknowledges that a false-positive rate below 0.1% is not zero and directs Claude Code users who believe a request was blocked incorrectly to report it with /feedback.
The account-level figure should not be treated as a prediction for every API customer or workload. The announcement discusses Claude Code, Claude.ai, and Cowork, while the documentation describes refusal billing across the Claude API, Amazon Bedrock, Claude Platform on AWS, Google Cloud, and Microsoft Foundry.
Which refusal categories are billed?
The documentation says that a refusal arriving before any output is billed when stop_details.category is one of these values:
Documentation category | Description | Billed before any output? |
|---|---|---|
| Requests that could enable cyber harm | No |
| Requests that could enable biological harm | Yes |
| Requests that could assist development of competing AI models | Yes |
| Requests to reproduce a model’s internal reasoning in the response | Yes |
| Other usage-policy areas outside the named categories | No |
The documentation also says that benign work can trigger a classifier and that the billed-category list may change as Anthropic measures and refines its safeguards. A before-output refusal with another category, or with a null category, is not billed under those rules. The request still counts against rate limits.
How a refusal appears in an API response
A safety refusal is normally a successful HTTP 200 response rather than an API error. Developers should inspect the response fields instead of relying on HTTP errors or missing text alone.
A simplified refusal has this shape:
{
"model": "claude-fable-5",
"content": [],
"stop_reason": "refusal",
"stop_details": {
"type": "refusal",
"category": "bio",
"explanation": "This request was declined because it could enable biological harm."
},
"usage": {
"input_tokens": 412,
"output_tokens": 0
}
}The key signal is stop_reason: "refusal". The stop_details.category field identifies the policy area when the refusal maps to a named category. Both category and explanation can be null, so application logic should not require either field to be populated. The documentation says the explanation is for display and is not stable enough to parse as a control signal.
A refusal before any output has an empty content array and still counts against rate limits. It is billed only when its category is one of the documentation’s billable categories. A refusal in another category, or with a null category, is not billed before output.
Streaming refusals have different billing consequences
A refusal can arrive after generation has started. If it occurs mid-stream, the input tokens and output already streamed are billed at normal rates. Applications should treat any partial output as incomplete and discard it rather than presenting it as a finished answer.
This can produce both a partial first attempt and a subsequent fallback attempt. Each attempt is evaluated and billed under the rules for the model that runs it: output-producing attempts are billed, while a before-output refusal is billed only when its category is billable.
Fallback can create additional attempts
The documentation describes fallback as a way to send a refused request to another Claude model. On the Claude API, developers can request server-side fallback, use Anthropic SDK middleware, or implement a retry themselves. Fallback does not guarantee a successful answer: the fallback model can also refuse, be rate limited, or be unavailable.
When fallback runs, the original refusal is billed if it was a mid-stream refusal or belonged to one of the documentation’s billable before-output categories. The fallback attempt is evaluated and billed under the rules for the model that runs it: output-producing attempts are billed, while a before-output refusal is billed only when its category is billable.
Fallback credit compensates for the fallback request’s prompt-cache miss, so developers do not pay to cache the conversation twice. Server-side fallback and Anthropic’s SDK middleware apply that credit automatically. Developers implementing their own retry must redeem the fallback credit according to the documentation. The credit does not make every attempt free.
For server-side fallback, the response identifies the model that ultimately served the request in the top-level model field. The usage.iterations array records each attempt, including a refused one. A fallback_message entry indicates that a fallback model ran.
For example, a developer can check whether a fallback attempt served the response like this:
fallback_ran = any(
iteration.type == "fallback_message"
for iteration in response.usage.iterations or []
)
served_by_fallback = fallback_ran and response.stop_reason != "refusal"The top-level usage fields describe the attempt that produced the returned message, while usage.iterations provides the per-attempt record. Tokens from different models are not combined into one top-level count. Every attempt that runs also counts against the rate limit of its own model.
Practical handling for developers
A refusal-aware implementation should:
**Branch on
stop_reason.** Treatstop_reason == "refusal"as the primary detection rule. Do not infer a refusal solely from missing text or an HTTP error.Record the category when available. Store
stop_details.categoryfor billing and safety monitoring, but allow it to be null.Discard incomplete streamed output. A mid-stream refusal means the received content is not a complete answer.
Retry on a different model. Sending the same request back to the model that refused it will usually produce another refusal.
Budget every attempt. Account for the original refusal, fallback responses, output tokens, cache behavior, and each model’s rate limits.
Configure fallback on every request path. Background workers, retry handlers, tool-driven sub-agents, and error-recovery branches can otherwise bypass the fallback configuration.
Monitor refusals separately from errors. Because a refusal can be HTTP 200, a dashboard based only on 5xx responses will miss it. Track refusal events and fallback-served responses independently.
Keep later turns on the accepting model when appropriate. The documentation recommends continuing a conversation on the fallback model rather than repeatedly asking a model that already refused.
The practical effect is not simply that some blocked prompts may incur a charge. A refusal can be a successful API response, consume rate-limit capacity, and lead to additional model attempts when fallback is enabled. Developers need to inspect the refusal fields and per-attempt usage to understand what happened and how the request was handled.





0 comments
No approved comments yet. You can start the conversation.
Leave a comment