← Blog

Claude API Circuit Breaker Pattern Design Guide

2026-10-22 · 5 min read · SubToAPI Team

What a circuit breaker does for Claude API calls

A circuit breaker protects your application from a failing or slow Claude API dependency by stopping requests before they pile up and take down the rest of your system. Instead of letting every request wait for a timeout against an unresponsive endpoint, the circuit "opens" after a threshold of failures, returns fast errors (or falls back to a cached/alternate response), and periodically tests whether the API has recovered.

This is different from retry logic. Retries help with transient, isolated failures. A circuit breaker is for sustained outages or degraded performance — when Anthropic's API, your network path, or a rate-limit wall is causing failures at scale. Without one, a single upstream incident can exhaust your thread pool, queue, or connection pool as thousands of requests pile up waiting on dead connections.

The three states

A circuit breaker design has three states, and the whole pattern is really a state machine:

Minimal implementation

class CircuitBreaker {
  constructor({ failureThreshold = 5, resetTimeoutMs = 30000, halfOpenTrials = 2 } = {}) {
    this.failureThreshold = failureThreshold;
    this.resetTimeoutMs = resetTimeoutMs;
    this.halfOpenTrials = halfOpenTrials;
    this.state = "closed";
    this.failureCount = 0;
    this.nextAttempt = 0;
    this.trialCount = 0;
  }

  canRequest() {
    if (this.state === "open") {
      if (Date.now() >= this.nextAttempt) {
        this.state = "half-open";
        this.trialCount = 0;
        return true;
      }
      return false;
    }
    return true;
  }

  onSuccess() {
    this.failureCount = 0;
    if (this.state === "half-open") {
      this.trialCount++;
      if (this.trialCount >= this.halfOpenTrials) {
        this.state = "closed";
      }
    }
  }

  onFailure() {
    this.failureCount++;
    if (this.state === "half-open") {
      this.state = "open";
      this.nextAttempt = Date.now() + this.resetTimeoutMs;
      return;
    }
    if (this.failureCount >= this.failureThreshold) {
      this.state = "open";
      this.nextAttempt = Date.now() + this.resetTimeoutMs;
    }
  }
}

Wrap your actual API call:

const breaker = new CircuitBreaker({ failureThreshold: 5, resetTimeoutMs: 30000 });

async function callClaude(payload) {
  if (!breaker.canRequest()) {
    throw new Error("circuit_open");
  }
  try {
    const res = await fetch("https://api.subtoapi.app/v1/messages", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(payload),
    });
    if (!res.ok) throw new Error(`http_${res.status}`);
    breaker.onSuccess();
    return await res.json();
  } catch (err) {
    breaker.onFailure();
    throw err;
  }
}

What should count as a failure

Not every non-200 response should trip the breaker the same way. Separate your error classes:

If you're calling the Claude API directly, 529 ("overloaded") responses are a strong signal to trip the breaker, since they explicitly mean the upstream is shedding load — hammering it harder makes things worse for everyone.

Combine with retries and backoff, not instead of them

Circuit breakers and retry-with-backoff solve different problems and should be layered: retries handle a single flaky request, while the breaker handles sustained degradation across many requests. A sane order of operations per request is: try the call, retry a couple of times with exponential backoff on transient errors, then report the final failure to the breaker. If the breaker is already open, skip retries entirely and fail fast — retrying into an open circuit defeats its purpose.

What to do when the circuit is open

Fast failure is only useful if your caller does something sensible with it:

What you should avoid is silently queuing unlimited requests behind an open circuit, since that just delays the pileup instead of preventing it.

Per-dependency granularity

If you call Claude for multiple purposes (chat, summarization, extraction) through different endpoints or with different timeout profiles, use a separate breaker instance per logical dependency. A slow streaming endpoint and a fast classification endpoint have different latency baselines; one shared breaker will either trip too eagerly for the fast path or too late for the slow one.

Where SubToAPI fits

If you're running Claude through SubToAPI, you still need application-level circuit breakers — this pattern protects your service regardless of upstream, and no API gateway removes the need for it. What SubToAPI does give you is consistent sub_live_... application keys, usage metadata per request, and streaming support (see /docs/streaming) so you can distinguish real upstream failures from your own quota or auth issues when deciding what should trip the breaker. Check /docs/messages for response shapes and /docs/quickstart to get a key running in minutes, or see /pricing for plan details.

Questions

Does a circuit breaker replace retry logic? No. Retries fix isolated, transient failures on a single request. Circuit breakers stop you from hammering an API that's already degraded across many requests. Use both, in that order: retry a few times, then let sustained failures trip the breaker.

Should 429 rate-limit responses trip the circuit breaker? Generally no, not the same breaker used for outages. A 429 means you're exceeding your own quota, not that the API is down. Handle it with local throttling or a separate rate-limit-specific breaker so you don't falsely mark a healthy API as unhealthy.

How long should the open state last before testing recovery? Start with 20–30 seconds for interactive APIs and increase with backoff if the half-open trial fails again. Too short causes flapping; too long delays recovery once the dependency is healthy again.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →