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 SDK | With SDK |
|---|---|
| Manual HTTP requests | Simple method calls |
| JSON parsing | Typed objects |
| Error handling | Exceptions |
| Authentication boilerplate | Built-in auth |
| Rate limiting logic | Automatic retries |
| Pagination complexity | Iterator/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 ObjectLanguage-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 dataDocumentation
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/javascriptAPI 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 issueTypeScript First
Generate SDKs from TypeScript definitions:
- Write TypeScript SDK
- Compile to JavaScript
- Include
.d.tstype definitions - 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