# speakeasy-vs-apimatic
Source: https://speakeasy.com/blog/speakeasy-vs-apimatic


import { Table } from "@/mdx/components";

At Speakeasy, we create [idiomatic SDKs](/docs/sdk-design/intro) 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.

### 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.

<Table
  title="SDK Generation Support"
  columns={[
    { key: "language", header: "Language" },
    { key: "speakeasy", header: "Speakeasy" },
    { key: "apimatic", header: "APIMatic" }
  ]}
  data={[
    { language: "Python", speakeasy: "✅", apimatic: "✅" },
    { language: "TypeScript", speakeasy: "✅", apimatic: "✅" },
    { language: "Go", speakeasy: "✅", apimatic: "✅ (Alpha)" },
    { language: "C# (.NET)", speakeasy: "✅", apimatic: "✅" },
    { language: "PHP", speakeasy: "✅", apimatic: "✅" },
    { language: "Ruby", speakeasy: "✅", apimatic: "✅" },
    { language: "Java", speakeasy: "✅", apimatic: "✅" },
    { language: "Kotlin", speakeasy: "⚠ Java is Kotlin-compatible", apimatic: "❌" },
    { language: "Terraform provider", speakeasy: "✅", apimatic: "❌" },
    { language: "Unity", speakeasy: "✅", apimatic: "❌" },
    { language: "Postman Collection", speakeasy: "✅", apimatic: "❌" }
  ]}
  rowKeyField="language"
/>

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](/roadmap) 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.

<Table
  title="SDK Features"
  columns={[
    { key: "feature", header: "Feature" },
    { key: "speakeasy", header: "Speakeasy" },
    { key: "apimatic", header: "APIMatic" }
  ]}
  data={[
    { feature: "Union types", speakeasy: "✅", apimatic: "✅" },
    { feature: "Discriminated union types", speakeasy: "✅", apimatic: "⚠ non-OpenAPI standard" },
    { feature: "Server-sent events", speakeasy: "✅", apimatic: "❌" },
    { feature: "Retries", speakeasy: "✅", apimatic: "✅" },
    { feature: "Pagination", speakeasy: "✅", apimatic: "❌" },
    { feature: "Async support", speakeasy: "✅", apimatic: "✅" },
    { feature: "Streaming uploads", speakeasy: "✅", apimatic: "❌" },
    { feature: "OAuth 2.0", speakeasy: "✅", apimatic: "✅" },
    { feature: "Custom SDK naming", speakeasy: "✅", apimatic: "✅" },
    { feature: "Customize SDK structure", speakeasy: "✅", apimatic: "❌" },
    { feature: "Custom dependency injection", speakeasy: "✅", apimatic: "❌" }
  ]}
  rowKeyField="feature"
/>

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.

<Table
  title="Platform Features"
  columns={[
    { key: "feature", header: "Feature" },
    { key: "speakeasy", header: "Speakeasy" },
    { key: "apimatic", header: "APIMatic" }
  ]}
  data={[
    { feature: "GitHub CI/CD", speakeasy: "✅", apimatic: "✅" },
    { feature: "CLI", speakeasy: "✅", apimatic: "⚠" },
    { feature: "Web interface", speakeasy: "✅", apimatic: "✅" },
    { feature: "Package publishing", speakeasy: "✅", apimatic: "✅" },
    { feature: "OpenAPI linting", speakeasy: "✅", apimatic: "✅" },
    { feature: "Documentation generation", speakeasy: "✅", apimatic: "✅" },
    { feature: "Test generation", speakeasy: "✅", apimatic: "✅" },
    { feature: "OpenAPI overlays", speakeasy: "✅", apimatic: "❌" },
    { feature: "Change detection", speakeasy: "✅", apimatic: "❌" },
    { feature: "Developer portal", speakeasy: "❌", apimatic: "✅" }
  ]}
  rowKeyField="feature"
/>

### Enterprise Support

Both Speakeasy and APIMatic offer support for Enterprise customers. This includes features like concierge onboarding, private Slack channels, and enterprise SLAs.

<Table
  title="Enterprise Support"
  columns={[
    { key: "feature", header: "Feature" },
    { key: "speakeasy", header: "Speakeasy" },
    { key: "apimatic", header: "APIMatic" }
  ]}
  data={[
    { feature: "Concierge onboarding", speakeasy: "✅", apimatic: "✅" },
    { feature: "Private Slack channel", speakeasy: "✅", apimatic: "✅" },
    { feature: "Enterprise SLAs", speakeasy: "✅", apimatic: "✅" },
    { feature: "User issues triage", speakeasy: "✅", apimatic: "✅" }
  ]}
  rowKeyField="feature"
/>

### Pricing

Speakeasy offers a free plan, while APIMatic offers a limited free trial.

<Table
  title="Pricing"
  columns={[
    { key: "plan", header: "Plan" },
    { key: "speakeasy", header: "Speakeasy" },
    { key: "apimatic", header: "APIMatic" }
  ]}
  data={[
    { plan: "Free", speakeasy: "1 target; max 250 endpoints", apimatic: "Trial only" },
    { plan: "Startup", speakeasy: "N/A", apimatic: "N/A" },
    { plan: "Lite: Starter", speakeasy: "N/A", apimatic: "$15/mo. 1 API, 10 endpoints, no team members." },
    { plan: "Lite: Basic", speakeasy: "N/A", apimatic: "$115/mo. 1 API, 20 endpoints, 2 team members." },
    { plan: "Business", speakeasy: "$600/mo; max 250 endpoints", apimatic: "Custom. Up to 50 APIs, 100 endpoints each, 15 team members." },
    { plan: "Enterprise", speakeasy: "Custom", apimatic: "Custom" }
  ]}
  rowKeyField="plan"
/>

Speakeasy's free plan is more generous than both Lite plans offered by APIMatic.

## Speakeasy vs APIMatic Technical Walkthrough

### Dependencies and SBOM (Software Bill of Materials)

<Table
  title="Dependencies and SBOM"
  columns={[
    { key: "company", header: "Company" },
    { key: "deps", header: "Total dependencies" },
    { key: "unmaintained", header: "Unmaintained dependencies" },
    { key: "risks", header: "Supply chain risks" }
  ]}
  data={[
    { company: "Speakeasy", deps: "1", unmaintained: "0", risks: "0" },
    { company: "ApiMatic", deps: "41", unmaintained: "7", risks: "4" }
  ]}
  rowKeyField="company"
/>

From day one, Speakeasy has prioritized efficiency, and we've kept the dependency trees for our generated SDKs as lean as possible. For example, here's the [dependency graph for the Vercel SDK](https://npmgraph.js.org/?q=%40vercel%2Fsdk), an SDK generated by Speakeasy. It has zero direct dependencies, and Zod bundled as a regular dependency.

![Vercel dependency graph](/assets/blog/speakeasy-vs-apimatic/vercel.png)

By contrast, here's the [dependency graph for the Maxio SDK](https://npmgraph.js.org/?q=@maxio-com/advanced-billing-sdk), an SDK generated by APIMatic. It has many dependencies, which in turn have transitive dependencies, leading to a much more bloated SDK that is harder to maintain.

![Maxio dependency graph](/assets/blog/speakeasy-vs-apimatic/maxio-dependency-graph.png)

Having more dependencies isn't only bad in terms of efficiency. Many libraries single or no active maintainer. Many dependencies also have unaddressed critical vulnerabilities (CVs), leaving the upstream SDK vulnerable as well ([the Maxio SDK has 4 of these](https://socket.dev/npm/package/@maxio-com/advanced-billing-sdk/alerts/6.1.0?tab=dependencies)).

### Bundle size

<Table
  title="Bundle Size"
  columns={[
    { key: "company", header: "Company" },
    { key: "size", header: "Bundle size" }
  ]}
  data={[
    { company: "Speakeasy", size: "124.1 kb" },
    { company: "ApiMatic", size: "358.1 kb" }
  ]}
  rowKeyField="company"
/>

Dependencies aren't just a security risk. They also bloat the size of your SDK. Here's a comparison of the bundle sizes for libraries generated from the same OpenAPI spec by Speakeasy and APIMatic:

![Speakeasy Bundle Size](/assets/blog/speakeasy-vs-apimatic/speakeasy-bundle-size.png)

![APIMatic Bundle Size](/assets/blog/speakeasy-vs-apimatic/apimatic-bundle-size.png)

That bundle size is a problem if you have customers that need to integrate with your API in runtimes where performance is critical (browser and on the edge).

### Type safety

Speakeasy creates SDKs that are type-safe from development to production. As our CEO recently wrote, [Type Safe is better than Type Faith](/post/type-safe-vs-type-faith).

Speakeasy uses [Zod](https://zod.dev/) 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](https://medium.com/distant-horizons/using-optimistic-ui-to-delight-your-users-ac819a81d59a) that update as soon as an end user enters data, greatly improving end users' perception of your API's speed.

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.

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.

### 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:

```yaml filename="openapi.yaml"
schema:
  type: array
  items:
    oneOf:
      - $ref: "#/components/schemas/ProgrammingBook"
      - $ref: "#/components/schemas/FantasyBook"
      - $ref: "#/components/schemas/SciFiBook"
    discriminator:
      propertyName: category
      mapping:
        Programming: "#/components/schemas/ProgrammingBook"
        Fantasy: "#/components/schemas/FantasyBook"
        Sci-fi: "#/components/schemas/SciFiBook"
```

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.

```typescript filename="speakeasy/books.ts" mark=16,29,50,61
/*
 * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
 */

import { Author, Author$ } from "./author";
import * as z from "zod";

export type ProgrammingBook = {
    id?: number | undefined;
    title: string;
    description: string;
    /**
     * Price in USD cents
     */
    price: number;
    category?: "Programming" | undefined;
    author: Author;
    coverImage?: string | undefined;
};

/** @internal */
export namespace ProgrammingBook$ {
    export const inboundSchema: z.ZodType<ProgrammingBook, z.ZodTypeDef, unknown> = z
        .object({
            id: z.number().int().optional(),
            title: z.string(),
            description: z.string(),
            price: z.number().int(),
            category: z.literal("Programming").optional(),
            author: Author$.inboundSchema,
            cover_image: z.string().optional(),
        })
        .transform((v) => {
            return {
                ...(v.id === undefined ? null : { id: v.id }),
                title: v.title,
                description: v.description,
                price: v.price,
                ...(v.category === undefined ? null : { category: v.category }),
                author: v.author,
                ...(v.cover_image === undefined ? null : { coverImage: v.cover_image }),
            };
        });

    export type Outbound = {
        id?: number | undefined;
        title: string;
        description: string;
        price: number;
        category: "Programming";
        author: Author$.Outbound;
        cover_image?: string | undefined;
    };

    export const outboundSchema: z.ZodType<Outbound, z.ZodTypeDef, ProgrammingBook> = z
        .object({
            id: z.number().int().optional(),
            title: z.string(),
            description: z.string(),
            price: z.number().int(),
            category: z.literal("Programming").default("Programming" as const),
            author: Author$.outboundSchema,
            coverImage: z.string().optional(),
        })
        .transform((v) => {
            return {
                ...(v.id === undefined ? null : { id: v.id }),
                title: v.title,
                description: v.description,
                price: v.price,
                category: v.category,
                author: v.author,
                ...(v.coverImage === undefined ? null : { cover_image: v.coverImage }),
            };
        });
}
```

We can see how Speakeasy generates SDK code to handle the different book types in the response for the `getgetAllBooks` operation:

```typescript filename="speakeasy/getallbooks.ts"
/*
 * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
 */

import * as components from "../components";
import * as z from "zod";

export type ResponseBody =
    | (components.ProgrammingBook & { category: "Programming" })
    | (components.FantasyBook & { category: "Fantasy" })
    | (components.SciFiBook & { category: "Sci-fi" });

export type GetAllBooksResponse = {
    httpMeta: components.HTTPMetadata;
    /**
     * A list of books
     */
    responseBodies?:
        | Array<
              | (components.ProgrammingBook & { category: "Programming" })
              | (components.FantasyBook & { category: "Fantasy" })
              | (components.SciFiBook & { category: "Sci-fi" })
          >
        | undefined;
};

// ...
```

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:

```typescript filename="apimatic/programmingBook.ts"
/**
 * Bookstore APILib
 *
 * This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ).
 */

// ...

import { CategoryEnum, categoryEnumSchema } from './categoryEnum';

// ...

export interface ProgrammingBook {
  id?: number;
  title: string;
  description: string;
  /** Price in USD cents */
  price: number;
  category: CategoryEnum;
  author: Author2;
  coverImage?: string;
}

// ...
```

Following the `CategoryEnum` import:

```typescript filename="apimatic/categoryEnum.ts"
/**
 * Bookstore APILib
 *
 * This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ).
 */

// ...

/**
 * Enum for CategoryEnum
 */
export enum CategoryEnum {
  Scifi = 'Sci-fi',
  Fantasy = 'Fantasy',
  Programming = 'Programming',
}

// ...
```

Discriminating between different book types in the response is left to users.

## 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](/post/compare-speakeasy-open-source).