anthropic-version: 2023-06-01 header, the wrong auth shape (x-api-key not Authorization: Bearer), or a deprecated model ID — current valid IDs in 2026 are claude-opus-4-7, claude-sonnet-4-6, and claude-haiku-4-5-20251001. Run the curl in the diagnostic card below from the same env your app uses; if curl works and your code doesn't, the bug is in your client.9 out of 10 "Claude API is broken" tickets I see in San Diego are actually one of five specific issues. Here's how to diagnose and fix each one in under 15 minutes.
x-api-key header is missing, malformed, or using a revoked key. Regenerate at console.anthropic.com/settings/keys, pass it as x-api-key: sk-ant-... (NOT as a Bearer token like OpenAI), and set anthropic-version: 2023-06-01. Missing the version header is the #1 silent failure.claude-sonnet-4-5, claude-opus-4-1, or claude-haiku-4-5 (not claude-3-sonnet-20240229 — deprecated). Always check the current model list in Anthropic docs before shipping. Also: messages array must alternate user/assistant roles and start with user.retry-after header, switch to streaming to reduce token pressure, batch requests, or request a tier upgrade. If you see overloaded_error, that's Anthropic's side — retry with jitter.stream: true with SSE parsing and handle content_block_delta events, not the full OpenAI-style choices shape.Before you rewrite anything, run this curl from the same environment your app runs in. If this works and your code doesn't, it's your code. If this fails too, it's auth, network, or account-level.
Paste this into your terminal. Replace the key. If you get a 200 response back, your key and account are fine — the bug is in your integration layer.
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model":"claude-sonnet-4-5",
"max_tokens":64,
"messages":[{"role":"user","content":"ping"}]
}'
Wrong: Authorization: Bearer ... — that's OpenAI.
Wrong: Forgetting max_tokens — it's required, not optional.
Wrong: System prompt inside messages array — use the top-level system parameter instead.
Wrong: Parsing response.choices[0] — Claude returns response.content[0].text.
Wrong: Not handling stop_reason: "max_tokens" — your response got truncated.
Streaming failures are the single most common "it works in dev, breaks in prod" issue. Claude's streaming format uses Server-Sent Events with event types like message_start, content_block_delta, and message_stop. If you're piping through a proxy (Vercel, Cloudflare, nginx), make sure you're not buffering the response — set X-Accel-Buffering: no on nginx, disable edge caching on Vercel, and use new Response(stream) with a ReadableStream, not res.json().
Claude's tool use returns stop_reason: "tool_use" and expects you to send back a tool_result message with the matching tool_use_id. If your loop isn't feeding results back correctly, Claude will either hang, hallucinate, or return a schema error. The fix: log every message in the array and confirm IDs match one-to-one before the next call.
I debug Claude API integrations for a living — text me your error message and I'll tell you what's wrong in 5 minutes. $100/hr, no retainer, no BS.
Same operator, adjacent debug surfaces. If you got here from search, these are the next pages worth a 2-minute scan:
type values.Claude uses x-api-key: sk-ant-... — NOT Authorization: Bearer ... (that pattern is OpenAI). You also need anthropic-version: 2023-06-01 and content-type: application/json. Missing the version header returns a confusing 400, not a 401, which is why people chase the auth header for an hour before noticing.
As of May 2026 the live IDs are claude-opus-4-7, claude-sonnet-4-6, and claude-haiku-4-5-20251001. Older aliases like claude-3-sonnet-20240229 and claude-3-5-sonnet-latest are deprecated and will return 404 model_not_found. Always pin a dated ID in production; never rely on -latest for billing predictability.
Tier-1 Anthropic accounts cap at ~50 RPM, ~20K input tokens/min, and ~8K output tokens/min. A single 30K-token document submission will trip ITPM even with 1 request. Read anthropic-ratelimit-input-tokens-remaining and retry-after response headers, implement exponential backoff with jitter, and request tier-2 in console once you've spent >$5.
Three usual causes: (1) you're calling Claude directly from the browser and CORS silently drops the response — always proxy server-side; (2) your serverless function timed out (Vercel serverless = 10s default; switch to edge runtime or background workers); (3) a proxy (nginx, CloudFront) is buffering SSE — set X-Accel-Buffering: no and disable edge HTML caching on the route.
When Claude returns stop_reason: "tool_use", you must send back a user message containing a tool_result block whose tool_use_id matches the id Claude assigned. Mismatched or missing IDs = silent hang or schema_invalid. Log every message-array item and confirm one-to-one ID pairing before each follow-up call.
Don't see what you were looking for?
Text PJ a sentence about what you actually need — I'll build you a free custom shareable on the house. No email, no funnel, no SOW.
📲 Text PJ — free shareable