Skip to content
FeedbackDashboard
Resources
Comparisons

Speakeasy vs. Stainless

A detailed comparison of Stainless and Speakeasy for SDK generation: features, language support, configuration, documentation, and more.

Choosing an SDK generation platform is a significant decision that affects your developer experience, maintenance burden, and API adoption. This page provides an honest, detailed comparison of Stainless and Speakeasy to help you evaluate which platform best fits your needs.

Both platforms generate SDKs from OpenAPI specifications and support multiple languages. They differ in their approach to SDK design, configuration, documentation, and what ships out of the box versus what requires paid add-ons.

Why teams choose Stainless:

  • Battle-tested at scale: Stainless SDKs are downloaded over 130 million times per week and trusted by API leaders like Anthropic, Cloudflare, Google, and OpenAI. That scale means more edge cases solved and more production-hardened output than any other codegen platform.
  • Flexible and customizable: Add custom code anywhere in your SDKs (persists across regeneration), fully customize your generated docs, and fine-tune naming, design, and deployment to match your needs.
  • Complete API platform: SDKs, docs, CLI tools, Terraform providers, and MCP servers all generate from one OpenAPI spec and one config file, so each additional target you adopt makes the whole platform more valuable.

Both Stainless and Speakeasy support SDKs for the most popular programming languages. The key differences lie in additional codegen targets beyond traditional SDKs.

Stainless generates more than just language SDKs. Terraform providers, CLI tools, and MCP servers extend how your users interact with your API beyond traditional SDK integrations.

LanguageStainlessSpeakeasy
TypeScript
Python
Go
Java
Kotlin
C# / .NET / Unity
Ruby
PHP
Terraform
CLI⚠️ Beta
SQL⚠️ Experimental
MCP server

Stainless is the only platform that generates Kotlin SDKs from your OpenAPI spec, and both platforms can generate CLI tools (Speakeasy’s CLI generation is in beta). The Kotlin SDK has a distinct public API from the Java SDK, using nullable types instead of Optional, Sequence instead of Stream, and suspend functions instead of CompletableFuture.

The features built into your generated SDKs determine how much boilerplate your API consumers write and how robust their integrations are out of the box.

Stainless SDKs ship with sensible defaults like rich error hierarchies, forward-compatible response handling, and per-call raw responses, so your users write less boilerplate and build more resilient integrations.

FeatureStainlessSpeakeasy
Authentication✅ (incl. OAuth 2.0, mTLS via hooks)
Retries with backoff✅ (with idempotency keys)
Timeouts
Auto-pagination
Error handling✅ Rich error hierarchy⚠️ Per-status classes when schemas defined
Raw responses✅ Per-call⚠️ SDK-level configuration
Response validation⚠️ Not forward compatible by default
Forward compatibility✅ Extra params on every method⚠️ Response-side only
Webhook deserialization
WebSocket support⚠️ Beta
Server-sent events✅ All languages⚠️ 7 languages
Automated SDK tests✅ Included

Stainless generates a rich error hierarchy with typed access to error details. Speakeasy generates per-status error classes only when error response schemas are defined in the spec.

import Example, {
APIError,
APIConnectionError,
APIConnectionTimeoutError,
APIUserAbortError,
BadRequestError,
AuthenticationError,
PermissionDeniedError,
NotFoundError,
ConflictError,
UnprocessableEntityError,
RateLimitError,
InternalServerError,
ExampleError,
} from "stainless-demo-sdk";
const client = new Example({ apiKey: "..." });
async function createWidget() {
try {
const widget = await client.widgets.create({ name: "Test" });
return widget;
} catch (err) {
// --- HTTP status code errors (most specific first) ---
if (err instanceof BadRequestError) {
// 400 - invalid request parameters
console.error("Bad request:", err.status, err.message);
console.error("Error body:", err.error);
return;
}
if (err instanceof AuthenticationError) {
// 401 - missing or invalid credentials
console.error("Auth failed:", err.message);
return;
}
if (err instanceof PermissionDeniedError) {
// 403 - insufficient permissions
console.error("Forbidden:", err.message);
return;
}
if (err instanceof NotFoundError) {
// 404 - resource not found
console.error("Not found:", err.message);
return;
}
if (err instanceof ConflictError) {
// 409 - conflicts with current resource state
console.error("Conflict:", err.message);
return;
}
if (err instanceof UnprocessableEntityError) {
// 422 - validation failed
console.error("Validation error:", err.message, err.error);
return;
}
if (err instanceof RateLimitError) {
// 429 - rate limited
const retryAfter = err.headers?.get("retry-after");
console.error("Rate limited. Retry after:", retryAfter);
return;
}
if (err instanceof InternalServerError) {
// 5xx - server error
console.error("Server error:", err.status, err.message);
return;
}
// --- Connection errors ---
if (err instanceof APIConnectionTimeoutError) {
// Request timed out (subclass of APIConnectionError, check first)
console.error("Timeout:", err.message);
return;
}
if (err instanceof APIConnectionError) {
// Network failure, DNS resolution, etc.
console.error("Connection failed:", err.message, err.cause);
return;
}
// --- User abort ---
if (err instanceof APIUserAbortError) {
// Request cancelled via AbortSignal
console.error("Request aborted:", err.message);
return;
}
// --- Catch-all for any other API error ---
if (err instanceof APIError) {
// Unexpected status code not covered above
console.error("API error:", err.status, err.message);
console.error("Headers:", err.headers);
console.error("Body:", err.error);
return;
}
// --- Base SDK error (non-API) ---
if (err instanceof ExampleError) {
console.error("SDK error:", err.message);
return;
}
throw err; // Re-throw unknown errors
}
}

Stainless SDKs are designed with evolution in mind so that you can update your APIs easier. For example, SDKs preserve unknown fields from API responses, so your users can access new fields before updating to the latest SDK version.

// Stainless preserves unknown fields on responses - new API fields are
// accessible immediately, before the SDK is updated
const user = await client.users.get('123');
console.log(user.newField); // accessible even before SDK update
// Extra params are also forwarded on every request method
const widget = await client.widgets.create({
name: "Test",
// @ts-ignore - forward extra request params to the API
newParam: "value",
});

The design of a generated SDK directly affects how quickly developers can adopt your API and how confidently they can build on it. Well-designed SDKs reduce friction by providing clear types, idiomatic patterns, and consistent behavior across languages.

Stainless invests in clean type consolidation and compile-time safety, producing SDKs that feel hand-written while keeping bundle sizes small by avoiding runtime validation libraries.

AspectStainlessSpeakeasy
Type consolidation✅ Merges equivalent schemas⚠️ Direct OpenAPI mapping
ValidationCompile-time typesZod (TS) / Pydantic (Python)
Sync and async clients✅ (with configuration)

Type safety approach

Stainless and Speakeasy take different approaches to type safety. Stainless relies on compile-time type checking and generates clean type hierarchies that consolidate equivalent OpenAPI schemas. Speakeasy uses runtime validation libraries like Zod (TypeScript) and Pydantic (Python) to validate responses at runtime.

Stainless intentionally avoids runtime request validation to keep bundle sizes small, preserve forward compatibility, and avoid rejecting valid requests when API constraints change. This is particularly important for frontend use cases and edge runtimes where code size matters. See the FAQ for more on this design decision.

How you configure SDK generation and integrate it into your development workflow affects how quickly you can iterate on your SDKs.

A single configuration file and semantic merge for custom code let teams iterate faster without worrying about losing their customizations or managing multiple config files.

AspectStainlessSpeakeasy
Configuration1 (stainless.yml)2+ (gen.yaml, workflow.yaml)
Custom code✅ Semantic merge✅ Code regions and hooks
Local development✅ CLI, VS Code Extension, LSP, MCP server✅ CLI
OpenAPI customizationTransformsOverlays
CI/CD integrationGitHub ActionsGitHub Actions

Stainless uses a single stainless.yml file to define your SDK configuration: resources, methods, pagination, authentication, publishing targets, and more.

Speakeasy splits configuration across gen.yaml (generation settings), .speakeasy/workflow.yaml (CI/CD workflow), and x-speakeasy-* OpenAPI extensions for feature-specific configuration like retries, pagination, and error handling.

Stainless shows diagnostics in the Studio and CLI output if we detect problems with or potential improvements in your OpenAPI spec or Stainless config. An AI agent based workflow helps you fix diagnostics in your OpenAPI spec and config, and you can also use the Stainless VS Code extension to get diagnostics feedback directly in your editor. ~193 diagnostics span across 23 categories, covering issues from OpenAPI schema problems to suboptimal SDK design patterns.

Speakeasy provides a linter that validates OpenAPI 3.x documents for correctness, style, and SDK generation readiness. It is built on the open-source speakeasy-api/openapi linter framework and runs using the speakeasy-recommended ruleset by default, which can be extended with additional rulesets.

Both platforms allow you to add custom code to generated SDKs. You commit custom code directly to your SDK repository, and both platforms preserve your changes across regeneration cycles. See Add custom code for details.

The difference is subtle. If you have multiple environments, preview builds, or feature branches, Speakeasy’s custom code will create friction. Stainless handles this natively.

Stainless: When you create a new branch, custom code from the parent branch is automatically inherited. You can freely add new custom code to your branch and resolve branch-specific merge conflicts as needed. Once you merge your branch, any custom code you added is pulled into the base branch, and conflict resolutions are rerere’d so you do not resolve the same conflict twice.

Speakeasy: Custom code state is tracked in a lockfile that points to low-level git objects. Branches that diverge cannot easily be merged because the lockfiles will also diverge, and there is no way to determine a semantically correct resolution.

When your OpenAPI spec needs adjustments for SDK generation, Stainless provides transforms, a declarative system for modifying your spec during the generation pipeline without changing the source file. Stainless transforms provide feedback when they become unused, preventing configuration from silently drifting out of sync with your spec. Overlays do not provide this feedback.

Speakeasy uses OpenAPI Overlays, a specification-level standard for modifying OpenAPI documents. Both approaches let you fix incorrect types, add missing fields, and customize the spec without altering the original file.

# Stainless transform - warns if path no longer matches
transforms:
- target: "$.paths['/users'].post.requestBody"
update:
required: true

For a deeper look at how transforms work, see Stainless Transforms: The Fast Lane for Fixing Imperfect OpenAPI Specs.

The number of runtime dependencies in your generated SDKs affects installation size, security surface area, and compatibility with different environments.

Fewer dependencies mean a smaller attack surface, faster installs, and fewer version conflicts for your SDK consumers.

LanguageStainlessSpeakeasy
TypeScript0
1
  • zod
Python
6
  • httpx
  • pydantic
  • typing-extensions
  • anyio
  • distro
  • sniffio
4
  • httpcore
  • httpx
  • jsonpath-python
  • pydantic
Go
2
  • tidwall/gjson
  • tidwall/sjson
2
  • spyzhov/ajson
  • stretchr/testify
Java
11
  • jackson-core
  • jackson-databind
  • jackson-annotations
  • jackson-datatype-jdk8
  • jackson-datatype-jsr310
  • jackson-module-kotlin
  • error_prone_annotations
  • httpcore5
  • httpclient5
  • okhttp
  • logging-interceptor
10
  • jackson-annotations
  • jackson-databind
  • jackson-datatype-jsr310
  • jackson-datatype-jdk8
  • jackson-databind-nullable
  • commons-io
  • jakarta.annotation-api
  • slf4j-api
  • json-path
  • reactive-streams
Ruby
2
  • cgi
  • connection_pool
6
  • base64
  • faraday
  • faraday-multipart
  • faraday-retry
  • janeway-jsonpath
  • sorbet-runtime
PHP
5
  • php-http/discovery
  • psr/http-client
  • psr/http-client-implementation
  • psr/http-factory-implementation
  • psr/http-message
2
  • ext-json
  • guzzlehttp/guzzle
C# / .NET / Unity
2–4
  • System.Text.Json
  • System.Net.ServerSentEvents
  • Microsoft.SourceLink.GitHub (build-time only)
  • System.Collections.Immutable (netstandard2.0 only)
2
  • Newtonsoft.Json
  • NodaTime
Terraform
10–11
  • davecgh/go-spew
  • hashicorp/terraform-plugin-docs
  • hashicorp/terraform-plugin-framework
  • hashicorp/terraform-plugin-framework-jsontypes
  • hashicorp/terraform-plugin-framework-timetypes
  • hashicorp/terraform-plugin-framework-validators
  • hashicorp/terraform-plugin-go
  • hashicorp/terraform-plugin-log
  • stainless-sdks/project-sdk-go (project's own generated Go SDK)
  • tidwall/gjson
  • tidwall/sjson
8
  • hashicorp/go-uuid
  • hashicorp/terraform-plugin-docs
  • hashicorp/terraform-plugin-framework
  • hashicorp/terraform-plugin-framework-validators
  • hashicorp/terraform-plugin-go
  • hashicorp/terraform-plugin-log
  • apyzhov/ajson
  • stretchr/testify
MCP Server
15–16
  • project-sdk (project's own generated TypeScript SDK)
  • @cloudflare/cabidela
  • @modelcontextprotocol/sdk
  • @valtown/deno-http-worker
  • cookie-parser
  • cors
  • express
  • fuse.js
  • jq-web
  • morgan
  • qs
  • typescript
  • yargs
  • zod
  • zod-to-json-schema
  • zod-validation-error
5
  • @modelcontextprotocol/sdk
  • @stricli/core
  • bun
  • express
  • zod

The table below shows the on-disk size of each installed SDK package. Stainless optimizes for size at the language level: TypeScript SDKs support opt-in tree shaking so bundlers can eliminate unused resources, and Python SDKs use lazy loading so imports are deferred until first use.

LanguageStainlessSpeakeasy
TypeScript1.9M81M
Python2.4M107M
Go1.4M4.0M
Java4.4M62M
Ruby2.5M10M
PHP1.6M58M
C# / .NET2.3M2.8M
Terraform1.5M1.6M

API documentation is a critical part of developer experience. The approach your SDK generator takes to documentation determines how much additional tooling you need.

Stainless provides a complete documentation platform with prose content, SDK references, and self-hosting. Speakeasy provides API reference generation only, powered by Scalar.

FeatureStainlessSpeakeasy
Full documentation site⚠️ API reference only
Prose documentation
API reference✅ Auto-generated✅ Auto-generated (via Scalar)
SDK reference✅ Specialized per language⚠️ Code samples per-language
Custom hosting
Docs-as-code workflow
Code samples for third-party docs

Stainless includes a complete documentation platform built on Astro that generates API references, SDK references, and supports custom prose content with a docs-as-code workflow. You can host Stainless-generated docs on your own domain with full control over branding, navigation, and content.

Speakeasy offers “Speakeasy Docs” (powered by Scalar), which generates API reference documentation with auto-synced SDK code samples from your OpenAPI spec. Both platforms can generate code snippets (via x-codeSamples extensions) that enrich existing OpenAPI-powered documentation.

Stainless gives you a full Astro repository that you own and control: add React, Vue, or Svelte components, write custom CSS, and deploy to any static host including Vercel, Netlify, or Cloudflare Pages. Changes go through pull requests and CI/CD, following a true docs-as-code workflow.

Speakeasy Docs is focused on API reference and does not support custom prose content, self-hosting, or docs-as-code workflows. For a full documentation site with Speakeasy SDKs, you need to set up and maintain a separate tool.

Model Context Protocol (MCP) servers allow AI agents to interact with your API. Both platforms can generate MCP servers from your OpenAPI spec.

The two platforms take different approaches: Stainless generates MCP servers with code execution and documentation search tools, while Speakeasy offers a broader managed platform with Gram for cloud hosting and OAuth support.

FeatureStainlessSpeakeasy
Code execution tool
Documentation search tool
Publishing (npm)
Cloudflare Workers hosting
Managed cloud platform
Custom tool definitions
Scope-based filtering
OAuth support

Stainless-generated MCP servers include two tools: a code execution tool that runs TypeScript code against your SDK in a sandboxed environment, and a documentation search tool that looks up SDK usage information. This approach lets AI assistants write and execute actual SDK code rather than just making raw API calls.

Speakeasy generates MCP servers with individual tools for each API operation, derived directly from your OpenAPI spec. You can filter tools by scope (for example, --scope read for read-only access) and customize tool names and descriptions using the x-speakeasy-mcp extension. Speakeasy also offers Gram, a managed MCP cloud platform with auto-scaling, OAuth support, and custom tool definitions. MCP servers can be deployed as Cloudflare Workers or hosted on the Gram platform.

Both Speakeasy and Stainless generate SDKs from OpenAPI specifications, but they differ in runtime features, design quality, developer workflow, and additional capabilities like documentation and CLI generation. The right choice depends on your priorities: whether you value zero-dependency output, hand-written SDK design patterns, or breadth of language support.