Other SDKs and OpenAI Compatibility Best Practices
A checklist for choosing between a native Go, Java, C#, PHP, or Ruby SDK and the OpenAI-compatible migration shortcut, and for using each well once you've picked.
How to Use This Checklist
- Work through section A first if you haven't picked an integration path yet; sections B-D assume you already have.
- Revisit this list whenever you add a new language to a polyglot backend, or when a compatibility-layer prototype is about to become a permanent fixture.
- Treat unchecked items as open questions to resolve before a Claude integration goes to production, not as optional polish.
A - Choosing an Integration Path
- Match the SDK to the service's language, not to personal preference. A Go service should use
anthropic-sdk-go; a Ruby app should use the Ruby SDK, even if the team's favorite language is elsewhere. - Use the OpenAI compatibility layer only as a bridge, not a destination. It's built for fast migration and side-by-side testing, not as the recommended long-term integration path.
- Check the parameter mapping reference before migrating a codebase that uses uncommon OpenAI parameters. Sampling penalties, multi-completion requests, and function-calling shapes are the most likely places to hit a gap.
- Plan the graduation path from the compatibility layer up front. Decide, before you start, what condition ("once we validate output quality," "once the prototype gets real traffic") triggers a move to the native SDK.
- Accept that a polyglot backend may use several official SDKs at once. There's no cross-SDK compatibility tax; a Go gateway and a Java service mesh can both call Claude natively without any coordination overhead between them.
B - Working With a Native SDK (Go, Java, C#, PHP, Ruby)
- Construct the client once and reuse it. Building a new client per request (especially in Go, Java, and C#) discards connection pooling and adds avoidable latency.
- Read the API key from environment configuration, not hardcoded strings. Every SDK's client constructor accepts an API key sourced from
ANTHROPIC_API_KEYby convention; keep it out of source control. - Set an explicit
max_tokensfor every request. None of the five SDKs supply a sensible implicit default; size it to your expected response length. - Match error handling to the language's own convention, not another language's habits. Check returned errors in Go, catch typed exceptions in Java and C#, and handle raised exceptions in PHP and Ruby, the way native code in that language normally would.
- Treat a
tool_usestop reason as "waiting," not "done." Run the requested tool, then send atool_resultback in a follow-up call before treating the exchange as complete.
C - Idiomatic Tool Use Across Languages
- Use each language's natural schema shape for tool definitions. Structs in Go, builders in Java, typed objects in C#, associative arrays in PHP, hashes in Ruby; don't fight the language's idioms to make five SDKs look identical.
- Remember the wire protocol is identical regardless of syntax. A tool schema built as a Go struct and one built as a Ruby hash produce the same JSON Schema on the wire; debug at the protocol level when something looks wrong, not just the language level.
- Match every
tool_resultto its originatingtool_useID. This requirement is identical across all five SDKs; a mismatched or missing ID produces a rejected request no matter which language sent it. - Validate tool input shape on the receiving end, especially in PHP and Ruby. Neither language type-checks the request or response at compile time, so add your own validation before trusting fields like a tool's input.
D - Streaming in Java and C#
- Consume Java's stream through try-with-resources.
StreamResponseholds an open HTTP connection; failing to close it leaks connections under sustained load. - Keep C#'s streaming calls fully async end to end. Use
await foreachinside anasyncmethod chain; calling.Resultor.Wait()on a streaming call risks deadlocks and defeats the purpose ofIAsyncEnumerable. - Don't assume text deltas align with word or sentence boundaries. Buffer and render incrementally in both languages without assuming a delta ends cleanly.
- Accumulate partial JSON for streamed tool_use input before parsing it. A tool_use block's arguments can arrive as incomplete JSON fragments; only parse once the block's stop event fires.
- Check the final stop reason, not just the last text delta received. A response that stopped early due to
max_tokenslooks similar to a complete one if you're only watching for text, so read the terminal event's stop reason explicitly.
When You Are Done
Work through section A once per new integration, and keep B-D as an ongoing reference while building against whichever SDK the service ends up using.
If more than a couple of items in section A stay unchecked for a service now running in production on the compatibility layer, that's a signal the migration to a native SDK is overdue, not optional.
FAQs
Should I start with the OpenAI compatibility layer or a native SDK?
Start with the compatibility layer only if you already have a working OpenAI integration and want a fast test or migration. If you're building fresh, go straight to the native SDK for your language; there's no compatibility-layer benefit to a greenfield project.
Is it wrong to leave a service on the compatibility layer permanently?
Not automatically wrong, but it's a trade-off: you're limited to the OpenAI-shaped parameter surface and inherit its translation gaps. If the parameters you use all map cleanly and the team has no bandwidth to migrate, it can be an acceptable long-term choice.
What's the single most common mistake teams make choosing between SDKs?
Picking a language for the integration based on team preference rather than what the service is actually written in, which then forces an unnecessary cross-language hop just to call Claude.
Why does Go's error handling differ from Java's and C#'s in this checklist?
Because each SDK follows its host language's own convention: Go returns errors as values, while Java and C# throw typed exceptions. This isn't inconsistency in the Claude SDKs; it's each SDK matching the idioms developers in that language already expect.
Do I need to worry about connection leaks in every SDK, or just Java?
Java's StreamResponse is the most explicit case since it's an AutoCloseable you must close yourself. Go and C# manage the underlying connection lifecycle more implicitly through their iterator and async-stream patterns, but reusing a single long-lived client (rather than constructing one per request) matters in all of them.
What's the biggest gap to watch for when migrating tool-calling code through the compatibility layer?
The shape mismatch between OpenAI's function-calling parameters and Claude's tool_use protocol. Test tool-calling flows specifically after migrating, rather than assuming they behave identically just because plain text generation worked.
Can I mix the compatibility layer and a native SDK in the same codebase?
Yes, temporarily, as a migration strategy: keep the compatibility layer on stable, low-risk call paths while porting higher-value or tool-heavy call paths to the native SDK first.
How strict should I be about matching schema syntax to each language's idioms?
Strict. Fighting a language's natural conventions (like trying to force strict typed schemas in PHP or Ruby) adds friction without adding safety, since the wire protocol is identical regardless of how idiomatically you write the client-side code.
What should trigger moving off the compatibility layer for good?
Any of: you've validated Claude is the right model for production, you need a Claude-specific capability not exposed through the OpenAI-shaped surface, or you've hit a parameter mapping gap that affects a real feature.
Is there a performance cost to using a native SDK over the compatibility layer?
Not meaningfully from the SDK itself; both talk over HTTP to Anthropic's infrastructure. The practical benefit of the native SDK is a more complete parameter surface and idiomatic error/streaming handling, not raw speed.
Related
- Beyond Python and TypeScript: The Other Official Claude SDKs - the conceptual overview behind section A's guidance
- Migrating an OpenAI SDK Codebase to Claude - the full migration walkthrough behind sections A and B
- OpenAI Compatibility Layer Parameter Mapping Reference - the parameter-by-parameter detail behind section A's mapping guidance
- Idiomatic Tool Use Across Go, Java, C#, PHP, and Ruby - the full comparison behind section C
- Streaming Responses in Java and C# with the Official SDKs - the full walkthrough behind section D
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the current official SDKs for Go, Java, C#, PHP, and Ruby. Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.