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.
📚 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 / Function | File | Purpose |
|---|---|---|
getAnthropicClient() | client.ts | Factory creating configured SDK client for the active provider (Anthropic, Bedrock, Vertex, Foundry) |
withRetry() | withRetry.ts | Async generator implementing resilient retry with exponential backoff, fast-mode fallback, and model switching |
getAssistantMessageFromError() | errors.ts | Converts API errors into displayable AssistantMessage objects with user-friendly text |
classifyAPIError() | errors.ts | Categorizes errors into auth, rate_limit, overloaded, invalid_request, etc. |
categorizeRetryableAPIError() | errors.ts | Returns 'retryable' | 'non_retryable' | 'fallback' decision for the retry loop |
extractConnectionErrorDetails() | errorUtils.ts | Walks error cause chain (max depth 5) to extract SSL/TLS root cause |
formatAPIError() | errorUtils.ts | Produces user-friendly error messages, strips CloudFlare HTML |
downloadSessionFiles() | filesApi.ts | Downloads file attachments with exponential backoff retry (max 3 retries) |
uploadSessionFiles() | filesApi.ts | Uploads files to Anthropic Files API with 500 MB limit and path validation |
createDumpPromptsFetch() | dumpPrompts.ts | Wraps fetch to capture request/response pairs to JSONL for debugging |
logAPIQuery() | logging.ts | Logs analytics events including gateway detection (LiteLLM, Helicone, Portkey, etc.) |
fetchBootstrapData() | bootstrap.ts | Fetches and caches client configuration and model options on startup |
fetchUtilization() | usage.ts | Retrieves rate-limit utilization (5-hour, 7-day windows) with 5 s timeout |
CannotRetryError | withRetry.ts | Signals that retry budget is exhausted |
FallbackTriggeredError | withRetry.ts | Signals a model fallback (e.g., Opus → Sonnet on repeated 529s) |
Data Structures
| Type | Name | Description |
|---|---|---|
| type | RetryContext | Mutable context carrying maxTokensOverride, model, thinkingConfig, fastMode |
| type | RetryOptions | Config for withRetry: maxRetries, model, fallbackModel, signal, querySource |
| const | EMPTY_USAGE | Readonly zero-initialized usage object with all token counters at 0 |
| type | GlobalCacheStrategy | 'tool_based' | 'system_prompt' | 'none' — prompt caching approach |
| type | ConnectionErrorDetails | { code, message, isSSLError } — extracted from error cause chain |
| type | FilesApiConfig | { oauthToken, baseUrl?, sessionId } — config for Files API operations |
| type | DownloadResult | { fileId, path, success, error?, bytesWritten? } |
| type | UploadResult | { fileId, path, size, created_at, success, error? } |
| type | PreviousState | Prompt cache fingerprint: hashes of system, tools, model, betas, effort for break detection |
| type | AdminRequestType | 'limit_increase' | 'seat_upgrade' |
| type | Utilization | Rate-limit windows: five_hour, seven_day, seven_day_opus, extra_usage |
Interactions
| Direction | Partner | Interface | Description |
|---|---|---|---|
| → | Anthropic API | POST /v1/messages | Primary model inference endpoint (streaming SSE) |
| → | Anthropic Files API | POST /v1/files | Upload/download file attachments (500 MB max) |
| → | AWS Bedrock | AnthropicBedrock SDK | Inference via AWS IAM credentials |
| → | Google Vertex AI | AnthropicVertex SDK | Inference via GCP project & region |
| → | Azure Foundry | AnthropicFoundry SDK | Inference via Azure resource & API key |
| ← | Core Agent Loop | withRetry() | Agent loop consumes the async generator for streamed tokens |
| → | Analytics | logAPIQuery() / logAPIError() | Emits telemetry events to Growthbook analytics & OTel |
| → | OAuth Module | withOAuth401Retry() | Refreshes expired OAuth tokens on 401 responses |
Configuration
| Knob / Parameter | Default | Description |
|---|---|---|
ANTHROPIC_API_KEY | — | Direct API key for first-party provider |
API_TIMEOUT_MS | 600000 | Request timeout in milliseconds (10 min) |
DEFAULT_MAX_RETRIES | 10 | Maximum retry attempts in withRetry loop |
MAX_529_RETRIES | 3 | Consecutive 529 errors before model fallback |
BASE_DELAY_MS | 500 | Base exponential backoff delay |
FLOOR_OUTPUT_TOKENS | 3000 | Minimum output token budget when adjusting for context overflow |
MAX_FILE_SIZE_BYTES | 524288000 | Maximum upload file size (500 MB) |
PERSISTENT_MAX_BACKOFF_MS | 300000 | Max backoff for unattended sessions (5 min) |
HEARTBEAT_INTERVAL_MS | 30000 | Keep-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.
Multi-Provider Client Focus
Dynamic SDK selection for Anthropic, Bedrock, Vertex AI, and Foundry with lazy imports and credential injection.
Error Classification Pipeline Focus
SSL cause-chain walking, HTML sanitization, prompt-too-long token parsing, and retryability categorization.
Gateway Detection
The logging module fingerprints AI gateway proxies from response headers to track infrastructure topology:
| Gateway | Detection Method | Header Prefix |
|---|---|---|
| LiteLLM | Response headers | x-litellm-* |
| Helicone | Response headers | helicone-* |
| Portkey | Response headers | x-portkey-* |
| Cloudflare AI Gateway | Response headers | cf-aig-* |
| Kong | Response headers | x-kong-* |
| BrainTrust | Response headers | x-bt-* |
| Databricks | Response headers | x-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}`);
}