Building an API SDK Generator for Your AI Agent Platform: OpenAPI to Code
Generate type-safe client SDKs from your AI agent API's OpenAPI specification. Covers spec design, code generation tools, custom templates, testing strategies, and distribution via PyPI and npm.
Why Generate SDKs for Your AI Agent API
Every AI agent platform reaches a point where raw HTTP calls become a developer experience problem. Users copy-paste curl commands, get authentication wrong, miss required headers, and parse responses manually. A well-crafted SDK eliminates these friction points by providing type-safe methods, automatic authentication, built-in retry logic, and IDE autocompletion.
Manually maintaining SDKs for Python, TypeScript, Go, and other languages is unsustainable. The answer is to generate them from your OpenAPI specification. Write the spec once, generate clients for every language your users need.
Writing a Generation-Ready OpenAPI Spec
Not all OpenAPI specs produce good SDKs. The quality of the generated code depends on how well you define your schemas, operation IDs, and descriptions.
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI(
title="Agent Platform API",
version="1.0.0",
description="API for managing AI agents, conversations, and evaluations.",
servers=[
{"url": "https://api.example.com/v1", "description": "Production"},
{"url": "https://staging-api.example.com/v1", "description": "Staging"},
],
)
class Agent(BaseModel):
"""An AI agent configuration."""
id: str = Field(..., description="Unique agent identifier", examples=["agent_abc123"])
name: str = Field(..., description="Human-readable agent name", max_length=100)
model: str = Field(..., description="LLM model ID", examples=["gpt-4o"])
system_prompt: str = Field(..., description="System instructions for the agent")
temperature: float = Field(
0.7, ge=0.0, le=2.0,
description="Sampling temperature for response generation",
)
tools: list[str] = Field(
default_factory=list,
description="List of tool IDs the agent can invoke",
)
class CreateAgentRequest(BaseModel):
"""Request body for creating a new agent."""
name: str = Field(..., description="Human-readable agent name")
model: str = Field("gpt-4o", description="LLM model to use")
system_prompt: str = Field(..., description="System instructions")
temperature: float = Field(0.7, ge=0.0, le=2.0)
tools: list[str] = Field(default_factory=list)
@app.post(
"/agents",
response_model=Agent,
operation_id="create_agent",
summary="Create a new agent",
tags=["Agents"],
status_code=201,
)
async def create_agent(body: CreateAgentRequest):
"""Create a new AI agent with the specified configuration.
The agent will be immediately available for conversations
after creation.
"""
pass
The operation_id field is critical. It becomes the method name in generated SDKs. Without explicit operation IDs, generators create ugly names like post_v1_agents_create_agent_post. Use clear, verb-noun patterns: create_agent, list_conversations, get_evaluation_result.
Exporting the OpenAPI Spec
FastAPI generates the OpenAPI spec automatically. Export it as a JSON file for the code generator.
import json
from pathlib import Path
def export_openapi_spec():
spec = app.openapi()
# Add security scheme
spec["components"]["securitySchemes"] = {
"ApiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key",
},
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
},
}
spec["security"] = [{"ApiKeyAuth": []}, {"BearerAuth": []}]
Path("openapi.json").write_text(
json.dumps(spec, indent=2)
)
print("Exported openapi.json")
if __name__ == "__main__":
export_openapi_spec()
Generating Python and TypeScript SDKs
Use openapi-python-client for Python and openapi-typescript-codegen for TypeScript. Both read the OpenAPI spec and produce typed client code.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
# Install generators
pip install openapi-python-client
npm install -g openapi-typescript-codegen
# Generate Python SDK
openapi-python-client generate \
--path openapi.json \
--config sdk-config.yaml \
--output-path ./sdks/python
# Generate TypeScript SDK
openapi-typescript-codegen \
--input openapi.json \
--output ./sdks/typescript \
--client axios \
--name AgentPlatformClient
The Python generator produces a package with models, API clients, and type hints. Here is what the generated code looks like when consumed.
from agent_platform_client import Client
from agent_platform_client.models import CreateAgentRequest
from agent_platform_client.api.agents import create_agent, list_agents
client = Client(
base_url="https://api.example.com/v1",
headers={"X-API-Key": "your-key-here"},
)
# Type-safe agent creation
new_agent = create_agent.sync(
client=client,
body=CreateAgentRequest(
name="Customer Support Agent",
model="gpt-4o",
system_prompt="You are a helpful support agent.",
temperature=0.3,
tools=["search_knowledge_base", "create_ticket"],
),
)
print(f"Created agent: {new_agent.id}")
Customizing Generated Code
Default generated code is often too bare-bones for production use. Add retry logic, authentication helpers, and custom error handling by wrapping the generated client.
import httpx
import asyncio
class AgentPlatformSDK:
"""High-level SDK wrapping the generated client."""
def __init__(
self,
api_key: str,
base_url: str = "https://api.example.com/v1",
max_retries: int = 3,
timeout: float = 30.0,
):
self._client = httpx.AsyncClient(
base_url=base_url,
headers={
"X-API-Key": api_key,
"Content-Type": "application/json",
},
timeout=timeout,
)
self._max_retries = max_retries
async def create_agent(self, **kwargs) -> dict:
return await self._request("POST", "/agents", json=kwargs)
async def list_agents(self, limit: int = 20) -> dict:
return await self._request(
"GET", "/agents", params={"limit": limit}
)
async def _request(self, method: str, path: str, **kwargs) -> dict:
for attempt in range(self._max_retries + 1):
response = await self._client.request(method, path, **kwargs)
if response.status_code < 400:
return response.json()
if response.status_code == 429:
retry_after = int(
response.headers.get("Retry-After", 2 ** attempt)
)
await asyncio.sleep(retry_after)
continue
if response.status_code >= 500 and attempt < self._max_retries:
await asyncio.sleep(2 ** attempt)
continue
response.raise_for_status()
async def close(self):
await self._client.aclose()
async def __aenter__(self):
return self
async def __aexit__(self, *args):
await self.close()
Testing the Generated SDK
Test the SDK against a mock server that validates requests match the OpenAPI spec. Tools like Prism can spin up a mock server from your spec.
# Start a mock server from the OpenAPI spec
npx @stoplight/prism-cli mock openapi.json --port 4010
import pytest
@pytest.mark.asyncio
async def test_create_agent():
async with AgentPlatformSDK(
api_key="test-key",
base_url="http://localhost:4010/v1",
) as sdk:
agent = await sdk.create_agent(
name="Test Agent",
model="gpt-4o",
system_prompt="Test prompt",
)
assert "id" in agent
assert agent["name"] == "Test Agent"
@pytest.mark.asyncio
async def test_rate_limit_retry():
"""Verify SDK retries on 429 responses."""
async with AgentPlatformSDK(
api_key="test-key",
base_url="http://localhost:4010/v1",
max_retries=2,
) as sdk:
result = await sdk.list_agents(limit=10)
assert isinstance(result, dict)
Distribution
Publish the Python SDK to PyPI and the TypeScript SDK to npm. Automate generation and publishing in your CI/CD pipeline so the SDK stays in sync with the API.
# Python: build and publish
cd sdks/python
python -m build
twine upload dist/*
# TypeScript: build and publish
cd sdks/typescript
npm run build
npm publish --access public
FAQ
How do I keep the SDK in sync with API changes?
Automate SDK generation in your CI/CD pipeline. When the API code changes, regenerate the OpenAPI spec, run the code generators, execute the test suite against the spec, and publish a new SDK version. Use semantic versioning: patch for docs-only changes, minor for new endpoints or optional fields, major for breaking changes.
Should I use the generated code directly or wrap it?
Wrap it. Generated code handles the mechanics — HTTP calls, serialization, type definitions — but lacks polish. Your wrapper adds authentication management, retry logic with backoff, rate limit handling, connection pooling, and a clean public API that hides implementation details. Think of the generated code as infrastructure and the wrapper as the product.
What makes an OpenAPI spec produce high-quality SDKs?
Four things: explicit operationId on every endpoint (controls method names), detailed description fields on schemas and parameters (becomes docstrings), examples on fields (used in generated documentation), and clear tags grouping endpoints logically (becomes module or class organization). Also define all response codes including errors so the SDK can handle them properly.
#OpenAPI #SDKGeneration #CodeGeneration #APIDesign #DeveloperExperience #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.