---
title: "Building an API SDK Generator for Your AI Agent Platform: OpenAPI to Code"
description: "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."
canonical: https://callsphere.ai/blog/building-api-sdk-generator-ai-agent-platform-openapi-to-code
category: "Learn Agentic AI"
tags: ["OpenAPI", "SDK Generation", "Code Generation", "API Design", "Developer Experience"]
author: "CallSphere Team"
published: 2026-03-17T00:00:00.000Z
updated: 2026-05-06T01:02:44.503Z
---

# 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.

```mermaid
flowchart LR
    CLIENT(["Client SDK"])
    GW["API Gateway
auth plus rate limit"]
    APP["FastAPI app
handlers and DI"]
    VAL["Pydantic validation"]
    SVC["Service layer
business logic"]
    DB[(Database)]
    QUEUE[(Background queue)]
    OBS[(Tracing)]
    CLIENT --> GW --> APP --> VAL --> SVC
    SVC --> DB
    SVC --> QUEUE
    SVC --> OBS
    SVC --> CLIENT
    style GW fill:#4f46e5,stroke:#4338ca,color:#fff
    style APP fill:#f59e0b,stroke:#d97706,color:#1f2937
    style DB fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
```

```python
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.

```python
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.

```bash
# 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.

```python
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.

```python
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 = 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.

```bash
# Start a mock server from the OpenAPI spec
npx @stoplight/prism-cli mock openapi.json --port 4010
```

```python
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.

```bash
# 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

---

Source: https://callsphere.ai/blog/building-api-sdk-generator-ai-agent-platform-openapi-to-code
