Generated SDKs that feel hand-crafted

Generated SDKs that feel hand-crafted

Generated SDKs that feel hand-crafted

Automate the generation of truly idiomatic SDKs for TypeScript, Python, Go, and more. Built by the team behind Stripe's code generation, with rich types, auto-pagination, and retries out-of-the-box. With Stainless, your libraries feel as though they were hand-written by an expert in the language who had the time to get it right.

Automate the generation of truly idiomatic SDKs for TypeScript, Python, Go, and more. Built by the team behind Stripe's code generation, with rich types, auto-pagination, and retries out-of-the-box. With Stainless, your libraries feel as though they were hand-written by an expert in the language who had the time to get it right.

Automate the generation of truly idiomatic SDKs for TypeScript, Python, Go, and more. Built by the team behind Stripe's code generation, with rich types, auto-pagination, and retries out-of-the-box. With Stainless, your libraries feel as though they were hand-written by an expert in the language who had the time to get it right.

TypeScript

Python

Go

Java

Kotlin

Ruby

Terraform

C#

PHP

import OpenAI from "openai";
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";

const openai = new OpenAI();

const format = zodTextFormat(
  z.object({ namesOfParticipants: z.array(z.string()) }),
  "namesOfParticipants"
);

const response = await openai.responses.parse({
  model: "gpt-5",
  instructions: "Extract the names of the participants.",
  input: "Alice and Bob are going to a science fair on Friday.",
  text: { format },
});
console.log(response.output_parsed);

TypeScript

Python

Go

Java

Kotlin

Ruby

Terraform

C#

PHP

import OpenAI from "openai";
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";

const openai = new OpenAI();

const format = zodTextFormat(
  z.object({ namesOfParticipants: z.array(z.string()) }),
  "namesOfParticipants"
);

const response = await openai.responses.parse({
  model: "gpt-5",
  instructions: "Extract the names of the participants.",
  input: "Alice and Bob are going to a science fair on Friday.",
  text: { format },
});
console.log(response.output_parsed);

TypeScript

Python

Go

Java

Kotlin

Ruby

Terraform

C#

PHP

import OpenAI from "openai";
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";

const openai = new OpenAI();

const format = zodTextFormat(
  z.object({ namesOfParticipants: z.array(z.string()) }),
  "namesOfParticipants"
);

const response = await openai.responses.parse({
  model: "gpt-5",
  instructions: "Extract the names of the participants.",
  input: "Alice and Bob are going to a science fair on Friday.",
  text: { format },
});
console.log(response.output_parsed);

Stop Choosing Between
Idiomatic and Automated

Stop Choosing
Between Idiomatic
and Automated

Hand-written SDKs are expensive to build and a nightmare to maintain. Most auto-generators produce clunky, unidiomatic wrappers that developers hate.

// Legacy
export class PetApi {
  protected _basePath = defaultBasePath;
  protected defaultHeaders: any = {};
  protected _useQuerystring: boolean = false;

  protected authentications = {
    'default': new VoidAuth(),
    'api_key': new ApiKeyAuth('header', 'api_key'),
    'petstore_auth': new OAuth(),
  }

  public updatePet(body: Pet, options: any = {}): Promise<{ response: http.ClientResponse; body?: any; }> {
    const localVarPath = this.basePath + '/pet';
    let localVarQueryParameters: any = {};
    let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders);
    let localVarFormParams: any = {};

    // verify required parameter 'body' is not null or undefined
    if (body === null || body === undefined) {
      throw new Error('Required parameter body was null or undefined when calling updatePet.');
    }

    (Object).assign(localVarHeaderParams, options.headers);

    let localVarUseFormData = false;

    let localVarRequestOptions: localVarRequest.Options = {
      method: 'PUT',
      qs: localVarQueryParameters,
      headers: localVarHeaderParams,
      uri: localVarPath,
      useQuerystring: this._useQuerystring,
      json: true,
      body: ObjectSerializer.serialize(body, "Pet")
    };

    this.authentications.petstore_auth.applyToRequest(localVarRequestOptions);

    this.authentications.default.applyToRequest(localVarRequestOptions);

    if (Object.keys(localVarFormParams).length) {
      if (localVarUseFormData) {
        (<any>localVarRequestOptions).formData = localVarFormParams;
      } else {
        localVarRequestOptions.form = localVarFormParams;
      }
    }
    return new Promise<{ response: http.ClientResponse; body?: any; }>((resolve, reject) => {
      localVarRequest(localVarRequestOptions, (error, response, body) => {
        if (error) {
          reject(error);
        } else {
          if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) {
            resolve({ response: response, body: body });
          } else {
            reject({ response: response, body: body });
          }
        }
      })

// Legacy
export class PetApi {
  protected _basePath = defaultBasePath;
  protected defaultHeaders: any = {};
  protected _useQuerystring: boolean = false;

  protected authentications = {
    'default': new VoidAuth(),
    'api_key': new ApiKeyAuth('header', 'api_key'),
    'petstore_auth': new OAuth(),
  }

  public updatePet(body: Pet, options: any = {}): Promise<{ response: http.ClientResponse; body?: any; }> {
    const localVarPath = this.basePath + '/pet';
    let localVarQueryParameters: any = {};
    let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders);
    let localVarFormParams: any = {};

    // verify required parameter 'body' is not null or undefined
    if (body === null || body === undefined) {
      throw new Error('Required parameter body was null or undefined when calling updatePet.');
    }

    (Object).assign(localVarHeaderParams, options.headers);

    let localVarUseFormData = false;

    let localVarRequestOptions: localVarRequest.Options = {
      method: 'PUT',
      qs: localVarQueryParameters,
      headers: localVarHeaderParams,
      uri: localVarPath,
      useQuerystring: this._useQuerystring,
      json: true,
      body: ObjectSerializer.serialize(body, "Pet")
    };

    this.authentications.petstore_auth.applyToRequest(localVarRequestOptions);

    this.authentications.default.applyToRequest(localVarRequestOptions);

    if (Object.keys(localVarFormParams).length) {
      if (localVarUseFormData) {
        (<any>localVarRequestOptions).formData = localVarFormParams;
      } else {
        localVarRequestOptions.form = localVarFormParams;
      }
    }
    return new Promise<{ response: http.ClientResponse; body?: any; }>((resolve, reject) => {
      localVarRequest(localVarRequestOptions, (error, response, body) => {
        if (error) {
          reject(error);
        } else {
          if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) {
            resolve({ response: response, body: body });
          } else {
            reject({ response: response, body: body });
          }
        }
      })

Stainless generates client libraries that feel hand-crafted, with idiomatic patterns and type-safety, while offering the speed and consistency of automation.

// Stainless
import { APIResource } from '../core/resource';
import { APIPromise } from '../core/api-promise';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';

export class PetResource extends APIResource {
  update(body: PetUpdateParams, options?: RequestOptions): APIPromise<Pet> {
    return this._client.put('/pet', { body, ...options });
  }
}
// Stainless
import { APIResource } from '../core/resource';
import { APIPromise } from '../core/api-promise';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';

export class PetResource extends APIResource {
  update(body: PetUpdateParams, options?: RequestOptions): APIPromise<Pet> {
    return this._client.put('/pet', { body, ...options });
  }
}

"Stainless' tooling is intuitive, well-documented, and automated, which is essential when you're managing SDKs across multiple languages."

Zeke Sikelianos

Founding Designer - Replicate

"Stainless' tooling is intuitive, well-documented, and automated, which is essential when you're managing SDKs across multiple languages."

Zeke Sikelianos

Founding Designer - Replicate

"Stainless' tooling is intuitive, well-documented, and automated, which is essential when you're managing SDKs across multiple languages."

Zeke Sikelianos

Founding Designer - Replicate

Add any custom code anywhere

Add any custom code anywhere

Add any custom code anywhere

Unlike other codegen systems, you can freely edit the generated code as if it were a normal repo – any change, anywhere in the codebase. Your changes will persist the next time Stainless generates a new version from your OpenAPI spec.

Unlike other codegen systems, you can freely edit the generated code as if it were a normal repo – any change, anywhere in the codebase. Your changes will persist the next time Stainless generates a new version from your OpenAPI spec.

Unlike other codegen systems, you can freely edit the generated code as if it were a normal repo – any change, anywhere in the codebase. Your changes will persist the next time Stainless generates a new version from your OpenAPI spec.

Merged

Add AI helper method : completion generator #8211

Changes from

all commits

File Filter

Conversations

Review changes

...

1

2

3

4




...

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

   @@ -0,0 +6,87 @@ 
   class OpenAI extends Core.APIClient {
     this.project = project;
   }

+  /**
+   * Helper method to generate a completion, summarize it, and create an image based on the summary.
+   */
+  async generateAndVisualize(prompt: string): Promise<string> {
+    try {
+      const completion = await this.completions.create({
+        model: 'text-davinci-003',
+        prompt,
+        max_tokens: 100,
+      });
+      const generatedText = completion?.choices[0]?.text.trim();
+
+      const summary = await this.completions.create({
+        model: 'text-davinci-003',
+        prompt: `Summarize the following text: ${generatedText}`,
+        max_tokens: 50,

Merged

Add AI helper method : completion generator #8211

Changes from

all commits

File Filter

Conversations

Review changes

...

1

2

3

4




...

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

   @@ -0,0 +6,87 @@ 
   class OpenAI extends Core.APIClient {
     this.project = project;
   }

+  /**
+   * Helper method to generate a completion, summarize it, and create an image based on the summary.
+   */
+  async generateAndVisualize(prompt: string): Promise<string> {
+    try {
+      const completion = await this.completions.create({
+        model: 'text-davinci-003',
+        prompt,
+        max_tokens: 100,
+      });
+      const generatedText = completion?.choices[0]?.text.trim();
+
+      const summary = await this.completions.create({
+        model: 'text-davinci-003',
+        prompt: `Summarize the following text: ${generatedText}`,
+        max_tokens: 50,

Merged

Add AI helper method : completion generator #8211

Changes from

all commits

File Filter

Conversations

Review changes

...

1

2

3

4




...

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

   @@ -0,0 +6,87 @@ 
   class OpenAI extends Core.APIClient {
     this.project = project;
   }

+  /**
+   * Helper method to generate a completion, summarize it, and create an image based on the summary.
+   */
+  async generateAndVisualize(prompt: string): Promise<string> {
+    try {
+      const completion = await this.completions.create({
+        model: 'text-davinci-003',
+        prompt,
+        max_tokens: 100,
+      });
+      const generatedText = completion?.choices[0]?.text.trim();
+
+      const summary = await this.completions.create({
+        model: 'text-davinci-003',
+        prompt: `Summarize the following text: ${generatedText}`,
+        max_tokens: 50,

“Proud to have Stainless as a partner at OpenAI.

All our SDKs are generated by them. The team is extremely thoughtful about SDK design and push us to improve our API + other products while they're at it.

Stainless is bringing a new standard of SDKs to our industry and this is a great thing for all developers.”

Nikunj Handa

API Product - OpenAI

“Proud to have Stainless as a partner at OpenAI.

All our SDKs are generated by them. The team is extremely thoughtful about SDK design and push us to improve our API + other products while they're at it.

Stainless is bringing a new standard of SDKs to our industry and this is a great thing for all developers.”

Nikunj Handa

API Product - OpenAI

“Proud to have Stainless as a partner at OpenAI.

All our SDKs are generated by them. The team is extremely thoughtful about SDK design and push us to improve our API + other products while they're at it.

Stainless is bringing a new standard of SDKs to our industry and this is a great thing for all developers.”

Nikunj Handa

API Product - OpenAI

Built for ambitious APIs

Streaming

Streaming

Streaming

Handle Server-Sent Events and raw byte streams with first-class support.

const stream = await client.responses.create({
  model: 'gpt-5',
  input: 'Say "Sheep sleep deep" ten times fast!',
  stream: true,
});

for await (const event of stream) {
  console.log(event);
}

Webhooks

Webhooks

Verify webhook signatures and parse payloads automatically.

app.post('/webhook', async (req, res) => {
  const event = client.webhooks.unwrap(
    req.body.toString(),
    req.headers,
  );
});

File Uploads

File Uploads

Upload files with multipart/form-data, including progress tracking.

client = Groq()
client.audio.transcriptions.create(
    model="whisper-large-v3-turbo",
    file=Path("/path/to/file"),
)
client = Groq()
client.audio.transcriptions.create(
    model="whisper-large-v3-turbo",
    file=Path("/path/to/file"),
)

Pagination

Pagination

Auto-paginating iterators for cursor and offset-based pagination.

async def main() -> None:
    # Iterate through items across all pages, issuing requests as needed.
    async for card in client.cards.list():
        print(card.id)

Request Options

Request Options

Fine-grained control over timeouts, retries, headers, and more.

Lithic client = client.withOptions(optionsBuilder -> {
    optionsBuilder.baseUrl("https://example.com");
    optionsBuilder.maxRetries(42);
});

Type Safety

Type Safety

Catch API errors at compile time with full type definitions.

async function main() {
  const params: SessionCreateParams = { 
    projectId: 'your_project_id' 
  };
  const session: SessionCreateResponse = 
    await client.sessions.create(params);
}

main();

How It Works

Provide your OpenAPI spec

All Stainless needs to get started is your OpenAPI spec. It doesn’t have to be perfect. ‍

We’ll intelligently generate a Stainless config that standardizes naming and defines core functionality like pagination and authentication.

# openapi.yaml
openapi: 3.0.0 
paths: 
  /chat/completions: 
    post: 
      operationId: createChatCompletion 
      tags: 
        - OpenAI 
      summary: Creates a model response for the given chat conversation. 
      requestBody

Generate your SDKs

Choose languages, customize naming conventions, add auth

# stainless.yaml
targets:
  ruby:
    gem_name: taskninja-rb
    production_repo: taskninja/taskninja-rb
    publish:
      rubygems: true
  typescript:
    package_name: taskninja-ts
    production_repo: taskninja/taskninja-ts
    publish:
      npm: true

Iterate and customize

Review generated code, run automated tests, iterate

# stainless.yaml
organization:
  name: taskninja
settings:
  disable_mock_tests: false
client_settings:
  opts:
    api_key:
      type: string
      read_env

Publish

Auto-publish to package managers like npm, PyPI, Maven. Updates via GitHub PRs

Automate updates

Push your updated OpenAPI spec to us whenever it changes with a GitHub Action (or equivalent). We regenerate your SDK and open a pull request.

Maintain

You approve our PR and we automatically release new SDKs to your GitHub repos and package managers like npm, pypi, and maven. You own the code, we automate the toil, and your security team is happy.

# stainless.yaml
languages:
  - typescript
  - python
  - go
  - java
settings:
  typescript:
    package_name: "@mycompany/sdk"
    module_format: "esm"
    target: "ES2022"
  python:
    package_name: "mycompany"
    project_name: "mycompany-python"
auth:
  type: bearer_token
  token_env_var: MY_API_KEY
custom_methods:
  - name: "getImageUrl"
    description: "Helper to get signed image URLs"

Start generating SDKs
in minutes from the CLI


FAQ

Do I need to write any code?

No. Stainless generates complete, production-ready SDKs from your OpenAPI spec. You can add custom methods if needed.

How do updates work?

When your OpenAPI spec changes, Stainless creates a GitHub PR with updated SDK code. Review, merge, and publish.

What if I need to customize something?

The Stainless config file supports extensive customization—naming conventions, auth patterns, custom methods, and more.

Can I use this with my existing API?

Yes. If you have an OpenAPI spec (or can generate one), you can use Stainless.

How much does it cost?

Free to get started. Enterprise plans available. Checkout our plans and pricing for more details.

What languages do you support?

TypeScript, Python, Go, Java, Kotlin, Ruby, Rust, C#. More languages coming soon.

Do I need to write any code?

No. Stainless generates complete, production-ready SDKs from your OpenAPI spec. You can add custom methods if needed.

How do updates work?

When your OpenAPI spec changes, Stainless creates a GitHub PR with updated SDK code. Review, merge, and publish.

What if I need to customize something?

The Stainless config file supports extensive customization—naming conventions, auth patterns, custom methods, and more.

Can I use this with my existing API?

Yes. If you have an OpenAPI spec (or can generate one), you can use Stainless.

How much does it cost?

Free to get started. Enterprise plans available. Checkout our plans and pricing for more details.

What languages do you support?

TypeScript, Python, Go, Java, Kotlin, Ruby, Rust, C#. More languages coming soon.

Do I need to write any code?

No. Stainless generates complete, production-ready SDKs from your OpenAPI spec. You can add custom methods if needed.

How do updates work?

When your OpenAPI spec changes, Stainless creates a GitHub PR with updated SDK code. Review, merge, and publish.

What if I need to customize something?

The Stainless config file supports extensive customization—naming conventions, auth patterns, custom methods, and more.

Can I use this with my existing API?

Yes. If you have an OpenAPI spec (or can generate one), you can use Stainless.

How much does it cost?

Free to get started. Enterprise plans available. Checkout our plans and pricing for more details.

What languages do you support?

TypeScript, Python, Go, Java, Kotlin, Ruby, Rust, C#. More languages coming soon.

Join the companies
building with Stainless

Ship production-ready SDKs in minutes, not months.