Get API key
APIs

Messages

Messages is Anthropic's API. llm.buzz serves it at POST https://api.llm.buzz/api/v1/messages for every text model, Claude or not, so the Anthropic SDK and the tools built on it work with the base URL alone.

On this page

Send a request

Point the Anthropic SDK at https://api.llm.buzz/api, without /v1: the SDK adds /v1/messages itself. max_tokens is required. Claude Code takes the same base URL: Authentication.

from anthropic import Anthropic
import os

client = Anthropic(
    base_url="https://api.llm.buzz/api",
    api_key=os.environ["LLM_BUZZ_KEY"],
)
message = client.messages.create(
    model="anthropic/claude-sonnet-5.5",
    max_tokens=2048,
    system="You answer in one short sentence.",
    messages=[{"role": "user", "content": "Why do HTTP clients retry with backoff?"}],
)
for block in message.content:
    if block.type == "text":
        print(block.text)

The loop reads the text blocks because a model that thinks puts a thinking block first. The fields llm.buzz adds to Anthropic's request, provider, gateway and session_id, go through extra_body in the SDKs, for example extra_body={"provider": {"data_collection": "deny"}}. The model name, and the models you can call, come from GET https://api.llm.buzz/api/v1/models (Choosing an API).

Request fields

A field Messages does not know, such as seed, is dropped, and the body's x-sgr-ignored-parameters names it, on message_start and on the final message. A field llm.buzz deliberately refuses is a 400 before any provider is called, in Anthropic's invalid_request_error.

FieldWhat llm.buzz does
modelThe model's name, author/slug. :floor tries the cheapest provider first and :nitro the fastest, and :exacto is accepted and ignored. :online is accepted and searches nothing, and x-sgr-ignored-parameters names it model:online. Models and routing.
max_tokensRequired. The most output tokens, thinking included. A provider that needs a larger minimum than your limit is skipped, and when none is left the request is refused.
systemThe system instruction: a string, or a list of text blocks.
messagesThe conversation, with the roles user and assistant. A block is text; image, from base64 or a URL; document, only for a model that reads PDFs, which no model on llm.buzz does today; tool_use; tool_result, whose content may hold text and images; or thinking and redacted_thinking, sent back unchanged. A model that cannot read an attachment refuses the request before any provider is called. Images in requests.
stop_sequencesSent to providers that take them. Where a provider has no stop field, llm.buzz cuts the answer at the first match itself and ends it with stop_reason: stop_sequence, the matched string in stop_sequence.
temperature, top_p, top_kSent to the provider. A model that takes none has the value dropped and named: Sampling settings a model does not take.
streamtrue streams the answer as server-sent events: Stream.
tools, tool_choiceSent, in Anthropic's shape. Some models cannot be forced to call a tool, and some providers take a tool schema only in part. Tools and structured output says what happens to each.
thinking, reasoningHow hard the model thinks, and whether you see it. Thinking.
output_configoutput_config.effort sets the reasoning effort, and every provider receives it in its own terms: Thinking. output_config.format asks for structured output. An output_config that holds more than effort, such as a format, is sent as you wrote it when every provider that may serve the request takes Anthropic's API. Otherwise llm.buzz drops the object and names output_config in x-sgr-ignored-parameters, and its effort still applies. Tools and structured output.
cache_controlAnthropic's prompt-cache marker, taken as the Messages API defines it, at every place it allows, the top level included. Passed on or translated, never added by llm.buzz. Prompt caching.
context_managementSent where the provider takes it, with the anthropic-beta token it needs, and dropped and named elsewhere.
inference_geo, speed, diagnosticsSent where the provider takes them, and dropped and named elsewhere.
session_idA name for the conversation, 1 to 256 characters, so its turns stay on the provider that holds its cache. An empty or whitespace-only value counts as absent; any other value that is not a string of 1 to 256 characters, such as one over 256 characters, is a 400 naming session_id. The x-session-id header names it too.
providerSteers one request: order, only, ignore, allow_fallbacks, sort, zdr and data_collection. Other keys, such as require_parameters, quantizations, max_price and sort.partition, are accepted and do not change which provider serves. The provider object.
gatewayllm.buzz's own per-request bounds, retry and routing.allow_fallbacks. Never sent to a provider. Retries and fallbacks per request.
plugins, web_search_optionsDropped whole and named: the whole plugins field is named plugins, not plugins.<id> as on Chat Completions and Responses. llm.buzz runs no web search, so nothing is searched.
models, route, transforms, usage, trace, debugDropped and named. A model's own providers fail over through its routing preset, in place of models. Usage counts and usage.cost come back whether or not you send usage.
seed, logit_bias, response_formatDropped and named. A Chat response_format is not applied on Messages: ask for structured output with output_config.format.
defer_loading on a toolKeeps the tool out of the prompt until a tool search finds it. Tool search.
Refused fields, including betas, container, fallbacks, fallback_credit_token, mcp_servers, service_tier, thread and user_profile_id400 invalid_request_error naming the field. Send the anthropic-beta header in place of betas.

Thinking

You can ask for thinking three ways, and llm.buzz translates each into the terms of the provider that serves the request.

  • thinking takes Anthropic's forms: adaptive, disabled, and enabled with a budget_tokens. Every Claude model llm.buzz lists today is in an adaptive-only family, and those families refuse a budget. A thinking object with budget_tokens answers 400 invalid_request_error naming thinking.budget_tokens, and a bare {"type": "enabled"} runs adaptive thinking, named thinking.type->adaptive in x-sgr-ignored-parameters. Use adaptive, and set the depth with output_config.effort.
  • A model from another provider gets thinking translated. A bare enabled or adaptive reasons at the model's default effort, and at medium when the model names none. A budget_tokens goes to providers that have a budget field, and is refused before any provider is called where none has one.
  • For a Claude model that takes a budget, which none on llm.buzz does today, a bare {"type": "enabled"} takes half of the output limit as its budget, and budget_tokens must be below max_tokens (at or above it is a 400 naming thinking.budget_tokens).
  • reasoning, OpenRouter's object, is accepted too: effort or max_tokens, plus enabled and exclude. Its fields must agree: an effort tier or a budget beside enabled: false, or effort: "none" beside enabled: true, is a 400 invalid_request_error whose message ends (param: reasoning.enabled).
  • output_config.effort is a reasoning control that every provider receives in its own terms. A route that cannot serve the effort refuses the request and names output_config.effort.

Thinking off is not always available. thinking: {"type": "disabled"} is refused before any provider is called for Claude Opus 5.5, Sonnet 5.5, Fable 5 and Fable 5.1, at every effort level. Leave thinking out, or use adaptive and choose an effort. Sonnet 5.5 takes {"type": "between_tools"} in place of disabled: it turns off up-front thinking, works with an output_config.effort of low, medium or high, and takes no other thinking field. reasoning.exclude: true keeps reasoning text out of the answer while the model still thinks and bills for it, but Anthropic's own signed thinking blocks stay, because replaying them depends on them.

A conversation can move between models. Claude's signed thinking blocks go back to Claude. Another provider gets the turn without them, and x-sgr-ignored-parameters names it messages.thinking->dropped(unsupported_by_provider).

A max_tokens under 1,024, with no thinking or effort setting of your own, on a model whose providers all reason by default and offer a none effort, is sent at effort none, named reasoning_effort->none(max_tokens_headroom). The reply is text, not thinking cut off at the ceiling. Reasoning has the rest.

Headers

You send:

HeaderUse
x-api-key or AuthorizationYour inference key. The Anthropic SDK sends x-api-key; Authorization: Bearer carries the same key. Authentication.
anthropic-versionThe SDK sends it, and llm.buzz accepts it.
anthropic-betaAnthropic's beta tokens. A token llm.buzz does not pass on is dropped, and x-sgr-ignored-parameters names it anthropic-beta.<token>; the request still serves.
x-session-idNames the conversation, as session_id does.
X-Title, HTTP-RefererName your app. App attribution.

Messages has no Idempotency-Key: see Retries. Every answer carries:

HeaderValue
x-request-idThe request's ID. Its generation ID is gen- and the same 32 hex digits: Cost of a finished request.
x-gateway-aliasThe model name that served.
x-gateway-canonical-modelThe exact model that produced the answer.
x-gateway-providerThe display name of the provider that answered, llm.buzz for llm.buzz's own provider. The message has no provider key, so read it here.
x-gateway-route-depth, x-gateway-route-reasonThe answering provider's position in the order tried, and why the route was chosen.

An answer that asks you to wait, a 429 and some 402 and 503 answers, carries Retry-After: wait that many seconds before you retry.

Count tokens

POST https://api.llm.buzz/api/v1/messages/count_tokens takes a Messages request body and answers how many input tokens it comes to. It needs no max_tokens; one is accepted and changes nothing. gateway is refused with a 400 invalid_request_error naming it.

curl https://api.llm.buzz/api/v1/messages/count_tokens \
  -H "x-api-key: $LLM_BUZZ_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5.5",
    "messages": [{"role": "user", "content": "Why do HTTP clients retry with backoff?"}]
  }'

The answer is {"input_tokens": N}. The count is llm.buzz's own estimate, because no provider counts tokens for it, and the body says so with input_tokens->estimated(gateway_tokenizer) in x-sgr-ignored-parameters. It is free, and a key whose spending limit is reached can still call it. It answers the same 404 as a Messages call for a model none of whose providers meets your data policy (Models and routing).

The answer

A non-streamed answer is Anthropic's message object. llm.buzz adds or fixes these parts:

  • content holds text blocks, tool_use blocks, and the model's reasoning as thinking blocks: Anthropic's own, signed, when Anthropic serves the request, and one unsigned block with the readable reasoning from other providers. A tool turn of some models also ends with a redacted_thinking block. Send every thinking block back unchanged with the assistant turn. reasoning.exclude withholds the unsigned one.
  • stop_reason is end_turn for a finished answer, tool_use when the model called tools, max_tokens when the output ceiling cut it, and stop_sequence when a stop sequence you sent matched, with the matched string in stop_sequence.
  • usage is described below.
  • x-sgr-ignored-parameters is a key in the body, not a header. It lists every field llm.buzz dropped and every value it changed, with the reason, such as temperature->dropped(unsupported_by_provider). It is on the final message and on message_start. Check it when a setting seems to have no effect.

Stream

Set stream: true and the answer arrives as Anthropic's events.

EventWhat it carries
message_start, then one pingThe message so far, with usage and x-sgr-ignored-parameters.
content_block_start, content_block_delta, content_block_stopThe content blocks: text, tool input, thinking.
message_deltaThe mapped stop_reason, the final usage and the cost.
message_stopThe end of a finished answer.
errorOne last event when a failure follows the first output, in place of message_stop. A failure before the stream opens is an ordinary HTTP error.

Closing a Messages stream stops the request, and the prompt and the output produced up to then are billed. While the model is silent, comment lines beginning with : keep the connection active; clients skip them. Streaming covers the event shapes of all three text APIs.

Usage and cost

FieldValue
input_tokensUncached input tokens only.
cache_creation_input_tokensInput tokens written to the provider's cache.
cache_read_input_tokensInput tokens read from it.
output_tokensOutput tokens.
cache_creationAnthropic's split of the writes, ephemeral_5m_input_tokens and ephemeral_1h_input_tokens, present when the split is known.
costThe request's total cost in US dollars at your rate. On the final message and on message_delta, never on message_start.

The four counts are always present, 0 when the provider reported none. The usage of message_start is a figure taken before the provider answers: Anthropic's own start counts when Anthropic serves the request, and llm.buzz's estimate of the prompt for any other provider, with output_tokens of 1 and cache counts of 0. message_delta carries the final counts, which replace it.

The Anthropic Python SDK does not copy cost into the message it assembles from a stream. Read it from the message_delta event, or from the answer of a request that does not stream:

stream = client.messages.create(
    model="anthropic/claude-sonnet-5.5",
    max_tokens=2048,
    messages=[{"role": "user", "content": "Why do HTTP clients retry with backoff?"}],
    stream=True,
)
for event in stream:
    if event.type == "message_delta":
        print(event.usage.model_extra.get("cost"))

message.usage.model_extra["cost"] is the same value on a non-streamed message. cost is a JSON number that includes any billed tool-search rounds, so it can exceed the counts times the listed prices. A failed attempt that another provider replaced adds 0. It is left out when any part of the cost is unknown; a model with no price reports 0. Usage and limits covers the rest, and Prompt caching the cache fields.

Retries

The Messages API defines no idempotency header, so llm.buzz applies none: a retry is a separate request that runs, and bills, again.

  • The Anthropic SDK also retries failed requests on its own, and each retry is a separate request. Set max_retries=0 when your application must bound the work of one operation.
  • A provider that fails or times out before output reaches you is replaced by the next one, and the failed attempt costs nothing; a provider's own 400 is not tried on another provider. Once output has reached you, the request stays on the provider that sent it.

Retries and idempotency explains the rest.

Errors

Errors come in Anthropic's body, {"type": "error", "error": {"type": "...", "message": "..."}}, and the message says what to do. A named field is appended to the message as (param: NAME), and a refusal by the model adds refusal_reason to error. error.type follows the status:

Statuserror.typeWhen
400invalid_request_errorYou sent a refused field, such as service_tier; a field is missing or fails validation, max_tokens included; thinking is off on a model that cannot turn it off; a budget_tokens on a model that takes none; a route cannot serve output_config.effort; or the prompt fits no provider's context window. The message names the field and says what to change.
401authentication_errorThe key is missing, unknown, expired, revoked or disabled.
402billing_errorYour balance is spent and the model is not free, or requests already running hold what it leaves.
403permission_errorThe key's spending limit is reached, or a management key called Messages.
404not_found_errorThe model is unknown, unavailable, has no provider that meets your data policy, or has no EU provider for an organization whose requests stay in the EU.
413request_too_largeThe body is over 64 MiB.
429rate_limit_errorA provider is throttling, a route is unavailable, or llm.buzz is overloaded. Wait Retry-After seconds, then retry.
503overloaded_errorEvery provider is down, or llm.buzz is restarting. Retry with backoff, and after Retry-After seconds when the answer carries it.
500, 502, 504api_errorllm.buzz or a provider failed, or the request ran past 600 seconds: retry with backoff. A 502 can also mean a provider's answer was over llm.buzz's response limit: ask for less output.

A stream that fails after output ends with an error event, described under Stream. Errors lists every code, including the key, balance, model and provider errors this API shares with the others.

Limits

  • max_tokens is required. A model's context window is context_length on its entry in GET https://api.llm.buzz/api/v1/models, and top_provider.max_completion_tokens is its output ceiling.
  • A request may run for 600 seconds. A stream still generating then ends with a timeout error and is billed for the prompt and the output you received. A request that is not streaming answers 504.
  • A request body over 64 MiB answers 413 request_too_large.
  • A request makes at most 8 provider calls, every fallback included; gateway.retry lowers the ceiling.

Usage and limits collects the limits of every API.

Docs