Because Claude Platform on AWS, Amazon Bedrock, Google Vertex AI, and Microsoft Foundry serve the same models through different front doors, a request that fails on one can often succeed on another. This pattern adds a secondary (and optionally tertiary) platform behind a routing layer. It buys resilience at a real cost — duplicate setup, duplicate governance review, and some per-request compromises — so treat it as a pattern for genuinely availability-critical paths, not a default.
What "degraded" looks like on the wire
On the Claude API surface, the retryable signals are documented precisely: 429 rate_limit_error (with a retry-after header saying how long to wait), 500 api_error, 529 overloaded_error (API-wide high traffic), and connection errors. The official SDKs already retry these automatically with exponential backoff (twice by default, honoring retry-after) — failover is what happens after in-platform retries are exhausted. Non-retryable errors (400, 401, 403, 404, 413) must not trigger failover: a malformed request will fail everywhere, and hammering a second platform with it just doubles the noise. Note also that mid-stream errors can arrive as SSE error events after a successful 200, so streaming pipelines need failover handling inside the stream reader, not just around the initial call.
The routing layer
Recommended practice is a thin wrapper owning an ordered list of (client, model ID) pairs, a circuit breaker per platform (after N consecutive retryable failures, skip that platform for a cooldown window), and structured logging of every failover event. The Python SDK makes the multi-client part small:
from anthropic import (AnthropicBedrockMantle, AnthropicVertex,
RateLimitError, InternalServerError, APIConnectionError)
ROUTES = [
(AnthropicBedrockMantle(aws_region="us-east-1"), "anthropic.claude-sonnet-5"),
(AnthropicVertex(project_id="acme-prod", region="global"), "claude-sonnet-5"),
]
def create(messages, **kw):
for client, model in ROUTES:
try:
return client.messages.create(model=model, max_tokens=1024,
messages=messages, **kw)
except (RateLimitError, InternalServerError, APIConnectionError):
continue
raise RuntimeError("all Claude platforms degraded")
Model IDs differ per platform and must be mapped, never shared: Bedrock prefixes IDs with anthropic. (current-generation IDs are dateless, e.g. anthropic.claude-sonnet-5), Google Cloud uses bare IDs with an @date form for older models (claude-haiku-4-5@20251001), and Claude Platform on AWS (AnthropicAWS(), SigV4 auth) and Foundry (AnthropicFoundry()) use bare first-party IDs.
Design for the lowest common denominator
Failover only works for requests every platform in your route list can serve. Core Messages, streaming, and tool use are available on all four platforms, so plain chat and tool loops fail over cleanly. Beyond that, gaps bite: code execution, web fetch, the Files API, and Anthropic's Message Batches API are not available on Bedrock or Vertex AI, and web search is absent on Bedrock with only the basic variant on Vertex. A request built around a Files API file_id cannot fail over to Bedrock at all — it would need re-encoding as base64, within Bedrock's smaller media limits. Practical rule: partition traffic into a "portable" tier restricted to universally available features, which gets failover, and a "platform-bound" tier that instead gets deeper in-platform retries and queuing.
What failover costs you
| Effect | Why |
|---|---|
| Cold prompt caches | Prompt caches don't follow you across platforms, and cache isolation is per organization or workspace — failed-over requests pay full input price until the cache re-warms |
| Separate quotas | Rate limits are per platform account; capacity planning must cover the secondary absorbing a surge |
| Governance x2 | Data now flows through two providers' boundaries — both need security and compliance sign-off |
Where to go next
See retries and fallbacks for single-platform resilience first, then multi-cloud strategy and model IDs across platforms for the mapping details.