SDKs: build, generate, or skip?
When an SDK is worth the cost for partner developer experience, when to generate from OpenAPI, when to hand-build, and which languages to ship first.
A partner engineer evaluates your API and then asks: is there an SDK, or do we write HTTP by hand. That is not about polish. It is about who pays for auth, retries, pagination, and errors. If you ship an SDK, you pay once. If you skip it, every partner pays, and some pad the estimate and walk.
The reverse is also true. A stale or thin SDK is worse than none, because it fails after the partner already committed. The decision is not "SDKs are good." It is when an SDK pays off, whether to generate it from OpenAPI or write it by hand, and which languages actually matter.
This sits next to our OpenAPI spec guide and the partner-ready API checklist. The spec is the contract. The SDK is one way of shipping that contract into a partner's codebase.
The 60-second version
If you only read one section, read this one:
- Skip the SDK until the API is the product. If docs, auth, errors, and a changelog are still thin, fix those first.
- Generate from OpenAPI for coverage, hand-build for the paths partners actually use. Most teams should not pick only one.
- Ship the languages your partners write in, not the ones your team likes. TypeScript, Python, and the cohort language beat a seven-language set.
- A partner-grade SDK is more than generated types. Auth, pagination, retries, typed errors, and a working example make the difference.
- Keep the SDK honest with the spec. Generate in CI, pin versions to API versions, treat drift as a broken API.
- A CLI can substitute for a first SDK. If you can only staff one client, a SaaS CLI often gets partners to a first call faster.
- Measure first successful call and issues, not stars. If partners still paste curl, the SDK is not doing its job.
- Do not publish a language you cannot support. An abandoned SDK is a trust tax.
When an SDK is worth the cost
An SDK is a maintained client library that turns your HTTP contract into types, helpers, and failure modes that feel native in a language the partner already uses. It pays off when three conditions are true at once.
Partners are writing real code against you. A prospect can live on curl and a quickstart. A partner shipping an integration that will run for years wants types, retries, and a pinable changelog.
The same work is being repeated in every integration. Auth, pagination, idempotency, rate limits, and your error catalog show up in every first sprint. If you document those well and still watch partners reimplement them, an SDK is cheaper than the same Slack thread forever.
You can keep it current. An SDK is a promise that the library matches the API. If you cannot staff that promise, skip it. Partners will forgive a missing SDK. They will not forgive a library that lies.
| Signal | Skip | Generate | Hand-build |
|---|---|---|---|
| API maturity | Docs and errors still moving | Stable spec, additive changes | Stable spec, a few high-traffic flows |
| Partner volume | Evaluating, not shipping | Several languages, similar needs | One or two languages, deep usage |
| Staffing | No named owner | CI plus a reviewer | A language-fluent owner |
| What you ship | curl, Postman, CLI | Typed clients from OpenAPI | Idiomatic helpers on generated cores |
Build, generate, or skip
Those three options are not a maturity ladder. They are answers to different jobs.
Skip means you do not publish a client library. You publish a spec, docs, examples, and preferably a CLI. This is right for an early partner-facing API, a surface that still changes shape, and any language you cannot support. Skipping is a product decision. Write it down so sales stops promising an SDK that does not exist.
Generate means an OpenAPI document is the source, and a generator produces clients. You review the output, add a thin layer for auth and examples, and publish. Coverage is wide. Idiom is average. Maintenance is a pipeline. This is the default once the spec is honest.
Hand-build means a person writes the client in the target language. You get idiomatic code and better errors. You also get drift unless that person keeps watching the API. Hand-build the small set of languages and flows where generated output is not good enough.
The common failure is to pick one forever. Generate the long tail. Hand-build the two or three clients your most important partners import. Skip everything else until someone asks twice.
Generate from OpenAPI versus hand-built
Generation only works if the spec is the source of truth. If your OpenAPI file disagrees with production, every generated SDK is a packaged lie. Fix the spec first, as described in the OpenAPI spec guide.
Generators are good at types and models, one method per operation, regenerating the whole surface, and keeping many languages in lockstep. They are bad at native auth, iterators instead of page structs, retries and idempotency as defaults, errors that map onto the language, and examples that actually run.
The useful pattern is hybrid. Generate the models and the call layer. Hand-write a small kit: one auth helper, one paginator, one retry policy, one error type, one example. Publish the kit as the thing partners import. Keep generated code in a folder nobody edits by hand.
| Dimension | Generated | Hand-built | Hybrid (usual winner) |
|---|---|---|---|
| Coverage | Full surface, every release | Whatever someone had time for | Generated core, hand-built kit |
| Idiom | Often awkward | Native | Native where it matters |
| Drift risk | Low if CI regenerates | High | Low on core, watched on kit |
| Time to first language | Days once the spec is good | Weeks per language | Days plus a short kit pass |
Two rules keep generation from rotting. Regenerate in CI, never by hand. A pull request that changes the spec should regenerate clients or fail. Pin SDK versions to API versions. When you cut a new API version, cut a new SDK major. Partners who pin the library should not silently start sending a new contract. That discipline belongs with your changelog.
Which languages to ship first
Language priority is a partner question, not a taste question. Count who is building on you, in what. A four-language set that nobody imports is worse than one language partners actually use. A practical default:
- TypeScript / JavaScript. Partner apps, internal tools, and a large share of integration code. It is also the language many MCP wrappers speak, so it doubles as a path into AI clients. See MCP for SaaS.
- Python. Data, automation, internal scripts, and a lot of partner glue. If your partners are analytics or ops, this may be first.
- The language of your biggest integration cohort. Java or C# into enterprise IT. Go for infrastructure partners. PHP next to WordPress-era stacks. Count the repos and the tickets. Do not guess.
- Anything else, generated if cheap, unpublished if you cannot answer issues. An internal Kotlin client is not a partner SDK.
| Priority | Language | Ship when | How |
|---|---|---|---|
| 1 | TypeScript | You have a public API at all | Hybrid: generate models, hand-write auth and pagination |
| 2 | Python | Partners script or work in data | Same hybrid pattern |
| 3 | Cohort language (Java, C#, Go, PHP) | Named partners or tickets | Generate first, kit if usage is real |
| 4 | Extra generated clients | Spec is stable and CI is boring | Generate only, no promise of idiom |
| Skip | Anything with no owner | Always | Document curl and move on |
Revisit the stack when the partner mix changes. The list above is a starting prior, not a ranking of languages.
What a partner-grade SDK actually contains
Partners do not need every endpoint wrapped. They need the painful parts of your API to disappear.
Auth that works in one call. Load a key or run OAuth, attach the token, refresh it.
Pagination as an iterator. Yield items until the partner stops, and honor rate limits while doing it.
Retries on errors that are safe to retry. Timeouts, 429s, and 5xx on idempotent calls. Not on a creating POST unless the partner passed an idempotency key.
Typed errors that match your catalog. Map codes onto types the partner can switch on. See API error design.
A runnable example that authenticates, creates or fetches one thing, and prints it. If that script is broken, the SDK is broken. Send a version and user-agent you can see in logs.
That list is the kit. Everything else can stay generated. Keep curl, docs, and a CLI first-class. An MCP server is a different consumer: a model choosing tools. The TypeScript client can sit inside the server. It should not be the interface the model sees.
Operating an SDK program without a platform team
You need a named owner, a pipeline, and a support path, not a DX org.
The owner is usually whoever owns the API. They own the spec, the generator, the kit, the release, and issue triage. Release on the API's cadence: new endpoint, clients ship in the same window; breaking API version, SDK majors.
Issues against generated code are often spec issues. Route them there. If a language has no kit and no owner, the README should say so. Do not list eight official SDKs if six have no reviewer.
Deprecate in public. When you drop a language or a major, put a date on it, leave the old package up, and point at the replacement.
Measure first successful call with your user-agent, issues per language, and version spread. Stars lie. If partners still paste curl into production, the example or the install is the problem, not a missing fifth language. The partner-ready API is the foundation. The SDK is a generated, slightly hand-built view of it.
Common mistakes, and the fix
Publishing an SDK to look mature while the API is still moving. The fix: skip until the spec, errors, and auth are stable enough to generate from.
Hand-writing five languages with no generator. The fix: one OpenAPI spec, generated cores, human time on the kit in the one or two languages that matter.
Shipping raw generator output as the product. The fix: add auth, pagination, retries, typed errors, and a runnable example.
Choosing languages by internal preference. The fix: count partner repos, tickets, and marketplace apps. Ship those. Skip the rest, in writing.
Letting the SDK drift from the spec. The fix: regenerate in CI, pin library majors to API versions, treat a failing example as a release blocker.
Treating the SDK as the only partner interface. The fix: keep curl, docs, and a CLI first-class.
FAQ
Do we need an SDK if we already have good docs and a CLI? Not always. Docs and a CLI get a partner to a first call. An SDK pays off when partners repeat auth, pagination, and error handling in every production repo. If that is not happening yet, skip it.
Should we generate SDKs from OpenAPI or write them by hand? Generate the models and the call layer from OpenAPI. Hand-write a small kit around auth, pagination, retries, and errors in the languages you support. Pure generation is awkward. Pure hand-building does not scale.
Which SDK languages should we ship first? TypeScript and Python cover most B2B partner code. Add the language your largest cohort actually writes. Generate extras if CI is cheap. Do not publish a language you cannot review.
What belongs in a v1 SDK? Auth, typed models, pagination, retries on safe errors, errors that match your catalog, a user-agent, and one runnable example. Full coverage can be generated.
Is a stale SDK worse than no SDK? Yes. No SDK means partners write HTTP against a documented API. A stale SDK means they trusted types that no longer match production.
How does an SDK relate to an MCP server? An SDK is for developers importing a package. An MCP server is for a model choosing tools. The TypeScript client can sit inside the server. It should not be the interface the model sees.
The short version
Skip the SDK until the API is stable enough to describe in an OpenAPI spec. Then generate the cores so coverage does not rot, and hand-build a small kit in the languages your partners actually write: usually TypeScript, Python, and one cohort language. Pin versions to the API, regenerate in CI, and measure first successful calls, not stars. A CLI can stand in for a first client. An abandoned library cannot.
The SDK is not a maturity badge. It is a way of paying auth, pagination, and error handling once, so every partner does not pay them separately. If you cannot staff that, do not publish it.
If you want a clear call on whether to skip, generate, or hand-build, and which languages would actually get used, that is exactly what a Partner Audit is for. We review your API, spec, and partner mix, then tell you what to ship and what to leave unpublished.
Further reading
- OpenAPI Specification: the current spec, which is the contract generators read.
- OpenAPI.tools: a maintained catalog of generators, validators, and SDK tooling.
- OpenAPI Generator: widely used open-source generation of client libraries from an OpenAPI document.