Every official Claude SDK — Python, TypeScript, Java, Go, Ruby, C#, PHP — is a wrapper around the same HTTP API. Nothing stops you from calling that API directly with curl, fetch, or your language's standard HTTP client. The question is not whether raw HTTP works (it does) but where the break-even point sits between "one less dependency" and "reimplementing plumbing the SDK already handles."
The raw request, in three headers
A direct request to the first-party Claude API needs three things beyond the JSON body: your API key in the x-api-key header, an anthropic-version: 2023-06-01 header, and content-type: application/json. That is the entire contract for a basic Messages call. If you are writing a smoke test, a health check, or a one-off script in an environment where adding a dependency is annoying, raw HTTP is a perfectly reasonable choice.
For comparison, the SDK equivalent is equally short — and it picks the key up from the ANTHROPIC_API_KEY environment variable automatically:
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY from the environment
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
What the SDK is actually doing for you
The value of the library shows up in everything around that call. Credential resolution is the big one: the SDKs implement a documented precedence chain — explicit constructor argument, then ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN, then named profiles, then Workload Identity Federation (WIF) settings. With WIF, the SDK exchanges your identity provider's token for a short-lived Claude access token and refreshes it automatically before expiry. Reproduce that by hand and you own token caching, refresh timing, and failure handling forever.
Streaming is the other one. Claude streams responses as server-sent events (SSE); the SDKs parse the event stream and reassemble message objects for you. Doing this in raw HTTP is possible but fiddly, especially once tool use enters the picture.
Raw HTTP gets harder on third-party platforms
On the first-party API, raw HTTP is one header away. On the 3P platforms, it is not.
Claude Platform on AWS authenticates with AWS SigV4 request signing. In curl that means --aws-sigv4 "aws:amz:<region>:aws-external-anthropic", an anthropic-workspace-id header on every request, and an x-amz-security-token header when you are on temporary credentials. Get the region in the signature and the endpoint URL out of sync and you receive a generic signature-rejection error, not a helpful diagnostic. The platform SDK clients (such as AnthropicAWS() in Python) handle the signing, the region-based base URL, and the workspace header for you — which is precisely why they exist.
Bedrock, Vertex AI, and Foundry each add their own cloud authentication (AWS credentials, Google Application Default Credentials, Foundry resource keys). Raw HTTP against those surfaces means hand-rolling cloud auth as well.
One more wrinkle: if you are using an OAuth token from the ant CLI rather than an API key, raw requests use Authorization: Bearer ... instead of x-api-key, plus an anthropic-beta: oauth-2025-04-20 header.
jq, and paginates list endpoints automatically, so you rarely need bare curl at all.Legitimate reasons to skip the SDK
There are real cases where raw HTTP wins: verifying that networking, credentials, and headers work before blaming your code; environments where you cannot add dependencies (locked-down build images, exotic runtimes); gateway or proxy layers that forward requests and should not re-serialize them; and quick reproduction cases to attach to a support ticket. For everything else — retries, streaming, credential refresh, typed responses — the official libraries earn their place in your dependency file.
Where to go next
If you are staying with the SDK, start at the Python quickstart or see the full list of SDK languages. For terminal-first workflows, read the ant CLI introduction, and the homepage quickstart covers first calls on each platform.