services/api/ — Anthropic API Client Service

HTTP client for the Anthropic Claude API

Handles streaming responses, retry logic with exponential backoff, error classification, rate-limit awareness, token counting, and multi-provider support (Bedrock, Vertex AI, Azure Foundry). The core communication layer between Claude Code and the model.

21
Files
~10.5 k
Lines
4
Providers
10
Retry Strategies

📚 What is this & why does it exist?

The API service is Claude Code's telephone line to the Anthropic model. Every time the user asks Claude a question, this module packages the request, dials out to the right provider (Anthropic direct, AWS Bedrock, Google Vertex AI, or Azure Foundry), streams back the response token-by-token, and gracefully handles dropped connections, overloaded servers, and expired credentials.

💡 Real-world analogy

Think of a seasoned travel agent who can book flights with four different airlines (Anthropic, Bedrock, Vertex, Foundry). When one airline's phone line is busy (rate limit), the agent waits and redials with exponential backoff. If the line keeps dropping (529 overloaded), the agent tries a partner airline (model fallback). The agent also keeps detailed logs of every call (prompt dumping) and caches frequent-flyer data (bootstrap) so repeat trips are faster.

Contents

Architecture

flowchart TD
  U["User Query"] --> WR["withRetry Generator"]
  WR --> CL["getAnthropicClient()"]
  CL -->|First-party| AN["Anthropic SDK"]
  CL -->|Bedrock| BK["AnthropicBedrock SDK"]
  CL -->|Vertex| VX["AnthropicVertex SDK"]
  CL -->|Foundry| FD["AnthropicFoundry SDK"]
  AN --> API["Anthropic API"]
  BK --> API
  VX --> API
  FD --> API
  API -->|Stream| SR["Streaming Response"]
  SR --> LOG["logging.ts"]
  SR --> ERR["errors.ts / errorUtils.ts"]
  WR -->|429/529| RB["Retry with Backoff"]
  WR -->|Fallback| FM["FallbackTriggeredError"]
  RB --> CL
  FM --> CL
  LOG --> AN2["Analytics Events"]

Request Lifecycle

sequenceDiagram
  participant App as Claude Code
  participant WR as withRetry
  participant Client as AnthropicClient
  participant API as Anthropic API
  participant Err as Error Handler

  App->>WR: operation(client, retryContext)
  WR->>Client: getAnthropicClient(provider)
  Client->>API: POST /v1/messages (stream)
  alt Success
    API-->>WR: SSE token stream
    WR-->>App: yield tokens
  else 429 Rate Limited
    API-->>Err: APIError(429)
    Err->>WR: retryAfterMs
    WR->>WR: sleep(backoff)
    WR->>Client: retry request
  else 529 Overloaded
    API-->>Err: APIError(529)
    Err->>WR: consecutive529Errors++
    alt Under MAX_529_RETRIES
      WR->>Client: retry with backoff
    else Over threshold
      WR->>App: FallbackTriggeredError
    end
  end

Key Classes & Functions

Class / FunctionFilePurpose
getAnthropicClient()client.tsFactory creating configured SDK client for the active provider (Anthropic, Bedrock, Vertex, Foundry)
withRetry()withRetry.tsAsync generator implementing resilient retry with exponential backoff, fast-mode fallback, and model switching
getAssistantMessageFromError()errors.tsConverts API errors into displayable AssistantMessage objects with user-friendly text
classifyAPIError()errors.tsCategorizes errors into auth, rate_limit, overloaded, invalid_request, etc.
categorizeRetryableAPIError()errors.tsReturns 'retryable' | 'non_retryable' | 'fallback' decision for the retry loop
extractConnectionErrorDetails()errorUtils.tsWalks error cause chain (max depth 5) to extract SSL/TLS root cause
formatAPIError()errorUtils.tsProduces user-friendly error messages, strips CloudFlare HTML
downloadSessionFiles()filesApi.tsDownloads file attachments with exponential backoff retry (max 3 retries)
uploadSessionFiles()filesApi.tsUploads files to Anthropic Files API with 500 MB limit and path validation
createDumpPromptsFetch()dumpPrompts.tsWraps fetch to capture request/response pairs to JSONL for debugging
logAPIQuery()logging.tsLogs analytics events including gateway detection (LiteLLM, Helicone, Portkey, etc.)
fetchBootstrapData()bootstrap.tsFetches and caches client configuration and model options on startup
fetchUtilization()usage.tsRetrieves rate-limit utilization (5-hour, 7-day windows) with 5 s timeout
CannotRetryErrorwithRetry.tsSignals that retry budget is exhausted
FallbackTriggeredErrorwithRetry.tsSignals a model fallback (e.g., Opus → Sonnet on repeated 529s)

Data Structures

TypeNameDescription
typeRetryContextMutable context carrying maxTokensOverride, model, thinkingConfig, fastMode
typeRetryOptionsConfig for withRetry: maxRetries, model, fallbackModel, signal, querySource
constEMPTY_USAGEReadonly zero-initialized usage object with all token counters at 0
typeGlobalCacheStrategy'tool_based' | 'system_prompt' | 'none' — prompt caching approach
typeConnectionErrorDetails{ code, message, isSSLError } — extracted from error cause chain
typeFilesApiConfig{ oauthToken, baseUrl?, sessionId } — config for Files API operations
typeDownloadResult{ fileId, path, success, error?, bytesWritten? }
typeUploadResult{ fileId, path, size, created_at, success, error? }
typePreviousStatePrompt cache fingerprint: hashes of system, tools, model, betas, effort for break detection
typeAdminRequestType'limit_increase' | 'seat_upgrade'
typeUtilizationRate-limit windows: five_hour, seven_day, seven_day_opus, extra_usage

Interactions

DirectionPartnerInterfaceDescription
→Anthropic APIPOST /v1/messagesPrimary model inference endpoint (streaming SSE)
→Anthropic Files APIPOST /v1/filesUpload/download file attachments (500 MB max)
→AWS BedrockAnthropicBedrock SDKInference via AWS IAM credentials
→Google Vertex AIAnthropicVertex SDKInference via GCP project & region
→Azure FoundryAnthropicFoundry SDKInference via Azure resource & API key
←Core Agent LoopwithRetry()Agent loop consumes the async generator for streamed tokens
→AnalyticslogAPIQuery() / logAPIError()Emits telemetry events to Growthbook analytics & OTel
→OAuth ModulewithOAuth401Retry()Refreshes expired OAuth tokens on 401 responses

Configuration

Knob / ParameterDefaultDescription
ANTHROPIC_API_KEY—Direct API key for first-party provider
API_TIMEOUT_MS600000Request timeout in milliseconds (10 min)
DEFAULT_MAX_RETRIES10Maximum retry attempts in withRetry loop
MAX_529_RETRIES3Consecutive 529 errors before model fallback
BASE_DELAY_MS500Base exponential backoff delay
FLOOR_OUTPUT_TOKENS3000Minimum output token budget when adjusting for context overflow
MAX_FILE_SIZE_BYTES524288000Maximum upload file size (500 MB)
PERSISTENT_MAX_BACKOFF_MS300000Max backoff for unattended sessions (5 min)
HEARTBEAT_INTERVAL_MS30000Keep-alive interval for long-running waits
CLAUDE_CODE_USE_BEDROCK—Enable AWS Bedrock provider
CLAUDE_CODE_USE_VERTEX—Enable Google Vertex AI provider
CLAUDE_CODE_USE_FOUNDRY—Enable Azure Foundry provider
FALLBACK_FOR_ALL_PRIMARY_MODELS—Force fallback for all models on 529 (not just Opus)

Directory Map

Source tree for api
api/
├── adminRequests.ts            ← Seat upgrade & limit increase requests
├── bootstrap.ts                ← Startup config fetch with disk caching
├── client.ts                   ← Multi-provider SDK client factory
├── claude.ts                   ← Core Claude model interaction
├── dumpPrompts.ts              ← Debug prompt/response capture to JSONL
├── emptyUsage.ts               ← Zero-initialized usage singleton
├── errorUtils.ts               ← SSL/TLS error detection & formatting
├── errors.ts                   ← Error classification & response parsing
├── filesApi.ts                 ← Files API upload/download with retry
├── firstTokenDate.ts           ← First usage date fetch & cache
├── grove.ts                    ← Terms/privacy notice management
├── logging.ts                  ← Analytics & gateway detection
├── metricsOptOut.ts            ← Org metrics opt-out (2-tier cache)
├── overageCreditGrant.ts       ← Overage credit eligibility cache
├── promptCacheBreakDetection.ts← Prompt cache fingerprint & break alerts
├── referral.ts                 ← Guest pass referral program
├── sessionIngress.ts           ← Session transcript persistence (409 recovery)
├── ultrareviewQuota.ts         ← Ultrareview quota peek
├── usage.ts                    ← Rate-limit utilization fetch
└── withRetry.ts                ← Resilient retry generator with fallback

Deep-Dive Highlights

Core mechanisms from this sub-module have been extracted into dedicated focus areas for in-depth exploration. Each topic covers one algorithm, protocol, or subsystem with diagrams and code walks.

Deep-Dive Pages

Retry & Fallback Engine Focus

Exponential backoff, fast-mode cooldown, model fallback on 529 overload, persistent retry for unattended sessions.

withRetry.ts deep dive

Multi-Provider Client Focus

Dynamic SDK selection for Anthropic, Bedrock, Vertex AI, and Foundry with lazy imports and credential injection.

client.ts deep dive

Error Classification Pipeline Focus

SSL cause-chain walking, HTML sanitization, prompt-too-long token parsing, and retryability categorization.

errors.ts + errorUtils.ts deep dive

Gateway Detection

The logging module fingerprints AI gateway proxies from response headers to track infrastructure topology:

GatewayDetection MethodHeader Prefix
LiteLLMResponse headersx-litellm-*
HeliconeResponse headershelicone-*
PortkeyResponse headersx-portkey-*
Cloudflare AI GatewayResponse headerscf-aig-*
KongResponse headersx-kong-*
BrainTrustResponse headersx-bt-*
DatabricksResponse headersx-databricks-*

Code Examples

Example 1: Creating a Multi-Provider Client

import { getAnthropicClient } from './client';

// Factory detects provider from environment variables
const client = await getAnthropicClient({
  model: 'claude-sonnet-4-20250514',
  // Injects CLIENT_REQUEST_ID_HEADER for timeout tracing
  // Selects AnthropicBedrock if CLAUDE_CODE_USE_BEDROCK is set
  // Selects AnthropicVertex if CLAUDE_CODE_USE_VERTEX is set
  // Falls back to first-party Anthropic SDK with API key or OAuth
});

// Custom headers injected from ANTHROPIC_CUSTOM_HEADERS env var
// Container / remote session IDs added automatically
// Logger redirected to stderr with "[anthropic sdk]" prefix

Example 2: Resilient Retry with Model Fallback

import { withRetry, FallbackTriggeredError } from './withRetry';

const retryOptions: RetryOptions = {
  maxRetries: 10,
  model: 'claude-opus-4-20250514',
  fallbackModel: 'claude-sonnet-4-20250514',
  thinkingConfig: { enabled: true },
  signal: abortController.signal,
  querySource: 'repl_main_thread',
};

try {
  // withRetry is an async generator — yields error messages, returns final result
  const generator = withRetry(getAnthropicClient, sendRequest, retryOptions);
  for await (const errorMsg of generator) {
    // Display retry status to user (e.g., "Retrying in 2s...")
    displaySystemMessage(errorMsg);
  }
  const result = generator.return; // Final successful response
} catch (err) {
  if (err instanceof FallbackTriggeredError) {
    // Switch to fallback model and re-run
    retryOptions.model = err.fallbackModel;
  }
}

Example 3: File Upload with Retry

import { uploadSessionFiles, parseFileSpecs } from './filesApi';

const files = parseFileSpecs(['file_abc123:src/index.ts']);
const config: FilesApiConfig = {
  oauthToken: accessToken,
  sessionId: currentSessionId,
  baseUrl: 'https://api.anthropic.com',
};

// Uploads with exponential backoff (BASE_DELAY_MS=500, MAX_RETRIES=3)
// Path validation prevents directory traversal attacks
// MAX_FILE_SIZE_BYTES = 500MB per file
const results = await uploadSessionFiles(files, config);
for (const r of results) {
  if (r.success) console.log(`Uploaded ${r.path} → ${r.fileId}`);
  else console.error(`Failed ${r.path}: ${r.error}`);
}