OpenAPI Generator vs Kiota vs Speakeasy

This comparison belongs to Client SDK Generation Pipelines in the API Design Fundamentals & Architecture reference. All three tools read an OpenAPI document and emit a client, and all three will produce something that compiles. They differ in what the output feels like to use, how you customise it, and how much of the release pipeline they own — which is what this page compares.

The decision trigger

Situation Points toward
You need clients in eight languages, including niche ones OpenAPI Generator
Your document is OpenAPI 3.1 with type: [T, "null"] throughout Kiota or Speakeasy
The SDK is a public product and ergonomics are a selling point Speakeasy
You want a fluent, request-builder call style Kiota
You must vendor the generator into an air-gapped build OpenAPI Generator or Kiota
You want publishing, changelogs and versioning handled for you Speakeasy
You already run a JVM-based build toolchain OpenAPI Generator

Side-by-side comparison

Dimension OpenAPI Generator Kiota Speakeasy
Licence / cost Apache 2.0, free MIT, free Commercial, free tier
Runtime required JVM (or Node wrapper, or Docker) .NET CLI (or Docker) Single binary CLI
Language coverage ~50 generators ~8 first-class ~10 first-class
OpenAPI 3.1 support varies per template native native
Customisation model Mustache template overrides limited; abstractions library config file + overlays
Call style one class per tag fluent request builders one namespace per tag
Error model thrown exception per language typed error union typed error classes
Publishing automation you build it you build it built in
Output determinism high, once timestamps are off high high
Best fit broad coverage, full control strongly typed graph-style APIs public product SDKs

The rows that actually decide it are usually language coverage and customisation model. If you need a Rust and a PHP client, the choice makes itself. If you need one TypeScript client whose ergonomics are part of your developer marketing, the calculation is entirely different.

Generators positioned by language breadth and output ergonomics A two-axis chart. The horizontal axis is language breadth from narrow to broad. The vertical axis is output ergonomics from derived to designed. OpenAPI Generator sits broad but derived, Kiota sits narrow and middling, Speakeasy sits narrow-to-medium and designed. language breadth → ergonomics → narrow ~50 languages derived designed OpenAPI Generator templates give full control Kiota fluent builders, strict types Speakeasy publishing + docs included

The same operation, three clients

The clearest way to choose is to read the call site each tool produces for one operation — GET /invoices/{invoiceId} with operationId: getInvoice and tags: [invoices].

// OpenAPI Generator (typescript-fetch): one API class per tag
import { Configuration, InvoicesApi } from "./sdk/typescript";

const invoices = new InvoicesApi(new Configuration({
  basePath: "https://api.example.com/v4",
  headers: { Authorization: `Bearer ${token}` },
}));

const invoice = await invoices.getInvoice({ invoiceId: "inv_123" });
// Kiota: a fluent request builder mirroring the URL structure
import { createBillingClient } from "./sdk/kiota";

const client = createBillingClient(adapter);
const invoice = await client.invoices.byInvoiceId("inv_123").get();
// Speakeasy: namespaced methods with typed error unions
import { Billing } from "./sdk/speakeasy";

const billing = new Billing({ bearerAuth: token });
const invoice = await billing.invoices.get({ invoiceId: "inv_123" });

Kiota’s builder style pays off when URLs are deeply nested and clients traverse them dynamically; it costs you a longer call chain for a flat CRUD API. The other two converge on the same shape, differing mainly in error handling: Speakeasy tends to surface documented error responses as typed values, while OpenAPI Generator throws a language-native exception carrying the raw response — which puts more of the burden on your wrapper to map an RFC 7807 Problem+JSON body into something callers can branch on.

Evaluating them on your own document

Do not choose from a comparison table — including this one. Run all three against your real spec and read the diff. Twenty minutes of this saves a year of regret.

# 1. Bundle once so every generator reads the identical input.
npx @redocly/cli bundle openapi.yaml -o dist/openapi.yaml --dereferenced=false

# 2. OpenAPI Generator
npx @openapitools/[email protected] generate \
  -i dist/openapi.yaml -g typescript-fetch -o /tmp/eval/oag \
  --additional-properties=hideGenerationTimestamp=true

# 3. Kiota
dotnet tool install --global Microsoft.OpenApi.Kiota
kiota generate -l typescript -d dist/openapi.yaml -c BillingClient -o /tmp/eval/kiota

# 4. Speakeasy
brew install speakeasy-api/tap/speakeasy   # or the install script
speakeasy generate sdk --schema dist/openapi.yaml --lang typescript --out /tmp/eval/speakeasy

# 5. Compare what each one actually produced.
for d in oag kiota speakeasy; do
  printf '%-12s files=%-5s lines=%s\n' "$d" \
    "$(find /tmp/eval/$d -type f | wc -l)" \
    "$(find /tmp/eval/$d -type f -name '*.ts' -exec cat {} + | wc -l)"
done

Then read, in each output: the method name for one operation, the type of one nullable field, the shape of one discriminated union, and what happens on a documented 404. Those four answers predict most of your future friction.

Four probes for evaluating generator output One bundled specification is fed to three generators. Each output is inspected for method naming, nullable field typing, discriminated union shape and error response handling, producing a comparison verdict. one bundled spec OpenAPI Generator Kiota Speakeasy four probes 1 · method name 2 · nullable field 3 · union shape 4 · 404 handling read, do not benchmark decision pin it in CI

Standard compliance

None of these tools implements the whole specification, and the gaps cluster in predictable places:

Spec feature Clause Typical support
oneOf + discriminator OAS 3.1 §4.8.25 good in all three when mapping is explicit
type: [T, "null"] JSON Schema 2020-12 §6.1.1 native in Kiota/Speakeasy; template-dependent in OpenAPI Generator
style / explode for arrays OAS 3.1 §4.8.12 good, but verify explode: false
unevaluatedProperties JSON Schema Core 2020-12 §11.3 widely unimplemented
webhooks OAS 3.1 §4.1 rarely generated as client code
links OAS 3.1 §4.8.20 rarely generated

The unimplemented rows are not blockers, but they should shape what you put in the contract: a document that leans on unevaluatedProperties for validation is fine on the server and invisible to every client generator.

The four probes above map onto concrete failure modes, and each tool fails differently:

Where each generator family tends to disappoint Open-source generators depend on the language template for OpenAPI 3.1 support and leave publishing to you. Commercial tooling covers publishing and ergonomics but ties the release process to a vendor. open-source generators commercial generators 3.1 support varies per language template nullable type arrays sometimes dropped publishing pipeline is yours to build customisation via template overrides 3.1 handled natively unions and nullability preserved publishing and changelogs included customisation via config and overlays generate from your real spec and read the emitted types — the tables never settle it

What changing generators costs

Switching is a full rewrite of the SDK’s public surface: method names, type names, error types, package layout and initialisation all change. For consumers that is a major version bump and a migration guide, not a patch release — plan it with the same care as an API version transition described in Breaking Change Detection. The mitigation is the wrapper layer: if the surface consumers import is hand-written and delegates to the generated code, a generator swap becomes an internal change and the public API stays put.

Common mistakes

Mistake Correct approach
Choosing from a feature table alone Generate with all three from your real spec and read the output
Assuming 3.1 support because the README says “OpenAPI 3” Test a type: [T, "null"] field and a discriminated union
Exposing generated types directly to consumers Wrap them so a generator swap stays internal
Running an unpinned generator in CI Pin the version; treat upgrades as reviewed changes
Picking a generator before the spec is linted Fix operationIds and component names first — they dominate output quality
Ignoring publishing when comparing Factor in the release automation you will otherwise build yourself

FAQ

Which SDK generator handles OpenAPI 3.1 documents best?

Kiota and Speakeasy were built after 3.1 landed and target the JSON Schema 2020-12 dialect natively. OpenAPI Generator’s support depends on the individual language template, and several templates historically down-converted 3.1 documents, silently dropping type arrays that include "null" and producing non-nullable fields. The test takes five minutes: put one nullable field and one discriminated union in a scratch document, generate, and look at the emitted types. Whichever tool preserves both is the one that understands your contract.

Can I switch generators later without breaking consumers?

Not transparently. Every tool names methods, types and packages differently, structures errors differently, and initialises the client differently, so the generated surface is not portable. If you expect to re-evaluate, insulate consumers now: publish a thin hand-written façade that delegates to the generated client, and keep the generated code an implementation detail. Then a switch is an internal refactor plus a patch release, rather than a major version and a migration guide.

Do I need a paid tool to generate good SDKs?

No. Output quality is dominated by contract quality — explicit operationIds, named components, documented error schemas, correct required lists — and those cost nothing. The open-source tools generate perfectly serviceable clients from a well-specified document. What paid tooling buys is the surrounding machinery: publishing to several registries, generated changelogs, usage snippets, docs integration and support. That is worth money when the SDK is a public product carrying your developer experience, and hard to justify for an internal service-to-service client.