In Depth: Speakeasy vs Stainless
Nolan Sullivan
June 5, 2024
This comparison of Speakeasy & Stainless is based on a snapshot of two developing companies as of May 2024. We welcome requests for updates to this post, so that users can make informed decisions about SDK generation in the future.
Besides comparing generated SDKs, we'll also investigate how these services differ in a broader sense: their development histories, their commitments to open standards, and how they fit into the API-first ecosystem.
We acknowledge that our views may be biased, but we'll show our work along with our conclusions so that readers can decide for themselves.
In Short: How Do Speakeasy and Stainless Differ?
-
OpenAPI Integration: Speakeasy is built for OpenAPI, supporting advanced features of OpenAPI 3.0 and 3.1, while testing and implementing upcoming OpenAPI features like OpenAPI Overlays (opens in a new tab) and Workflows (opens in a new tab). Stainless, is built on a custom DSL (opens in a new tab) that is currently OpenAPI compatible. This carries a risk of lock-in if the DSL diverges significantly from OpenAPI in the future.
-
Compatibility: Speakeasy is compatible with the REST ecosystem, aiming to support any API that is accurately represented by an OpenAPI document, without the need for dedicated API proxies, server-side code, or specific web frameworks. Stainless is vertically integrated, and are currently working on a TypeScript framework (opens in a new tab). While promising a streamlined solution, this vertical integration can also lead to lock-in and a loss of flexibility.
-
Velocity and Language Support: Stainless was founded in 2021 (opens in a new tab) and gradually expanded its language support and features. However, its progress has been slower compared to its competitors, releasing support for five languages in this time. In comparison, Speakeasy was founded in May 2022, found market traction in early 2023, and released support for ten languages within 12 months. Speakeasy meets the diverse needs of users, while supporting their existing stacks.
-
SDK Generator Maturity: Speakeasy creates SDKs that are idiomatic to each target language, type safe during development and production, human-readable, and fault-tolerant. Our comparison found some room for improvement in Stainless' type safety, fault tolerance and SDK project structure. Both products are under active development, and improvement should be expected.
Comparing Speakeasy and Stainless
Cautions about biases and a summary alone don't make for a good comparison, so let's take a closer look.
SDK Generation Targets
Tech stacks are diverse, and as your user base grows, so will their needs. We've found it vitally important to meet users where they already are, and supporting a wide range of generation targets is a priority at Speakeasy.
The table below shows the current SDK language support for Speakeasy and Stainless. Keep in mind that both lists are bound to change, so check the official documentation for updated lists.
Language | Speakeasy | Stainless |
---|---|---|
Go | ✅ | ✅ |
Python | ✅ | ✅ |
Typescript | ✅ | ✅ |
Java | ✅ | ✅ |
Kotlin | ⚠ Java is Kotlin-compatible | ✅ |
Terraform provider | ✅ | ❌ |
C# | ✅ | ❌ |
PHP | ✅ | ❌ |
Ruby | ✅ | ❌ |
Swift | ✅ | ❌ |
Unity | ✅ | ❌ |
Postman Collection | ✅ | ❌ |
Everyone has that one odd language that is critical to their business. In our first year, we've made a dent, but we've got further to go. See a language that you require that we don't support? Let us know (opens in a new tab).
SDK Features
This table shows the current feature support for Speakeasy and Stainless. Keep in mind that both lists are bound to change, so check the official documentation for updated lists.
Feature | Speakeasy | Stainless |
---|---|---|
Union types | ✅ | ✅ |
Discriminated union types | ✅ | ❌ |
Server sent events | ✅ | ⚠ non-OpenAPI standard |
Retries | ✅ | ✅ |
Pagination | ✅ | ✅ |
Async support | ✅ | ✅ |
Streaming uploads | ✅ | ✅ |
OAuth 2.0 | ✅ | ❌ |
Custom SDK Naming | ✅ | ✅ |
Customize SDK Structure | ✅ | ❌ |
Custom dependency injection | ✅ | ❌ |
Speakeasy creates SDKs that handle advanced authentication. For example, Speakeasy can generate SDKs that handle OAuth 2.0 with client credentials - handling the token lifecycle, retries, and error handling for you.
Stainless leaves some of these features to be implemented by the user. For example, Stainless generates SDKs that handle OAuth 2.0, but the user must implement the token lifecycle, retries, and error handling themselves.
Stainless also lacks support for custom dependency injection, advanced union types, and streaming uploads.
Platform Features
Speakeasy is designed to be used locally, with a CLI that allows for local experimentation and iteration. This makes it easier to test and iterate on your SDKs, and allows for custom CI/CD workflows.
Conversely, Stainless is a web-based tool that allows you to upload your OpenAPI document and generate an SDK. This makes it easier to visualize spec changes and see how they impact your SDKs.
Feature | Speakeasy | Stainless |
---|---|---|
GitHub CI/CD | ✅ | ✅ |
CLI | ✅ | ⚠ |
Web Interface | ✅ | ✅ |
OpenAPI GUI | ❌ | ✅ |
OpenAPI overlays | ✅ | ❌ |
Package Publishing | ✅ | ✅ |
Product Documentation | ✅ | ✅ |
OpenAPI Linting | ✅ | ⚠ |
Change Detection | ✅ | ❌ |
Enterprise Support
Both Speakeasy and Stainless offer support for Enterprise customers. This includes features like concierge onboarding, private Slack channels, and enterprise SLAs.
Feature | Speakeasy | Stainless |
---|---|---|
Concierge onboarding | ✅ | ✅ |
Private Slack channel | ✅ | ✅ |
Enterprise SLAs | ✅ | ✅ |
User issues triage | ✅ | ✅ |
Pricing
In terms of pricing, both Speakeasy and Stainless offer free plans, as well as paid plans for startups and enterprises.
The most significant pricing difference is the enterprise plan. Existing customers indicate that Stainless' custom pricing is pricier than Speakeasy's, with Stainless averaging 20% higher. Of course this can vary, and we recommend reaching out to both companies for a quote.
Plan | Speakeasy | Stainless |
---|---|---|
Free | 1 free Published SDK | 1 free local SDK; max 50 endpoints |
Startup | 1 free + $250/mo/SDK; max 50 endpoints | $250/mo/SDK; max 50 endpoints |
Business | N/A | $2,500/mo; max 5 SDKs; 150 endpoints |
Enterprise | Custom | Custom |
Speakeasy vs. Stainless Technical Walkthrough
To start our technical comparison, let's create an SDK using Speakeasy and Stainless. We'll create an OpenAPI document for a fictional bookstore API, that covers a broad range of OpenAPI functionality. You can find the complete OpenAPI document in the example repository (opens in a new tab), but let's take a look at what's included.
Our bookstore OpenAPI document is compliant with t OpenAPI 3.1, which is supported by both Speakeasy and Stainless. We define a basic info section and add a single development server.
Here we define two tags to organize our operations with: Books
and Orders
.
We define one global authentication method, apiKey
.
Let's take a look at the operations we'll need an SDK for, starting with getAllBooks
.
This operation takes no input.
What makes this operation interesting is that it returns an array of objects of three types: ProgrammingBook
, FantasyBook
, and SciFiBook
. Each object's type is determined by the book's category.
This example allows us to test how our SDK generators handle discriminated unions in OpenAPI.
Next up, we have an operation that adds a book to the database, called addBook
.
This operation takes one object of type ProgrammingBook
, FantasyBook
, or SciFiBook
as input.
Our next book-related operation, updateBookCoverById
, takes a book ID as a path variable, and an image as a binary payload.
We include this operation to test how our SDK generators handle binary payloads.
Our final book-related operation, getBookById
, takes a book ID as a path variable, and returns one of our book objects.
Next up, we have an operation that returns a list of all orders in the database, called getAllOrders
.
This operation returns an array of Order
objects, so that we can test an array of nested objects.
Our next order-related operation, createOrder
, takes an object of type NewOrder
as input, and returns an object of type Order
.
We include this one to test how our SDK generators help users avoid common mistakes, like passing the wrong type to an operation.
Finally, we have an operation that returns a stream of order events, called getOrderStream
.
We include this operation to test how our SDK generators handle server-sent events.
The remainder of the OpenAPI document defines the components used in the operations above.
Our bookstore OpenAPI document is compliant with t OpenAPI 3.1, which is supported by both Speakeasy and Stainless. We define a basic info section and add a single development server.
Here we define two tags to organize our operations with: Books
and Orders
.
We define one global authentication method, apiKey
.
Let's take a look at the operations we'll need an SDK for, starting with getAllBooks
.
This operation takes no input.
What makes this operation interesting is that it returns an array of objects of three types: ProgrammingBook
, FantasyBook
, and SciFiBook
. Each object's type is determined by the book's category.
This example allows us to test how our SDK generators handle discriminated unions in OpenAPI.
Next up, we have an operation that adds a book to the database, called addBook
.
This operation takes one object of type ProgrammingBook
, FantasyBook
, or SciFiBook
as input.
Our next book-related operation, updateBookCoverById
, takes a book ID as a path variable, and an image as a binary payload.
We include this operation to test how our SDK generators handle binary payloads.
Our final book-related operation, getBookById
, takes a book ID as a path variable, and returns one of our book objects.
Next up, we have an operation that returns a list of all orders in the database, called getAllOrders
.
This operation returns an array of Order
objects, so that we can test an array of nested objects.
Our next order-related operation, createOrder
, takes an object of type NewOrder
as input, and returns an object of type Order
.
We include this one to test how our SDK generators help users avoid common mistakes, like passing the wrong type to an operation.
Finally, we have an operation that returns a stream of order events, called getOrderStream
.
We include this operation to test how our SDK generators handle server-sent events.
The remainder of the OpenAPI document defines the components used in the operations above.
Linting OpenAPI Documents
Before generating an SDK, it's a good idea to lint your OpenAPI document. This ensures that your document is valid and that your SDK will be generated correctly.
Speakeasy's CLI includes a linter that checks your OpenAPI document for errors and warnings, and provides helpful hints on how to improve your document.
To lint your OpenAPI document, run the following command in the terminal:
Stainless does not currently offer a linter for OpenAPI documents, so you'll need to use an external tool to lint your document locally.
Generating SDKs: Speakeasy CLI vs. Stainless Studio
Speakeasy and Stainless both offer a web interface, but Speakeasy also provides a CLI. The CLI enables easier local experimentation and iteration, custom CI/CD workflows, and consistent onboarding for our users and your team - from your first day with Speakeasy all the way to production.
Stainless Studio, on the other hand, is good for visualizing spec changes and seeing how the changes impact SDKs. Although, in our experience, this type of experimentation is better suited for local development.
Speakeasy's CLI creates sharable links when linting OpenAPI documents, so your team can collaborate remotely right from the terminal.
Let's generate an SDK using each method.
Creating an SDK using Speakeasy CLI
Speakeasy CLI is distributed as a single binary, which you can install directly from GitHub (opens in a new tab), or by using Homebrew on macOS:
To generate an SDK, locate your openapi.yaml file and run the following in the terminal:
Speakeasy lints the OpenAPI document, then creates a new folder with the generated SDK.
This happens locally, and you have immediate access to start testing your SDK. We'll explore the SDK shortly.
Generating an SDK using Stainless Studio
Stainless Studio is a web-based tool that allows you to upload your OpenAPI document and generate an SDK. To generate an SDK, navigate to Stainless Studio (opens in a new tab) and upload your OpenAPI document.
The free Stainless plan allows for one TypeScript SDK targeting Node.
Stainless creates a new repo under the stainless-sdks
organization on GitHub, then invites your GitHub user as a contributor.
We cloned this repository to a local machine to start exploring the SDK.
Setting Up a Mock Server
We used Stoplight Prism (opens in a new tab) to generate a mock server to test our SDKs:
This command starts a mock server at http://localhost:4010
.
TypeScript SDK comparison
Now that we have two TypeScript SDKs generated from a single OpenAPI document, let's see how they differ.
SDK Structure Overview
Before we dive into the detail, let's get an overall view of the default project structure for each SDK.
At a high level, the most obvious differences are:
- Speakeasy does not create SDKs with a particular package manager in mind, while Stainless generates for Yarn (currently,
yarn@1.22.22
), including ayarn.lock
file. - Speakeasy generates detailed documentation for each operation and component, while Stainless generates an empty
examples
folder. - Stainless generates a tests folder, while Speakeasy does not. We'll take a closer look at this shortly.
In the comparison below, comparing the folder structure might seem superficial at first, but keep in mind that SDK users get the same kind of high-level glance as their first impression of your SDK. Some of this may be a matter of opinion, but at Speakeasy we aim to generate SDKs that are as organized as SDKs coded by hand.
Speakeasy SDK Structure
Speakeasy generates separate folders for models and operations, both in the documentation and in the source folder. This indicates a clear separation of concerns.
We also see separate files for each component and operation, indicating modularity and separation of concerns.
Stainless SDK Structure
Stainless generates an SDK that at a glance looks less organized, considering the greater number of configuration files at the root of the project, no separation of models and operations, and a larger number of shims scattered throughout.
SDK Code Comparison
With the bird's-eye view out of the way, let's take a closer look at the code.
Generated Types
Both Speakeasy and Stainless generate TypeScript types, enabling developers to see errors and hints during development. However, Stainless does not generate types for complex OpenAPI component schemas.
For example, consider the following Author
component form our OpenAPI document.
The highlighted anyOf
list is of particular interest. This list states that a valid Author
object must have a name
or an id
, or both. An author with neither a name
nor an id
should not validate against this schema.
Let's take a look at the relevant types generated by each SDK generator, starting with Speakeasy:
Here we see that Speakeasy generates three types for the Author
schema: AuthorWithID
, AuthorWithName
, and a union of these, called Author
. We've highlighted the required fields and the union above.
These types and their names are generated from nothing more than the OpenAPI schema above.
The equivalent type generated by Stainless looks as follows:
Stainless generates an Author
interface for each book type from our OpenAPI document, most likely because the Author
schema is only used in operations related to books, and is only ever referenced as a sub-schema of the Book
schema and its children.
The highlighted lines in the snippet above show how Stainless generates an Author
type with both id
and name
marked as optional.
As a result, the following TypeScript code based on the Stainless will compile without any warnings:
Let's use the same new book object to add a new book using the SDK created by Speakeasy:
Compiling this TypeScript will fail with the following error:
speakeasy-test.ts:11:5 - error TS2322: Type
{}
is not assignable to typeAuthor
.
This is clearly the correct behavior, considering the Author
schema's required fields.
Runtime Type Checking
This brings us to the next type error that should be caught: Runtime type errors.
Speakeasy creates SDKs that are type safe from development to production. As our CEO recently wrote, Type Safe is better than Type Faith (opens in a new tab).
If we add a valid Author
object to our example code, both versions will compile, but only the example based on Speakeasy's SDK works as expected.
The SDK created by Speakeasy uses Zod (opens in a new tab) to validate data at runtime. Data sent to the server and data received from the server are validated against Zod definitions in the client.
This provides safer runtime code execution and helps developers who use your SDK to provide early feedback about data entered by their end users. Furthermore, trusting data validation on the client side allows developers more confidence to build optimistic UIs (opens in a new tab) that update as soon as an end user enters data, greatly improving end users' perception of your API's speed.
Let's see how Speakeasy's runtime type checking works in an example.
Consider the following Book
component from our OpenAPI document:
Book: type: object required: - title - description - price - category - author properties: id: $ref: "#/components/schemas/ProductId" title: type: string example: Clean Code description: type: string example: A Handbook of Agile Software Craftsmanship price: type: integer description: Price in USD cents example: 2999 category: type: string enum: - Sci-fi - Fantasy - Programming example: Programming
The highlighted price
field above has type integer
.
The price
field in the Book
object in our test code is set to 29.99
, which is a floating-point number. This will cause a validation error before the data is sent to the server, as the price
field is expected to be an integer.
Handling Zod validation errors (opens in a new tab) is straightforward, and allows developers to provide meaningful feedback to their end users early in the process.
The same book object in code using the SDK generated by Stainless will only be validated on the server. This means that the error will only be caught from the client's perspective after the data is sent to the server, and the server responds with an error message.
If the server is not set up to validate the price
field, the error will not be caught at all, leading to unexpected behavior in your developer-users' applications.
As a result, developers using the SDK generated by Stainless may need to write additional client-side validation code to catch these errors before they are sent to the server.
Dependency Injection: SDK Hooks vs. Custom Code
While we're on the topic of adding custom code to generated SDKs: Speakeasy generates a clean mechanism for safely injecting custom code.
The abridged code below is from the SDK generated by Speakeasy:
The types above are well documented, but you can read more about Speakeasy SDK Hooks in Speakeasy's documentation.
In short, hooks are typed and contain relevant context depending on when in the lifecycle they are applied. To add hooks, register hooks in the src/hooks/registration.ts
file in your TypeScript SDK.
Here's an example hook:
Speakeasy also provides a clean abstraction to add dependencies to the SDK, by specifying dependencies in the SDK's gen.yaml file:
In contrast, Stainless documents the process of patching (opens in a new tab) generated SDKs with custom code, and provides strategies to avoid or resolve merge conflicts.
Patching generated code with custom code is a recipe for maintenance headaches and delayed SDK releases. We much prefer the clean separation afforded by hooks.
OAuth Client Credentials Handling
Both Speakeasy and Stainless generate SDKs that handle OAuth 2.0 with client credentials. However, only Speakeasy's SDKs handle the token lifecycle, retries, and error handling without any additional code.
Our bookstore API requires an OAuth 2.0 token with client credentials to access the API. Let's see how the SDKs handle this.
Consider the following OAuth 2.0 configuration from our OpenAPI document:
Speakeasy's generated SDK takes a clientID
and clientSecret
when instantiating the SDK. The SDK also includes ClientCredentialsHook
class that implements BeforeRequestHook
to check whether the token is expired and refresh it if necessary. The hook also checks whether the client has the necessary scopes to access the endpoint, and handles authentication errors.
The SDK generated by Stainless includes the same authentication fields, but it does not handle the token lifecycle, retries, or error handling. This means that developers using the SDK generated by Stainless will need to write additional code to handle these scenarios.
Server-Sent Events (SSE) and Streaming Responses
Our bookstore API includes an operation that streams orders to the client using Server-Sent Events (SSE).
paths: /orderstream: get: summary: Get a stream of orders operationId: getOrderStream description: Returns a stream of orders tags: - Orders security: - apiKey: [] responses: '200': description: A stream of orders content: text/event-stream: schema: $ref: '#/components/schemas/OrderStreamMessage'
Let's see how the SDKs handle this.
Speakeasy generates types and methods for handling SSE without any customization. Here's an example of how to use the SDK to listen for new orders:
(The example above does not run against a local Prism server, but you can test it against Stoplight's hosted Prism (opens in a new tab) server.)
Stainless does not generate SSE handling code, but offers to configure SSE handling (opens in a new tab) in the SDK for enterprise customers.
Streaming Uploads
Both Speakeasy and Stainless support streaming uploads without any custom configuration. OpenAPI operations with multipart/form-data
content types are automatically handled as streaming uploads.
The following example illustrates how to use an SDK created by Speakeasy to upload a large file:
Discriminated Unions
Our OpenAPI document includes a Book
component with a category
field that can be one of three values: Programming
, Fantasy
, or SciFi
.
This allows us to type the Book
component in requests and responses as specific book types, such as ProgrammingBook
, FantasyBook
, and SciFiBook
.
OpenAPI supports discriminated unions using the discriminator
field in the schema. Here's an example of a response that returns an array of books of different types:
Let's see how the SDKs handle this.
Speakeasy generates TypeScript types for each book type, and uses a discriminated union to handle the different book types. This enables developers to use the correct type when working with books of different categories. This pattern could just as easily apply to payment methods or delivery options.
The example below shows how Speakeasy defines the ProgrammingBook
type. It also generates types for FantasyBook
and SciFiBook
.
In this example, you'll notice that the category
field is optional in the ProgrammingBook
type, but is enforced by Zod validation in the SDK.
We can see how Speakeasy generates SDK code to handle the different book types in the response for the getgetAllBooks
operation:
Note how the array elements in responseBodies
are typed according to the book category.
This may seem like a trivial example, but it illustrates how Speakeasy generates types that are more specific and easier to work with than the types generated by Stainless. This could, for instance, help developers correctly handle different book types in their applications.
Stainless does not generate types for discriminated unions, and developers must manually handle the different book types in the response.
Here is the equivalent type definition generated by Stainless:
Discriminating between different book types in the response is left to the developer using the SDK.
OpenAPI Extensions and Overlays vs. Stainless Config
Speakeasy embraces OpenAPI as the source of truth for generating SDKs. This means that Speakeasy does not require any additional configuration files to generate SDKs, apart from minimal configuration in the gen.yaml
file.
Any configuration related to individual operations or components is done in the OpenAPI document itself, using OpenAPI extensions. Speakeasy provides a list of supported OpenAPI extensions in its documentation.
If editing your OpenAPI document is not an option, Speakeasy also supports the OpenAPI Overlays specification, which allows you to add or override parts of an OpenAPI document without modifying the original document.
This step can form part of your CI/CD pipeline, ensuring that your SDKs are always up-to-date with your API, even if your OpenAPI document is generated from code.
Speakeasy's CLI can also generate OpenAPI overlays for you, based on the differences between two OpenAPI documents.
Instead of using OpenAPI extensions, Stainless uses a configuration DSL (opens in a new tab) to customize SDKs. This configuration overrides many of the aspects Speakeasy allows you to configure in the OpenAPI document itself.
SDK and Bundle Size
By relying heavily on abstractions, SDKs generated by Stainless tend to have smaller code bases at the expense of bundle sizes.
Additionally, Speakeasy SDKs include runtime data validation, which can increase the bundle size. However, Speakeasy SDKs are designed to be tree-shakable, so you can remove any unused code from the SDK before bundling it.
Creating Bundles
Let's compare the bundle sizes of the SDKs generated by Speakeasy and Stainless.
Start by adding a src/speakeasy.ts
file that imports the Speakeasy SDK:
Next, add a src/stainless.ts
file that imports the Stainless SDK:
We'll use esbuild
to bundle the SDKs. First, install esbuild
:
npm install esbuild
Next, add a build.ts
script that uses esbuild
to bundle the SDKs:
Run the build.ts
script:
npx ts-node build.ts
This generates two bundles, dist/speakeasy.js
and dist/stainless.js
, along with their respective metafiles.
Bundle Size Comparison
Now that we have two bundles, let's compare their sizes.
First, let's look at the size of the dist/speakeasy.js
bundle:
du -sh dist/speakeasy.js# Output# 132K dist/speakeasy.js
Next, let's look at the size of the dist/stainless.js
bundle:
du -sh dist/stainless.js# Output# 476K dist/stainless.js
Despite lacking runtime data validation, the bundle built with the SDK generated by Stainless is significantly larger than that built with the SDK generated by Speakeasy.
We can use the metafiles generated by esbuild
to analyze the bundle sizes in more detail.
Analyzing Bundle Sizes
The metafiles generated by esbuild
contain detailed information about which source files contribute to each bundle's size, presented as a tree structure.
We used esbuild's online bundle visualizer (opens in a new tab) to analyze the bundle sizes.
Here's a summary of the bundle sizes:
The dist/speakeasy.js
bundle's largest contributor, at 41.6%, is the Zod library used for runtime data validation. The Zod library's tree-shaking capabilities are a work in progress, and future versions of SDKs are expected to have smaller bundle sizes.
The dist/stainless.js
bundle's largest contributor, at 53.8%, is tr46
, a library for converting between Unicode and ASCII, followed by web streaming polyfills - neither of which are used in the bundle we created.
Bundling for the Browser
Speakeasy SDKs are designed to work in a range of environments, including the browser. To bundle an SDK for the browser, you can use a tool like esbuild
or webpack
.
Here's an example of how to bundle the Speakeasy SDK for the browser using esbuild
:
npx esbuild src/speakeasy.ts --bundle --minify --target=es2020 --platform=browser --outfile=dist/speakeasy.js
Doing the same for the Stainless SDK generates an error, as the SDK is not designed to work in the browser out of the box.
Python SDK Comparison
In this section, we'll compare Python SDKs generated by Speakeasy and Stainless.
We'll include examples from Python SDKs based on the same OpenAPI document we used for the TypeScript SDKs.
In terms of similarities, both Speakeasy and Stainless generate SDKs that use httpx
as the HTTP client, and Pydantic for data modeling.
Type Safety
As with TypeScript, the Python SDK generated by Stainless does not guarantee type safety at runtime. Stainless relies more on Python's built-in type annotations, while Speakeasy uses Pydantic V2 for a richer type system.
Stainless often uses @overload
to create SDK methods that accept objects of multiple types instead of accepting a union as Speakeasy does. This makes model definitions extremely verbose, without any added benefit. These overloads also don't bring the runtime type safety that Pydantic does.
Speakeasy has a close working relationship with the Pydantic team. By adding to Pydantic's type system, Speakeasy avoids wrapping Pydantic in custom models where possible.
Pydantic Models as Input
Where Stainless SDKs accept only TypeDict
as method inputs, Speakeasy creates methods that accept both TypeDict
and Pydantic models. This simplifies passing responses from one method to inputs of another method, which greatly improves chainability and application code readability. Less manual type wrangling leads to a far better developer experience.
In the example below, the SDK created by Speakeasy accepts a union request type, Union[operations.AddBookRequestBody, operations.AddBookRequestBodyTypedDict]
.
The equivalent method in the SDK generated by Stainless is too verbose to add here in its entirety, but the example below is one of the overloaded methods:
Cleaner Interfaces
As the example above shows, Stainless generates methods that expose extra_headers
, extra_query
, and extra_body
parameters. By contrast, Speakeasy exposes only the defined OpenAPI properties to users, without forcing the user to think about whether parameters will be sent in the header, query, or body of the request.
From a user's perspective, Speakeasy provides a much cleaner interface, while minimizing the possibility of drift between generated SDKs and documentation through ad-hoc and potentially undocumented parameters.
Documentation
As it does for the TypeScript SDK, Speakeasy creates detailed markdown documentation for each method and model in the Python SDK, along with working examples that users can copy and paste. Stainless only generates a single README.md
file and an empty examples folder.
Speakeasy also generates usage examples that you can include in your documentation platform. Speakeasy currently supports Mintlify and Redocly, with more platform support planned. Through Speakeasy's integrations with documentation platforms, your usage examples will be updated automatically when your API and SDK change.
Positional Arguments
Both Speakeasy and Stainless disallow positional arguments in SDK methods by prepending a wildcard to each method's signature. This ensures that SDK methods don't break when the API specification changes or parameters are reordered.
Linting and Change Detection
Speakeasy's CLI includes a detailed and accurate linter that checks your OpenAPI document and provides feedback. This is especially useful during development, but can also catch errors in your CI/CD pipeline.
Speakeasy also keeps track of changes in your OpenAPI document, and versions the SDKs it creates based on changes.
Automation and CI/CD
Speakeasy's CLI, a single binary, is designed to be used in CI/CD pipelines, and can be used to automate the generation of SDKs based on changes in your OpenAPI document.
While Stainless also supports CI/CD, the lack of a CLI means that you'll need to use the web interface to generate SDKs, which may not be as convenient for automation - especially if you want to test generated SDKs locally before committing changes.
Speakeasy Compared to Open Source Generators
If you are interested in seeing how Speakeasy stacks up against other SDK generation tools, check out our post.