mdxui

SDKs

Language-specific client libraries for product integration

SDKs (Software Development Kits)

SDKs are language-specific client libraries that wrap REST APIs and MCP tools, providing idiomatic interfaces for developers. Every product should offer SDKs in popular programming languages to simplify integration.


Why SDKs?

Developer Experience

Without SDKWith SDK
Manual HTTP requestsSimple method calls
JSON parsingTyped objects
Error handlingExceptions
Authentication boilerplateBuilt-in auth
Rate limiting logicAutomatic retries
Pagination complexityIterator/generator

Example Comparison

Without SDK (raw HTTP):

const response = await fetch('https://api.acme.com/v1/products', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'New Product',
    type: 'API',
    business: 'biz_123'
  })
})

if (!response.ok) {
  const error = await response.json()
  throw new Error(error.error.message)
}

const { data } = await response.json()
console.log(data)

With SDK:

const acme = new Acme('sk_live_...')

const product = await acme.products.create({
  name: 'New Product',
  type: 'API',
  business: 'biz_123'
})

console.log(product)

SDK Architecture

Core Components

// Client
class AcmeClient {
  constructor(apiKey: string, options?: ClientOptions)
  products: ProductsResource
  subscriptions: SubscriptionsResource
  apiKeys: APIKeysResource
}

// Resource
class ProductsResource {
  create(params: ProductCreateParams): Promise<Product>
  retrieve(id: string): Promise<Product>
  update(id: string, params: ProductUpdateParams): Promise<Product>
  delete(id: string): Promise<void>
  list(params?: ProductListParams): Promise<PaginatedList<Product>>
}

// Types
interface Product {
  id: string
  name: string
  type: 'SaaS' | 'API' | 'Data' | 'Headless'
  business: string
  // ...
}

Request/Response Flow

SDK Method Call
    ↓
Build HTTP Request (URL, headers, body)
    ↓
Send via HTTP Client
    ↓
Handle Response (parse JSON, errors)
    ↓
Return Typed Object

Language-Specific SDKs

JavaScript/TypeScript

// Installation
npm install @acme/sdk

// Usage
import Acme from '@acme/sdk'

const acme = new Acme('sk_live_...')

// Create product
const product = await acme.products.create({
  name: 'Acme API',
  type: 'API',
  business: 'biz_123'
})

// List products
const products = await acme.products.list({
  type: 'API',
  limit: 20
})

// Iterate through pages
for await (const product of acme.products.listAutoPaginate()) {
  console.log(product.name)
}

// Type safety
const product: Product = await acme.products.retrieve('prod_123')
product.name // string
product.type // 'SaaS' | 'API' | 'Data' | 'Headless'

Python

# Installation
pip install acme-sdk

# Usage
from acme import Acme

acme = Acme(api_key='sk_live_...')

# Create product
product = acme.products.create(
    name='Acme API',
    type='API',
    business='biz_123'
)

# List products
products = acme.products.list(type='API', limit=20)

# Auto-pagination
for product in acme.products.list_auto_paginate():
    print(product.name)

# Type hints
from acme.types import Product

product: Product = acme.products.retrieve('prod_123')
print(product.name)  # str
print(product.type)  # Literal['SaaS', 'API', 'Data', 'Headless']

Go

// Installation
go get github.com/acme/acme-go

// Usage
import "github.com/acme/acme-go"

client := acme.New("sk_live_...")

// Create product
product, err := client.Products.Create(&acme.ProductCreateParams{
    Name:     "Acme API",
    Type:     "API",
    Business: "biz_123",
})
if err != nil {
    return err
}

// List products
params := &acme.ProductListParams{
    Type:  "API",
    Limit: 20,
}
products := client.Products.List(params)

// Iterate
for products.Next() {
    product := products.Product()
    fmt.Println(product.Name)
}
if err := products.Err(); err != nil {
    return err
}

// Type safety
var product *acme.Product
product, err = client.Products.Get("prod_123")

Ruby

# Installation
gem install acme-sdk

# Usage
require 'acme'

acme = Acme::Client.new(api_key: 'sk_live_...')

# Create product
product = acme.products.create(
  name: 'Acme API',
  type: 'API',
  business: 'biz_123'
)

# List products
products = acme.products.list(type: 'API', limit: 20)

# Auto-pagination
acme.products.list_auto_paginate.each do |product|
  puts product.name
end

# Type checking (with Sorbet)
product = T.let(acme.products.retrieve('prod_123'), Product)

PHP

// Installation
composer require acme/sdk

// Usage
use Acme\AcmeClient;

$acme = new AcmeClient('sk_live_...');

// Create product
$product = $acme->products->create([
    'name' => 'Acme API',
    'type' => 'API',
    'business' => 'biz_123',
]);

// List products
$products = $acme->products->list([
    'type' => 'API',
    'limit' => 20,
]);

// Iterate
foreach ($products->autoPagingIterator() as $product) {
    echo $product->name;
}

Java

// Installation (Maven)
<dependency>
  <groupId>com.acme</groupId>
  <artifactId>acme-sdk</artifactId>
  <version>1.0.0</version>
</dependency>

// Usage
import com.acme.Acme;
import com.acme.model.Product;
import com.acme.param.ProductCreateParams;

Acme acme = new Acme("sk_live_...");

// Create product
ProductCreateParams params = ProductCreateParams.builder()
    .setName("Acme API")
    .setType("API")
    .setBusiness("biz_123")
    .build();

Product product = acme.products().create(params);

// List products
ProductListParams listParams = ProductListParams.builder()
    .setType("API")
    .setLimit(20)
    .build();

ProductCollection products = acme.products().list(listParams);

// Iterate
for (Product p : products.autoPagingIterable()) {
    System.out.println(p.getName());
}

SDK Features

Authentication

// API key
const acme = new Acme('sk_live_...')

// OAuth token
const acme = new Acme({
  accessToken: 'oauth_token_...'
})

// Custom auth
const acme = new Acme({
  auth: async (request) => {
    request.headers.set('Authorization', `Custom ${token}`)
    return request
  }
})

Configuration

const acme = new Acme('sk_live_...', {
  baseURL: 'https://api.acme.com',
  apiVersion: 'v2',
  timeout: 30000,              // 30 seconds
  maxRetries: 3,
  httpAgent: customAgent,
})

Error Handling

import { AcmeError, ValidationError, RateLimitError } from '@acme/sdk'

try {
  await acme.products.create({ name: '' })
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Validation failed:', error.field, error.message)
  } else if (error instanceof RateLimitError) {
    console.error('Rate limit exceeded, retry after:', error.retryAfter)
  } else if (error instanceof AcmeError) {
    console.error('API error:', error.message, error.code)
  }
}

Pagination

// Manual pagination
let hasMore = true
let cursor = undefined

while (hasMore) {
  const response = await acme.products.list({ limit: 100, cursor })

  for (const product of response.data) {
    console.log(product)
  }

  hasMore = response.hasMore
  cursor = response.nextCursor
}

// Auto-pagination
for await (const product of acme.products.listAutoPaginate()) {
  console.log(product)
}

// Stream
const stream = acme.products.listStream()
stream.on('data', (product) => console.log(product))
stream.on('end', () => console.log('Done'))

Retries

const acme = new Acme('sk_live_...', {
  maxRetries: 3,
  retryDelay: (attempt) => Math.min(1000 * Math.pow(2, attempt), 10000),
  retryCondition: (error) => {
    // Retry on network errors and 5xx
    return error.isNetworkError || (error.statusCode >= 500)
  }
})

Timeouts

const acme = new Acme('sk_live_...', {
  timeout: 30000,              // Global timeout: 30s
})

// Per-request timeout
await acme.products.create(params, {
  timeout: 60000               // 60s for this request
})

Webhooks

import { Acme } from '@acme/sdk'

// Verify webhook signature
const payload = request.body
const signature = request.headers['x-acme-signature']

const event = Acme.webhooks.verify(payload, signature, webhookSecret)

switch (event.type) {
  case 'product.created':
    console.log('New product:', event.data)
    break
  case 'subscription.canceled':
    console.log('Subscription canceled:', event.data)
    break
}

TypeScript Support

// Full type inference
const product = await acme.products.create({
  name: 'New Product',
  type: 'API',  // Only allows: 'SaaS' | 'API' | 'Data' | 'Headless'
})

product.name      // string
product.type      // 'SaaS' | 'API' | 'Data' | 'Headless'
product.isActive  // boolean | null

// Discriminated unions
if (product.type === 'API') {
  // TypeScript knows this is an API product
  product.rateLimit  // Available on API products only
}

// Generic types
const products: Product[] = await acme.products.list({ type: 'SaaS' })

SDK Implementation

Base Client

export class AcmeClient {
  private apiKey: string
  private baseURL: string
  private httpClient: HttpClient

  constructor(apiKey: string, options?: ClientOptions) {
    this.apiKey = apiKey
    this.baseURL = options?.baseURL ?? 'https://api.acme.com'
    this.httpClient = new HttpClient(this)

    // Initialize resources
    this.products = new ProductsResource(this)
    this.subscriptions = new SubscriptionsResource(this)
    this.apiKeys = new APIKeysResource(this)
  }

  public products: ProductsResource
  public subscriptions: SubscriptionsResource
  public apiKeys: APIKeysResource

  async request<T>(
    method: string,
    path: string,
    options?: RequestOptions
  ): Promise<T> {
    const url = `${this.baseURL}${path}`

    const response = await this.httpClient.request({
      method,
      url,
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
        ...options?.headers,
      },
      body: options?.body ? JSON.stringify(options.body) : undefined,
    })

    return response.json()
  }
}

Resource Class

export class ProductsResource {
  constructor(private client: AcmeClient) {}

  async create(params: ProductCreateParams): Promise<Product> {
    return this.client.request('POST', '/v1/products', { body: params })
  }

  async retrieve(id: string): Promise<Product> {
    return this.client.request('GET', `/v1/products/${id}`)
  }

  async update(id: string, params: ProductUpdateParams): Promise<Product> {
    return this.client.request('PATCH', `/v1/products/${id}`, { body: params })
  }

  async delete(id: string): Promise<void> {
    await this.client.request('DELETE', `/v1/products/${id}`)
  }

  async list(params?: ProductListParams): Promise<PaginatedList<Product>> {
    const queryString = new URLSearchParams(params as any).toString()
    return this.client.request('GET', `/v1/products?${queryString}`)
  }

  async *listAutoPaginate(
    params?: ProductListParams
  ): AsyncIterableIterator<Product> {
    let cursor = params?.cursor

    while (true) {
      const response = await this.list({ ...params, cursor })

      for (const product of response.data) {
        yield product
      }

      if (!response.hasMore) break
      cursor = response.nextCursor
    }
  }
}

Testing SDKs

Mock Responses

import { Acme } from '@acme/sdk'
import { mock } from '@acme/sdk/testing'

// Mock API responses
mock.products.create.mockResolvedValue({
  id: 'prod_123',
  name: 'Test Product',
  type: 'API',
})

const acme = new Acme('sk_test_...')
const product = await acme.products.create({ name: 'Test' })

expect(product.id).toBe('prod_123')

Test Mode

// Use test API keys
const acme = new Acme('sk_test_...')

// SDK detects test mode and uses test endpoints
await acme.products.create({ ... })  // Creates test data

Documentation

README

# Acme SDK for JavaScript

Official JavaScript/TypeScript SDK for Acme API.

## Installation

npm install @acme/sdk

## Quick Start

const Acme = require('@acme/sdk');
const acme = new Acme('sk_live_...');

const product = await acme.products.create({
  name: 'My Product',
  type: 'API',
});

## Documentation

Full API reference: https://docs.acme.com/sdk/javascript

API Reference

Auto-generated from TypeScript types:

/**
 * Create a new product.
 *
 * @param params - Product creation parameters
 * @returns The created product
 *
 * @example
 * const product = await acme.products.create({
 *   name: 'Acme API',
 *   type: 'API',
 *   business: 'biz_123',
 * })
 */
async create(params: ProductCreateParams): Promise<Product>

Best Practices

Versioning

{
  "name": "@acme/sdk",
  "version": "1.2.3"
}

Follow SemVer:

  • Major (1.x.x) - Breaking changes
  • Minor (x.2.x) - New features (backward compatible)
  • Patch (x.x.3) - Bug fixes

Changelog

## [1.2.0] - 2024-01-15

### Added
- Support for new `Headless` product type
- `products.listAutoPaginate()` for easier pagination

### Changed
- Updated `Product` type to include `interfaces` field

### Fixed
- Fixed pagination cursor encoding issue

TypeScript First

Generate SDKs from TypeScript definitions:

  1. Write TypeScript SDK
  2. Compile to JavaScript
  3. Include .d.ts type definitions
  4. Ship both JS + types in npm package

Next Steps

  • REST APIs - API endpoints that SDKs wrap
  • MCPs - Model Context Protocol for AI agents
  • API Products - Usage-based API products

On this page