In Depth: Speakeasy vs APIMatic
Nolan Sullivan
September 30, 2024
At Speakeasy, we create idiomatic SDKs in the most popular languages. Our generators follow principles that ensure we create SDKs that offer the best developer experience so that you can focus on building your API, and your developer-users can focus on delighting their users.
In this post, we'll compare TypeScript SDKs created by Speakeasy to those generated by APIMatic.
TL;DR? Here's what we found:
- Ease of installation: The Speakeasy CLI has a straightforward installation process, especially for macOS users, with a single dependency-free binary. APIMatic, while also straightforward, requires Node.js and is installed using npm, which might add complexity for some users.
- Code generation: In our test, the SDK generated by APIMatic via CLI had issues with the
src/clientInterface.ts
file. Although the APIMatic support team responded, the problem persisted in our Docker environment test. By contrast, Speakeasy generated a complete and error-free SDK on our first attempt. - Documentation and code structure: The Speakeasy-created SDK includes comprehensive documentation, and relies on Zod for runtime data parsing, making it easier for developers to understand and use. The structure and organization of the SDK were clear and intuitive.
- Developer experience and flexibility: Speakeasy seems to place a significant emphasis on the developer experience, offering more customization options and a focus on the API-Developer Experience (DevEx). This could be particularly beneficial for teams looking for greater control over their SDKs.
- Response to issues: While the APIMatic support team was responsive, the resolution provided did not address the issue effectively in our case. Since we encountered no issues creating an SDK with Speakeasy, there was no need to similarly test the Speakeasy support team's responsiveness.
- Bundle sizes and browser compatibility: Even when including Zod for runtime type checking, bundles using Speakeasy's SDKs are smaller than those created with APIMatic SDKs. This could be beneficial for developers who need to keep their bundle sizes small. Speakeasy's SDKs are also compatible with modern browsers, which is essential for web developers.
NOTE
Of course, individual experiences may vary based on specific needs and use cases, so you might want to follow our process below to test-drive both tools.
Comparing Speakeasy and APIMatic
Before we get into the technical walkthrough, let's see whether each platform targets the languages your users require, offers features you want, and provides the support you can rely on.
SDK Generation Targets
At Speakeasy, we believe it is crucial to meet your users where they are by supporting SDKs in languages your users depend on. Anyone who has had to maintain custom SDK code because a vendor doesn't support their tech stack knows how frustrating this can be.
This table shows the current, as of September 2024, languages and platforms targeted by Speakeasy and APIMatic. These lists will change over time, so check the official documentation for the latest language support.
Language | Speakeasy | APIMatic |
---|---|---|
Python | ✅ | ✅ |
TypeScript | ✅ | ✅ |
Go | ✅ | ✅ (Alpha) |
C# (.NET) | ✅ | ✅ |
PHP | ✅ | ✅ |
Ruby | ✅ | ✅ |
Java | ✅ | ✅ |
Kotlin | ⚠ Java is Kotlin-compatible | ❌ |
Terraform provider | ✅ | ❌ |
Swift | ✅ | ❌ |
Unity | ✅ | ❌ |
Postman Collection | ✅ | ❌ |
We're always open to expanding our language support, but would only ever do this if we have the in-house experience to create idiomatic, best-in-class SDKs for a given language. Let us know (opens in a new tab) if you would like to suggest a language or platform to support.
SDK Features
The table below compares the current SDK features offered by Speakeasy and APIMatic as of September 2024. Both Speakeasy and APIMatic are under active development, so these features may change over time.
Feature | Speakeasy | APIMatic |
---|---|---|
Union types | ✅ | ✅ |
Discriminated union types | ✅ | ⚠ non-OpenAPI standard |
Server-sent events | ✅ | ❌ |
Retries | ✅ | ✅ |
Pagination | ✅ | ❌ |
Async support | ✅ | ✅ |
Streaming uploads | ✅ | ❌ |
OAuth 2.0 | ✅ | ✅ |
Custom SDK naming | ✅ | ✅ |
Customize SDK structure | ✅ | ❌ |
Custom dependency injection | ✅ | ❌ |
APIMatic lacks advanced SDK customization features, and we couldn't find any code or documentation related to pagination. These are features Speakeasy's users rely on.
Platform Features
Speakeasy's primary interface is an open-source, full-featured, and portable CLI. Developers use our CLI to experiment and iterate locally and to customize their CI/CD workflows.
APIMatic's CLI depends on Node.js and several packages. This makes it much less portable. In testing, we also found that it does not generate SDKs as reliably as the APIMatic web interface.
Feature | Speakeasy | APIMatic |
---|---|---|
GitHub CI/CD | ✅ | ✅ |
CLI | ✅ | ⚠ |
Web interface | ✅ | ✅ |
Package publishing | ✅ | ✅ |
OpenAPI linting | ✅ | ✅ |
Documentation generation | ✅ | ✅ |
Test generation | ✅ | ✅ |
OpenAPI overlays | ✅ | ❌ |
Change detection | ✅ | ❌ |
Developer portal | ❌ | ✅ |
Enterprise Support
Both Speakeasy and APIMatic offer support for Enterprise customers. This includes features like concierge onboarding, private Slack channels, and enterprise SLAs.
Feature | Speakeasy | APIMatic |
---|---|---|
Concierge onboarding | ✅ | ✅ |
Private Slack channel | ✅ | ✅ |
Enterprise SLAs | ✅ | ✅ |
User issues triage | ✅ | ✅ |
Pricing
Speakeasy offers a free plan, while APIMatic offers a limited free trial.
Plan | Speakeasy | APIMatic |
---|---|---|
Free | 1 free Published SDK, 50 endpoints | Trial only |
Startup | 1 free + $250/mo/SDK, 50 endpoints each | N/A |
Lite: Starter | N/A | $15/mo. 1 API, 10 endpoints, no team members. |
Lite: Basic | N/A | Custom. 1 API, 20 endpoints, 2 team members. |
Business | N/A | Custom. Up to 50 APIs, 100 endpoints each, 15 team members. |
Enterprise | Custom | Custom |
Speakeasy's free plan is more generous than both Lite plans offered by APIMatic.
Speakeasy vs APIMatic Technical Walkthrough
Let's create SDKs with Speakeasy and APIMatic from a single API specification, to compare the output and customization features.
We've created an OpenAPI document that describes a fictional bookstore API. 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 OpenAPI 3.1, which is supported by both Speakeasy and APIMatic. 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 examine 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 OpenAPI 3.1, which is supported by both Speakeasy and APIMatic. 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 examine 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.
We'll save this as openapi.yaml
in the root of our test repository.
Installing the APIMatic CLI
The APIMatic CLI depends on Node.js, and we'll install it using npm. In the terminal, run:
npm install -g @apimatic/cli
This will install the APIMatic CLI in your global node_modules
folder and create the apimatic
command.
On our test environment, this installed 250 npm packages, 17 of which were deprecated.
Check your APIMatic CLI version:
apimatic --version# @apimatic/cli/1.1.0-alpha.5 darwin-arm64 node-v20.17.0
Authenticate with APIMatic by running:
apimatic auth:login
Then enter your APIMatic email address and password.
The APIMatic CLI is also open source (opens in a new tab), with the latest update in September 2023.
Installing the Speakeasy CLI
To install the Speakeasy CLI, we'll follow the Speakeasy Getting Started (opens in a new tab) guide.
-
Create an account on Speakeasy (opens in a new tab).
-
Install the Speakeasy CLI using Homebrew or cURL:
brew install speakeasy-api/tap/speakeasyor
curl -fsSL https://raw.githubusercontent.com/speakeasy-api/speakeasy/main/install.sh | sh -
Authenticate the CLI with Speakeasy:
speakeasy auth login
You can check the Speakeasy version:
speakeasy --version# speakeasy version 1.390.5# darwin_arm64
Linting OpenAPI Documents
Both Speakeasy and APIMatic can validate OpenAPI documents.
Validate openapi.yaml
using APIMatic:
apimatic api:validate --file=openapi.yaml# Validating specification file... done# Info: One or more elements in the API specification has a missing description. (View Details)Source: API.# ...# Specification file provided is valid
Speakeasy goes beyond validation by linting an OpenAPI document, and then providing separate errors, warnings, and hints. This includes a link to a shareable lint report for easier collaboration.
speakeasy lint openapi -s openapi.yaml
Both platforms validated our OpenAPI document without errors, so let's move on to generating SDKs.
Generating an SDK Using the APIMatic CLI Tutorial
We'll follow the APIMatic tutorial (opens in a new tab) to generate a TypeScript SDK.
In the terminal, run:
apimatic sdk:generate --platform=typescript --file="openapi.yaml"
This should print the following to the terminal:
Generating SDK... doneDownloading SDK... doneSuccess! Your SDK is located at ~/speakeasy-apimatic-comparison/openapi_sdk_typescript
Inspecting the APIMatic-Generated SDK
To see what was generated, run the tree command from the new SDK directory:
tree openapi_sdk_typescript
Poking around the source, we found that the generated SDK did not contain any model code or types related to our bookstore example.
Corrupted SDK Generation
In a previous test from January 2024, we used the standard OpenAPI Petstore example API to generate an SDK using APIMatic. In that case, the SDK also did not contain any models, and we encountered a bizarre error - the src/clientInterface.ts
file seems to have been generated incorrectly. Here's what we found:
The file seems to start in the middle, then wraps around.
This error gave us an opportunity to engage with the APIMatic support team, so we'll take a brief detour and share our experience: After seven days of filing the bug report, we received a curt, "We've tested out the SDKs via the CLI method and they're being generated as expected." To be fair, the support agent did offer to provide further assistance if we still had a problem.
As any responsible tester would do, we decided to isolate the test environment from our system to be sure the issue didn't stem from an error on our side. We're using nvm on macOS after all, and issues can crop up when switching between Node versions.
We created a Dockerfile to install the requirements and generate an SDK:
FROM node:latestARG apimatic_auth_key=APIMATIC_KEYRUN mkdir -p /usr/src/appWORKDIR /usr/src/appCOPY petstore.yaml /usr/src/appRUN npm install -g @apimatic/cliRUN apimatic auth:login --auth-key=$apimatic_auth_keyRUN apimatic sdk:generate --platform=typescript --file="petstore.yaml"RUN cat petstore_sdk_typescript/src/clientInterface.ts
Replace $APIMATIC_KEY
with your APIMatic API key, then run:
docker build -t apimatic-petstore --build-arg apimatic_auth_key=$APIMATIC_KEY --progress plain .
On a second run with Docker, the bug appeared to fix itself, only to fail again later.
Sure enough, the result was the same - the src/clientInterface.ts
file starts in the middle and wraps around. This might be caused by a race condition in the code that downloads and unzips the SDK from APIMatic. Tempting as it is to track this error down, we have SDKs to generate, so we'll move on.
If you're following along, remember to delete your Docker image, as it contains your API key. The key also appears in your Docker history.
In the terminal, run:
docker image rm apimatic-petstore
In both our Petstore and Bookstore examples, the APIMatic CLI failed to generate a usable SDK.
We need to find another way to generate an SDK using APIMatic - let's try the web app.
Generating an SDK Using the APIMatic UI
Log in to the APIMatic web application and follow the prompts to import our openapi.yaml
document as a new API.
Click on Generate and select TypeScript. This generates a TypeScript SDK, which downloads to your computer as a zip file. We'll save this in our working directory as bookstore-sdk-apimatic
.
Here's what's inside:
This looks more complete, and none of the files are corrupted, so we'll move on to trying the Speakeasy generator.
Create an SDK Using the Speakeasy CLI
To generate an SDK using Speakeasy, run the following in the terminal:
Speakeasy lints the OpenAPI document, then creates a new folder, bookstore-sdk-speakeasy
, with the generated 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
.
SDK Code Comparison
Now that we have two SDKs, let's compare the code generated by each platform.
Runtime Type Checking
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).
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 the 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 APIMatic 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 APIMatic may need to write additional client-side validation code to catch these errors before they are sent to the server.
Dependency Injection: SDK Hooks
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:
Dependency injection and SDK customization are not well documented for APIMatic. Of course, developers can patch the generated SDKs as much as they want, but mixing generated and custom code is often a recipe for disaster.
OAuth Client Credentials Handling
Both SDKs handle OAuth 2.0 with client credentials.
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:
The SDK generated by Speakeasy 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 APIMatic has similar functionality.
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.)
APIMatic does not generate SSE-handling code.
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 APIMatic. This could, for instance, help developers correctly handle different book types in their applications.
APIMatic 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 APIMatic:
Following the CategoryEnum
import:
Discriminating between different book types in the response is left to users.
OpenAPI Overlays
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.
SDK and Bundle Size
Let's compare the bundle sizes of the SDKs generated by Speakeasy and APIMatic.
Start by adding a sdk-tests/speakeasy.ts
file that imports the Speakeasy SDK:
Next, add a sdk-tests/apimatic.ts
file that imports the APIMatic SDK:
Running the code above generates a validation error, due to the lack of discriminated unions in the SDK generated by APIMatic. This won't affect the bundle size, though.
We'll use esbuild
to bundle the SDKs. First, install esbuild
:
npm install esbuild
Next, add a sdk-tests/build.js
script that uses esbuild
to bundle the SDKs:
Run the build.js
script:
node build.ts
This generates two bundles, dist/speakeasy.cjs
and dist/apimatic.cjs
, 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.cjs
bundle:
du -sh dist/speakeasy.cjs# Output# 128K dist/speakeasy.cjs
Next, let's look at the size of the dist/apimatic.cjs
bundle:
du -sh dist/apimatic.cjs# Output# 360K dist/apimatic.cjs
Despite lacking runtime data validation, the bundle built with the SDK generated by APIMatic 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.cjs
bundle's largest contributor, at 43.4%, 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/apimatic.cjs
bundle's largest contributor, at 37.4%, is mime-db
, a "large database of mime types and information about them" (mime-db on npm (opens in a new tab)).
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 speakeasy.ts --bundle --minify --target=es2020 --platform=browser --outfile=dist/speakeasy.js
Doing the same for the APIMatic SDK generates an error, as the SDK is not designed to work in the browser out of the box.
npx esbuild apimatic.ts --bundle --minify --target=es2020 --platform=browser --outfile=dist/apimatic.js# ✘ [ERROR] Could not resolve "stream"
Linting and Change Detection
Speakeasy keeps track of changes in your OpenAPI document, and versions the SDKs it creates based on 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.