Your API is great. But developers evaluate it through your SDK. If the SDK is clunky, poorly documented, or doesn't feel native to their language, they'll use a competitor's API instead.
SDK quality is product quality for developers. Marketing an SDK means making it so good that developers prefer it over alternatives. Here's how.
Why SDKs Matter More Than You Think
Developers judge your API by your SDK:
They don't read API documentation and craft HTTP requests. They:
- Install SDK
- Try quickstart example
- If it works easily, continue. If not, try competitor.
Bad SDK = Lost developer, even if API is superior.
Example:
API A: Great features, poor Python SDK (outdated, no type hints, verbose) API B: Fewer features, excellent Python SDK (modern, typed, concise)
Python developers choose API B.
The Three SDK Quality Levels
Level 1: Thin wrapper (minimum viable)
What it is: Direct mapping of API endpoints to SDK methods. No abstractions.
Example:
# Thin wrapper
response = client.post('/users', {
'email': 'user@example.com',
'name': 'John'
})
Barely better than using requests library directly.
When it's okay:
- Very simple API (5-10 endpoints)
- Beta/early stage product
- Community-maintained SDK
Level 2: Idiomatic SDK (good)
What it is: Follows language conventions, provides typed interfaces, handles common patterns.
Example:
# Idiomatic SDK
user = client.users.create(
email='user@example.com',
name='John'
)
# Returns typed User object with autocomplete
Feels native to language. Developer experience improved.
When to build:
- Production product
- Multiple customer use cases
- Competitive market
Level 3: Developer experience SDK (best)
What it is: Abstracts complexity, anticipates needs, provides helpers for common workflows.
Example:
# DX-focused SDK
async with client.users.create_and_verify(
email='user@example.com',
name='John',
send_welcome_email=True
) as user:
# Automatic retry, error handling, rollback on context exit
user.set_role('customer')
Anticipates "create user, verify email, set role" pattern. Handles complexity.
When to build:
- Core product differentiation
- Complex workflows developers repeat
- High-touch enterprise use cases
Most companies should aim for Level 2, invest in Level 3 for critical workflows.
Language-Specific SDK Best Practices
JavaScript/TypeScript:
Must have:
- TypeScript definitions (even if written in JS)
- ESM and CommonJS support
- Async/await patterns
- Tree-shakeable exports
Example:
import { StripeClient } from '@company/sdk';
const client = new StripeClient(apiKey);
const charge = await client.charges.create({
amount: 2000,
currency: 'usd',
source: token,
});
// TypeScript knows charge is type Charge with autocomplete
Don't:
- Callbacks (use Promises)
- Global namespace pollution
- Requiring polyfills
Python:
Must have:
- Type hints (Python 3.7+)
- Context managers where appropriate
- Follows PEP 8 conventions
- Works with asyncio
Example:
from company_sdk import Client
from company_sdk.types import User
client = Client(api_key="...")
# Type hints provide IDE autocomplete
user: User = client.users.create(
email="user@example.com",
name="John"
)
# Context managers for resources
async with client.stream_events() as events:
async for event in events:
print(event.type)
Don't:
- camelCase methods (use snake_case)
- Untyped returns
- Blocking I/O without async option
Go:
Must have:
- Idiomatic error handling
- Context support
- Interfaces for testability
- Structured return types
Example:
package main
import (
"context"
"github.com/company/sdk-go"
)
client := sdk.NewClient("api-key")
user, err := client.Users.Create(context.Background(), &sdk.UserCreateParams{
Email: "user@example.com",
Name: "John",
})
if err != nil {
return fmt.Errorf("creating user: %w", err)
}
Don't:
- Panic on errors (return errors)
- Ignore context deadlines
- Mutable global state
Ruby:
Must have:
- Follows Ruby conventions (snake_case, not camelCase)
- Chainable methods
- Block syntax support
- Idiomatic error classes
Example:
require 'company_sdk'
client = CompanySDK::Client.new(api_key: ENV['API_KEY'])
user = client.users.create(
email: 'user@example.com',
name: 'John'
) do |u|
u.send_welcome_email = true
u.role = 'customer'
end
Don't:
- Java-style getters/setters
- Excessive configuration objects
- Non-rubyish patterns
SDK Documentation That Works
README structure:
Above fold:
- One-line description
- Installation command
- 5-line quickstart example
Example - Stripe Python:
# Stripe Python Library
Official Stripe Python library.
Installation:
`pip install stripe`
Quick start:
```python
import stripe
stripe.api_key = "sk_test_..."
stripe.Charge.create(amount=2000, currency="usd", source="tok_visa")
Perfect. Developers can start in 30 seconds.
Below fold:
- Detailed setup
- Authentication
- Common use cases
- Error handling
- Links to full docs
API reference:
Auto-generated from code:
Use tools like Sphinx (Python), JSDoc (JavaScript), godoc (Go).
Keeps docs in sync with code.
Example:
def create_user(
email: str,
name: str,
role: Optional[str] = None,
) -> User:
"""
Create a new user.
Args:
email: User's email address
name: User's full name
role: Optional role assignment
Returns:
User: Created user object
Raises:
ValidationError: If email is invalid
APIError: If API request fails
Example:
>>> user = client.users.create(
... email="user@example.com",
... name="John Smith"
... )
"""
Docstring becomes API reference automatically.
Examples for every use case:
Don't just document methods. Show how to accomplish tasks.
Not: "users.create() creates a user."
Instead:
Creating and verifying users:
# Create user
user = client.users.create(email="...", name="...")
# Send verification email
client.emails.send_verification(user.id)
# Check verification status
if user.is_verified():
print("User verified!")
Shows complete workflow, not just individual methods.
SDK Installation and Distribution
Make installation trivial:
JavaScript:
npm install @company/sdk
# or
yarn add @company/sdk
Published to npm. Developers expect this.
Python:
pip install company-sdk
Published to PyPI. Not GitHub installation.
Go:
go get github.com/company/sdk-go
Use Go modules, version tags.
Ruby:
gem install company_sdk
Published to RubyGems.
Don't make developers:
- Clone from GitHub
- Build from source
- Install non-standard tools
- Download .zip files
Semantic versioning:
Major.Minor.Patch
- Major: Breaking changes
- Minor: New features, backward compatible
- Patch: Bug fixes
Example:
- v2.0.0: Renamed methods, changed response format (breaking)
- v2.1.0: Added new endpoints (compatible)
- v2.1.1: Fixed bug in error handling (patch)
Deprecation policy:
When removing features:
- Mark as deprecated (v2.1.0)
- Log warnings when used
- Remove in next major version (v3.0.0)
Give developers 6-12 months to migrate.
Error Handling in SDKs
Errors should be actionable:
Bad error:
APIError: Request failed
What failed? Why? How to fix?
Good error:
AuthenticationError: Invalid API key.
Get your API key from: https://dashboard.company.com/api-keys
Docs: https://docs.company.com/authentication
Tells what happened, where to get key, link to docs.
Error types:
Create specific error classes:
class CompanySDKError(Exception):
"""Base exception"""
class AuthenticationError(CompanySDKError):
"""Invalid API key or permissions"""
class RateLimitError(CompanySDKError):
"""Rate limit exceeded"""
def __init__(self, retry_after: int):
self.retry_after = retry_after
class ValidationError(CompanySDKError):
"""Invalid parameters"""
def __init__(self, field: str, message: str):
self.field = field
self.message = message
Developers can catch specific errors:
try:
user = client.users.create(email=invalid_email)
except sdk.ValidationError as e:
print(f"Invalid {e.field}: {e.message}")
except sdk.RateLimitError as e:
time.sleep(e.retry_after)
Retry logic built-in:
Handle transient errors automatically:
# SDK handles retry with exponential backoff
client = Client(
api_key="...",
max_retries=3, # Retry transient errors up to 3 times
timeout=30, # 30-second timeout
)
Developers don't need to implement retry logic.
SDK Testing and Reliability
Test suite developers can run:
Include tests in SDK:
# Clone and test
git clone https://github.com/company/sdk-python
cd sdk-python
pip install -r requirements-dev.txt
pytest
Developers can verify SDK works in their environment.
Mock API responses:
Provide test helpers:
from company_sdk.testing import mock_client
def test_create_user():
client = mock_client()
user = client.users.create(email="test@example.com")
assert user.email == "test@example.com"
Developers can test their code without hitting real API.
SDK stability:
Before releasing:
- Run test suite against multiple language versions
- Test with major frameworks (Express, Django, Rails)
- Compatibility testing (Windows, Mac, Linux)
Measuring SDK Success
Metrics that matter:
Download/install count: Track npm downloads, PyPI installs, gem downloads.
Version adoption: What % on latest version? High % = good release process.
GitHub stars/issues: Stars indicate interest. Issues show engagement (and problems).
Time to first API call: How quickly do developers make first successful request after installing SDK?
Support tickets related to SDK: High volume = SDK problems. Track and fix.
Customer surveys: "Rate SDK quality 1-10. What would improve it?"
Marketing Your SDK
Announce new SDK versions:
Release notes developers care about:
## v2.5.0 (2024-06-01)
### New Features
- Added Webhooks.verify() helper for webhook signature validation
- Support for async/await in all API methods
### Improvements
- 40% faster JSON parsing
- Better error messages with links to docs
### Breaking Changes
- Removed deprecated Users.list() method. Use Users.search() instead.
### Migration Guide
See: docs.company.com/sdk/migration-2.5
Specific changes, clear migration path.
Developer changelog:
Separate from marketing announcements. Just technical changes.
Showcase SDK in content:
Tutorial posts: Show SDK code, not raw API calls.
Video tutorials: Install SDK, walk through common use cases.
Live coding: Demonstrate SDK in webinars and conference talks.
Community-Maintained vs. Official SDKs
When to build official SDKs:
Tier 1 languages (must have official):
- JavaScript/TypeScript
- Python
- Go
- Ruby
- Java
- PHP
Your primary customer languages.
Tier 2 languages (consider official):
- C#
- Rust
- Swift
- Kotlin
If significant customer base.
When to support community SDKs:
Tier 3 languages:
- Elixir
- Scala
- Perl
- Haskell
Smaller audience. Community-maintained okay.
Support community maintainers:
- Feature them on your SDK page
- Provide preview access to API changes
- Offer credits or swag
- Link from official docs
Example: Stripe lists community libraries, some with 100K+ downloads.
SDK Examples Done Well
Stripe:
- Excellent docs
- Idiomatic SDKs in 10+ languages
- Type safety
- Great error messages
- Webhooks helpers built-in
Twilio:
- Language-specific idioms
- Helper methods for common workflows
- Great examples in docs
- Auto-generated but well-designed
AWS SDK:
- Comprehensive coverage
- Auto-generated from API specs
- Consistent across languages
- Well-maintained
OpenAI:
- Simple, clean APIs
- Streaming support built-in
- Type-safe responses
- Excellent examples
Common SDK Mistakes
Mistake 1: Auto-generated and neglected
Generated from OpenAPI spec, but:
- Methods don't follow language conventions
- No helper functions
- Poor error messages
Fix: Generate structure, then refine for language idioms.
Mistake 2: Incomplete language support
JavaScript SDK: Excellent Python SDK: Barely maintained, missing features
Fix: Either support all major languages well, or don't claim official support.
Mistake 3: Breaking changes without warning
Release v2.0 with breaking changes. No migration guide. Developers stuck.
Fix: Deprecation warnings, migration guides, 6+ month transition.
Mistake 4: Poor release discipline
No versioning strategy. Random breaking changes in minor versions.
Fix: Semantic versioning. Clear release notes.
Getting Started
Month 1:
- Audit current SDKs (or decide which languages to support)
- Survey customers: What SDK issues do you face?
Month 2:
- Fix top 5 SDK issues
- Improve README and quickstart docs
Month 3:
- Add missing features to match API
- Implement better error messages
Ongoing:
- Regular releases (monthly or quarterly)
- Track SDK metrics
- Respond to issues quickly
Your SDK is your API's user interface. Invest in making it excellent.
Developers remember when SDKs are frustrating. They also remember when SDKs are delightful. Be the latter.