You are already sending well-formed requests to the Messages API. We accept exactly those requests. The migration is two lines, and one of them is your credential.
The diff
client = Anthropic(
- base_url="https://api.anthropic.com",
- api_key=os.environ["ANTHROPIC_API_KEY"],
+ base_url="https://api.lowcostllm.com",
+ api_key=os.environ["LOWCOSTLLM_API_KEY"],
)What stays the same
Everything. We do not wrap, reshape, or reinterpret the payload — we forward it and return what comes back.
- The
anthropic-versionheader, and every version you already use. - Request fields:
system,tools,tool_choice,stop_sequences,temperature,top_p,thinking,metadata. - Server-sent events, including
content_block_deltaordering and the finalmessage_stop. - Cache control blocks. Prompt caching works and the savings compound with ours.
- Error shapes: an upstream
invalid_request_errorreaches you as aninvalid_request_error.
Model names
Model strings are identical. If your code says claude-sonnet-4-5 today, it says claude-sonnet-4-5 after the migration, and it reaches the same model.
One real difference: we only route models listed on our pricing page. A model we do not carry returns 404 model_not_available rather than being silently swapped for something close. That is deliberate — a silent substitution is the exact failure mode you are right to be worried about.
Rolling back
Change the two lines back. There is no export step, no data to retrieve, and no contract to exit — nothing about your integration is specific to us. Keeping migration cheap in both directions is the point.