OpenAI can be integrated into .NET applications through the official OpenAI .NET SDK.
The official SDK is distributed as the OpenAI NuGet package and provides client types for major OpenAI capabilities, including chat, responses, embeddings, images, audio, files, vector stores, moderation, realtime, and other API areas. OpenAI's official repository states that the library is generated from the OpenAI OpenAPI specification in collaboration with Microsoft.
As of the current 2026-10-06 development cycle, the latest published OpenAI NuGet package verified here is 2.13.0, released on August 10, 2026.
The important architecture is:
.NET Application
|
+-------------+-------------+
| |
v v
OpenAI .NET SDK Microsoft.Extensions.AI
| |
| v
| IChatClient
| |
+-------------+-------------+
|
v
OpenAI API
|
v
AI Services
You can either use the OpenAI SDK directly or adapt OpenAI clients to the provider-neutral IChatClient abstraction. Microsoft currently exposes AsIChatClient adapters for OpenAI's ChatClient and also for ResponsesClient.
What Is OpenAI with .NET?
OpenAI with .NET means integrating OpenAI's models and API capabilities into C# and .NET applications.
The official SDK provides clients organized around API capabilities:
OpenAI
|
+-- Chat
| +-- ChatClient
|
+-- Responses
| +-- ResponsesClient
|
+-- Embeddings
| +-- EmbeddingClient
|
+-- Images
| +-- ImageClient
|
+-- Audio
| +-- AudioClient
|
+-- Files
| +-- OpenAIFileClient
|
+-- Vector Stores
| +-- VectorStoreClient
|
+-- Moderation
| +-- ModerationClient
|
+-- Realtime
| +-- RealtimeClient
|
+-- Assistants
| +-- AssistantClient
|
+-- Models
| +-- OpenAIModelClient
This organization is documented in the official OpenAI .NET repository.
Why Use the Official OpenAI .NET SDK?
The official SDK provides several advantages.
Official API integration
The package is maintained by OpenAI and is generated from the OpenAI API specification.
Strongly typed C# APIs
Instead of constructing raw HTTP requests manually, you can use types such as:
ChatClient
ResponsesClient
EmbeddingClient
ImageClient
AudioClient
Async support
The SDK provides asynchronous counterparts for API operations. For example, CompleteChatAsync is the async equivalent of CompleteChat.
Streaming support
The SDK provides streaming APIs for chat and responses.
Tool calling
The SDK supports tool and function calling through its chat APIs.
Structured outputs
The official SDK supports schema-based structured outputs. The official examples include CreateJsonSchemaFormat for chat completions.
Microsoft.Extensions.AI integration
OpenAI clients can be adapted to IChatClient, allowing the rest of a .NET application to remain provider-neutral.
Installing OpenAI for .NET
Create a project:
dotnet new console -n OpenAIDotNetDemo
cd OpenAIDotNetDemo
Install the official SDK:
dotnet add package OpenAI --version 2.13.0
The verified NuGet package version is 2.13.0.
You can also add the Microsoft abstraction package:
dotnet add package Microsoft.Extensions.AI
dotnet add package Microsoft.Extensions.AI.OpenAI
The Microsoft.Extensions.AI.OpenAI package provides an IChatClient implementation for the OpenAI package and OpenAI-compatible endpoints.
OpenAI API Key
OpenAI requires an API key to call the API.
The official OpenAI SDK documentation recommends keeping the key in a secure location such as an environment variable instead of hard-coding it in source code.
On Windows PowerShell:
$env:OPENAI_API_KEY="your-api-key"
For a persistent environment variable:
setx OPENAI_API_KEY "your-api-key"
After using setx, start a new terminal before running the application.
For local .NET development, User Secrets are another good option:
dotnet user-secrets init
dotnet user-secrets set "OpenAI:ApiKey" "your-api-key"
Do not put real API keys in:
Git repositories
appsettings.json committed to Git
public JavaScript
HTML
client-side Blazor WebAssembly
mobile app source
screenshots
Reading the API Key
var apiKey =
Environment.GetEnvironmentVariable(
"OPENAI_API_KEY")
?? throw new InvalidOperationException(
"OPENAI_API_KEY is not configured.");
Keep authentication outside your business logic.
The ChatClient
For chat-completions-style interactions, the OpenAI SDK provides:
OpenAI.Chat.ChatClient
Basic example:
using OpenAI.Chat;
string apiKey =
Environment.GetEnvironmentVariable(
"OPENAI_API_KEY")
?? throw new InvalidOperationException(
"OPENAI_API_KEY is not configured.");
string model =
Environment.GetEnvironmentVariable(
"AI_MODEL")
?? throw new InvalidOperationException(
"AI_MODEL is not configured.");
ChatClient client =
new(model, apiKey);
The official repository currently demonstrates constructing ChatClient with a model identifier and API key.
Sending Your First Request
A simple request is:
ChatCompletion completion =
await client.CompleteChatAsync(
"Explain dependency injection in ASP.NET Core.");
Read the response:
Console.WriteLine(
completion.Content[0].Text);
The official SDK documents CompleteChat and CompleteChatAsync for chat completions.
Complete Console Application
using OpenAI.Chat;
var apiKey =
Environment.GetEnvironmentVariable(
"OPENAI_API_KEY")
?? throw new InvalidOperationException(
"OPENAI_API_KEY is not configured.");
var model =
Environment.GetEnvironmentVariable(
"AI_MODEL")
?? throw new InvalidOperationException(
"AI_MODEL is not configured.");
ChatClient client =
new(model, apiKey);
ChatCompletion completion =
await client.CompleteChatAsync(
"Explain dependency injection in ASP.NET Core.");
Console.WriteLine(
completion.Content[0].Text);
The model identifier is intentionally configured externally rather than hard-coded into the source.
Understanding ChatCompletion
The returned object represents the result of a chat completion.
Conceptually:
ChatCompletion
|
+-- Content
+-- FinishReason
+-- Usage
+-- ToolCalls
+-- Other metadata
Depending on the request, content may contain ordinary text, structured data, or other supported content.
Messages Instead of One String
For simple requests, a single string is convenient.
For applications with multiple roles and conversation history, use chat messages.
using OpenAI.Chat;
List<ChatMessage> messages =
[
new SystemChatMessage(
"You are a helpful .NET assistant."),
new UserChatMessage(
"What is dependency injection?")
];
ChatCompletion completion =
await client.CompleteChatAsync(
messages);
This lets the application represent:
System
User
Assistant
Tool
as separate conversation elements.
System Instructions
For a support assistant:
List<ChatMessage> messages =
[
new SystemChatMessage(
"""
You are an enterprise customer support assistant.
Use only the supplied application information.
Do not invent product features.
Keep answers concise.
"""),
new UserChatMessage(
"How do I reset my password?")
];
ChatCompletion completion =
await client.CompleteChatAsync(
messages);
This follows the system/user separation covered in the previous articles.
Conversation History
Store the conversation as messages:
List<ChatMessage> messages =
[
new SystemChatMessage(
"You are a helpful assistant.")
];
messages.Add(
new UserChatMessage(
"What is ASP.NET Core?"));
ChatCompletion first =
await client.CompleteChatAsync(
messages);
messages.Add(
new AssistantChatMessage(first));
messages.Add(
new UserChatMessage(
"How does dependency injection work in it?"));
ChatCompletion second =
await client.CompleteChatAsync(
messages);
The assistant response can be inserted back into the history so future requests have access to the conversation.
Streaming Responses
For long responses, you may want text to appear progressively.
The official SDK provides CompleteChatStreaming and CompleteChatStreamingAsync.
Example:
AsyncCollectionResult<StreamingChatCompletionUpdate> updates =
client.CompleteChatStreamingAsync(
"Explain ASP.NET Core middleware.");
await foreach (
StreamingChatCompletionUpdate update
in updates)
{
foreach (
ChatMessageContentPart part
in update.ContentUpdate)
{
Console.Write(
part.Text);
}
}
The exact collection/update types are part of the current OpenAI .NET SDK's streaming surface.
Why Streaming Matters
Without streaming:
Request
|
v
Wait
|
v
Complete Response
With streaming:
Request
|
v
Chunk 1
|
v
Chunk 2
|
v
Chunk 3
|
v
Chunk 4
This is useful for:
chat applications
AI assistants
interactive documentation
real-time UI
long-form generation
The ResponsesClient
The OpenAI .NET SDK also includes:
OpenAI.Responses.ResponsesClient
The current official SDK exposes CreateResponse, CreateResponseAsync, and streaming variants through ResponsesClient.
Example:
#pragma warning disable OPENAI001
using OpenAI.Responses;
var apiKey =
Environment.GetEnvironmentVariable(
"OPENAI_API_KEY")
?? throw new InvalidOperationException(
"OPENAI_API_KEY is not configured.");
ResponsesClient client =
new(apiKey);
ResponseResult response =
await client.CreateResponseAsync(
"configured-model",
"Explain dependency injection in C#.");
Console.WriteLine(
response.GetOutputText());
#pragma warning restore OPENAI001
The current official SDK examples mark the Responses APIs as experimental with OPENAI001, so applications using that surface should account for API evolution.
Chat API vs Responses API
The SDK currently exposes both chat-completions and responses surfaces.
Conceptually:
OpenAI .NET SDK
|
+-- ChatClient
| |
| +-- Chat completions
|
+-- ResponsesClient
|
+-- Responses API
For a straightforward conversational application:
ChatClient
is easy to understand.
For applications using the newer Responses API capabilities, the corresponding:
ResponsesClient
is the direct SDK surface.
OpenAI's current .NET repository includes examples for Responses streaming, reasoning, web search, file search, and other capabilities.
Using ResponsesClient with Options
The Responses API can be configured with:
CreateResponseOptions options =
new()
{
Model = "configured-model"
};
Then:
options.InputItems.Add(
ResponseItem.CreateUserMessageItem(
"Explain ASP.NET Core dependency injection."));
ResponseResult response =
await client.CreateResponseAsync(
options);
Console.WriteLine(
response.GetOutputText());
The current ResponsesClient API uses CreateResponseOptions.InputItems for input items and the model is set through the options or convenience overloads.
Previous Response IDs
The Responses API supports continuing from a previous response through a previous response identifier.
Conceptually:
Response 1
|
v
Response ID
|
v
Response 2
The current SDK exposes PreviousResponseId in CreateResponseOptions and convenience overloads.
Example:
CreateResponseOptions options =
new()
{
Model = "configured-model",
PreviousResponseId = previousResponseId
};
options.InputItems.Add(
ResponseItem.CreateUserMessageItem(
"Continue from the previous request."));
ResponseResult response =
await client.CreateResponseAsync(
options);
This is different from maintaining your own List<ChatMessage> history, so choose the conversation-state strategy that fits your application architecture.
OpenAIClient
The SDK also provides a parent:
OpenAIClient
which can create feature-specific clients.
For example:
using OpenAI;
OpenAIClient client =
new(apiKey);
Then:
ChatClient chatClient =
client.GetChatClient(model);
or:
AudioClient audioClient =
client.GetAudioClient(
"configured-audio-model");
The official repository documents OpenAIClient as a convenient way to create feature-specific clients while sharing underlying implementation details.
Multiple OpenAI Clients
A single application might need:
OpenAIClient
|
+-- ChatClient
+-- EmbeddingClient
+-- ImageClient
+-- AudioClient
+-- FileClient
This provides a central client-management pattern.
Dependency Injection in ASP.NET Core
The official OpenAI .NET SDK documents its clients as thread-safe and suitable for singleton registration in ASP.NET Core dependency injection.
A simple registration is:
builder.Services.AddSingleton(
_ =>
{
var apiKey =
builder.Configuration[
"OpenAI:ApiKey"]
?? throw new InvalidOperationException(
"OpenAI API key is not configured.");
var model =
builder.Configuration[
"OpenAI:Model"]
?? throw new InvalidOperationException(
"OpenAI model is not configured.");
return new ChatClient(
model,
apiKey);
});
Then inject:
public sealed class AIService
{
private readonly ChatClient _client;
public AIService(
ChatClient client)
{
_client = client;
}
}
Configuration with appsettings.json
Use configuration for the model:
{
"OpenAI": {
"Model": "your-model-name"
}
}
Keep the API key outside source-controlled configuration.
For example, User Secrets:
dotnet user-secrets set "OpenAI:ApiKey" "your-api-key"
Or environment variables.
Strongly Typed Configuration
Create:
public sealed class OpenAIOptions
{
public string? ApiKey { get; set; }
public string? Model { get; set; }
}
Register:
builder.Services.Configure<OpenAIOptions>(
builder.Configuration.GetSection(
"OpenAI"));
Then:
public sealed class AIService
{
private readonly ChatClient _client;
public AIService(
IOptions<OpenAIOptions> options)
{
var settings =
options.Value;
_client =
new ChatClient(
settings.Model,
settings.ApiKey);
}
}
For production, prefer registering the client once rather than constructing it repeatedly inside requests.
Microsoft.Extensions.AI Integration
One of the strongest .NET patterns is to adapt the OpenAI SDK to:
IChatClient
Example:
using Microsoft.Extensions.AI;
using OpenAI;
var client =
new OpenAIClient(apiKey)
.GetChatClient(model)
.AsIChatClient();
Microsoft currently documents AsIChatClient(ChatClient) as the adapter from the OpenAI ChatClient to IChatClient.
This creates:
OpenAI ChatClient
|
v
AsIChatClient()
|
v
IChatClient
Why Use IChatClient?
The direct SDK approach:
MyService
|
v
OpenAI ChatClient
couples your service to OpenAI.
The abstraction approach:
MyService
|
v
IChatClient
|
v
OpenAI
allows you to change the implementation later.
For example:
IChatClient
|
+-- OpenAI
+-- Azure OpenAI
+-- Ollama
+-- Other provider
Microsoft's Microsoft.Extensions.AI.OpenAI package explicitly provides this integration.
Direct OpenAI SDK vs IChatClient
Use the direct OpenAI SDK when:
you need OpenAI-specific features
you need the exact OpenAI SDK API
you need OpenAI-specific options
you want direct control
Use IChatClient when:
you want provider abstraction
your application may switch providers
you want Microsoft.Extensions.AI middleware
you want common chat abstractions
A hybrid architecture is often ideal:
Application Services
|
v
IChatClient
|
v
OpenAI ChatClient
|
v
OpenAI
while specialized OpenAI-only components can still use direct SDK clients when necessary.
OpenAI Chat Completion Options
The direct SDK supports chat options.
For example:
ChatCompletionOptions options =
new()
{
Temperature = 0.2f
};
Then:
ChatCompletion completion =
await client.CompleteChatAsync(
messages,
options);
The exact available option set depends on the version and endpoint represented by the installed SDK.
Structured Outputs with the OpenAI SDK
The official OpenAI .NET SDK supports schema-based structured outputs.
The official example creates:
ChatResponseFormat.CreateJsonSchemaFormat(
...
)
and configures:
jsonSchemaIsStrict: true
The SDK example then parses the response with JsonDocument.
Example:
ChatCompletionOptions options =
new()
{
ResponseFormat =
ChatResponseFormat.CreateJsonSchemaFormat(
jsonSchemaFormatName:
"ticket_analysis",
jsonSchema:
BinaryData.FromString(
"""
{
"type": "object",
"properties": {
"category": {
"type": "string"
},
"priority": {
"type": "string"
},
"summary": {
"type": "string"
}
},
"required": [
"category",
"priority",
"summary"
],
"additionalProperties": false
}
"""),
jsonSchemaIsStrict: true)
};
Then:
ChatCompletion completion =
await client.CompleteChatAsync(
messages,
options);
Read the structured response:
using JsonDocument json =
JsonDocument.Parse(
completion.Content[0].Text);
The current official example demonstrates this same CreateJsonSchemaFormat pattern.
Structured Output with Microsoft.Extensions.AI
When using IChatClient, the equivalent architecture is:
var response =
await chatClient.GetResponseAsync<
TicketAnalysis>(
messages);
This was covered in the previous article.
The two approaches are:
Direct OpenAI SDK
|
v
OpenAI-specific API
and:
IChatClient
|
v
Provider-neutral abstraction
|
v
OpenAI implementation
Both are valid.
Tool Calling with the OpenAI SDK
The OpenAI SDK supports tool and function calling.
The basic workflow is:
User Request
|
v
AI Model
|
v
Tool Call
|
v
.NET Application
|
v
Execute Tool
|
v
Tool Result
|
v
AI Model
|
v
Final Answer
The official SDK documents using ChatCompletionOptions.Tools and handling a ChatFinishReason.ToolCalls result.
A conceptual tool definition:
ChatTool tool =
ChatTool.CreateFunctionTool(
functionName:
"get_order_status",
functionDescription:
"Gets the current status of an order.");
Then:
ChatCompletionOptions options =
new()
{
Tools =
{
tool
}
};
When the model requests a tool, inspect the returned tool calls and execute only approved operations.
Handling Tool Calls
The SDK's documented flow is:
CompleteChat
|
v
ToolCalls?
|
+---+---+
| |
No Yes
| |
v v
Answer Execute Tool
|
v
Add Tool Result
|
v
CompleteChat
The official repository demonstrates checking ChatFinishReason.ToolCalls, adding the assistant message with the tool calls to the conversation, executing each tool, and adding ChatRequestToolMessage results before making the next completion request.
Tool Security
Never assume a tool call should be executed simply because the model requested it.
The application should enforce:
authentication
authorization
input validation
rate limits
resource access rules
tool allowlists
The model chooses or requests a tool.
The application controls whether that operation is actually permitted.
OpenAI Embeddings with .NET
The SDK includes:
EmbeddingClient
for embeddings. The official SDK lists OpenAI.Embeddings and EmbeddingClient among its client areas.
Conceptually:
EmbeddingClient client =
new(
"configured-embedding-model",
apiKey);
Then generate embeddings from text according to the SDK's current embedding API.
Embeddings become important later in the series for:
RAG
semantic search
vector databases
similarity
document retrieval
OpenAI Images with .NET
The SDK includes:
ImageClient
under OpenAI.Images.
This allows a .NET application to integrate image-generation workflows without manually constructing HTTP requests.
A typical architecture is:
ASP.NET Core
|
v
Image Service
|
v
ImageClient
|
v
OpenAI Image API
|
v
Image Result
OpenAI Audio with .NET
The SDK includes:
AudioClient
under OpenAI.Audio.
The official SDK documentation includes audio input/output examples and audio transcription workflows.
This is useful for:
speech-to-text
text-to-speech
voice applications
audio processing
OpenAI Files with .NET
The SDK also includes file operations.
OpenAIFileClient
can support application workflows involving uploaded files. The official SDK repository lists files as a dedicated client area.
Files become increasingly useful for:
document processing
retrieval
file-based workflows
AI assistants
OpenAI Vector Stores
The SDK currently includes:
VectorStoreClient
which is part of the official client organization.
This becomes relevant later when the series reaches:
RAG
vector search
semantic retrieval
OpenAI Moderation
The SDK also includes:
ModerationClient
under OpenAI.Moderations.
This can be placed in an application pipeline:
User Input
|
v
Validation / Moderation
|
v
OpenAI Model
|
v
Response
|
v
Output Controls
Use application-specific safety policies appropriate to the service you are building.
OpenAI Realtime
The SDK also exposes:
RealtimeClient
for realtime functionality.
Realtime applications introduce a different architecture:
Client
|
v
Realtime Connection
|
v
OpenAI Realtime
|
+-- audio
+-- events
+-- tool calls
+-- responses
This becomes relevant later when building voice and realtime AI applications.
OpenAI Models
The SDK includes:
OpenAIModelClient
for model-related operations.
Keep model selection configuration-driven rather than scattering model identifiers throughout application code.
For example:
{
"OpenAI": {
"Model": "your-model-name"
}
}
Model Configuration
A clean service might read:
string model =
configuration["OpenAI:Model"]
?? throw new InvalidOperationException(
"OpenAI model is not configured.");
Then:
ChatClient client =
new(model, apiKey);
This allows model changes without recompiling business code.
ASP.NET Core Architecture
A production application should avoid calling OpenAI directly from controllers.
Prefer:
ASP.NET Core Controller
|
v
AI Application Service
|
v
OpenAI Client / IChatClient
|
v
OpenAI API
For example:
public interface IOpenAIService
{
Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default);
}
Implementation:
public sealed class OpenAIService
: IOpenAIService
{
private readonly ChatClient _client;
public OpenAIService(
ChatClient client)
{
_client = client;
}
public async Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default)
{
ChatCompletion completion =
await _client.CompleteChatAsync(
prompt,
cancellationToken);
return completion
.Content[0]
.Text;
}
}
Register:
builder.Services.AddScoped<IOpenAIService, OpenAIService>();
ASP.NET Core Endpoint
app.MapPost(
"/api/ai",
async (
AIRequest request,
IOpenAIService service,
CancellationToken cancellationToken) =>
{
var response =
await service.GenerateAsync(
request.Prompt,
cancellationToken);
return Results.Ok(
new
{
response
});
});
Request model:
public sealed class AIRequest
{
public required string Prompt { get; init; }
}
Architecture:
POST /api/ai
|
v
AIRequest
|
v
IOpenAIService
|
v
ChatClient
|
v
OpenAI
ASP.NET Core Streaming
For a chat UI, you can stream results from the OpenAI client to the HTTP response.
Conceptually:
Browser
|
v
ASP.NET Core
|
v
Streaming AI Service
|
v
ChatClient
|
v
OpenAI
|
+-- chunk
+-- chunk
+-- chunk
|
v
Browser
The exact transport can be:
Server-Sent Events
SignalR
HTTP streaming
depending on the application.
Using IChatClient in ASP.NET Core
An alternative is to register:
IChatClient
instead of ChatClient.
Example:
builder.Services.AddSingleton<IChatClient>(
_ =>
{
var apiKey =
builder.Configuration[
"OpenAI:ApiKey"]
?? throw new InvalidOperationException();
var model =
builder.Configuration[
"OpenAI:Model"]
?? throw new InvalidOperationException();
return new OpenAIClient(apiKey)
.GetChatClient(model)
.AsIChatClient();
});
Then:
public sealed class AIService
{
private readonly IChatClient _chatClient;
public AIService(
IChatClient chatClient)
{
_chatClient = chatClient;
}
}
This is particularly useful if you plan to support both OpenAI and another provider later.
Configuration-Driven OpenAI Clients
The official OpenAI SDK also includes configuration-driven ASP.NET Core integration for clients. Its current ASP.NET Core example uses AddResponsesClient and ResponsesClientSettings to bind a client from configuration.
Conceptually:
appsettings
|
v
ResponsesClientSettings
|
v
ResponsesClient
|
v
ASP.NET Core DI
The official sample shows:
builder.AddResponsesClient(
"Clients:ResponsesClient");
and then injects ResponsesClient into the endpoint.
Because the current configuration APIs are marked experimental in that sample, inspect the versioned SDK documentation before adopting them in a long-lived production project.
Error Handling
OpenAI integration should handle failures explicitly.
Common categories include:
authentication failure
authorization failure
invalid request
rate limiting
network failure
timeout
model/configuration problem
content/response processing failure
tool execution failure
A basic service can use:
try
{
ChatCompletion completion =
await client.CompleteChatAsync(
prompt,
cancellationToken);
}
catch (Exception ex)
{
logger.LogError(
ex,
"OpenAI request failed.");
throw;
}
For production, use more specific exception handling and preserve the original cancellation behavior.
Cancellation Support
Always pass the cancellation token:
await client.CompleteChatAsync(
messages,
cancellationToken:
cancellationToken);
This is especially important in ASP.NET Core because the client may disconnect before the model completes the request.
Timeout Handling
Long AI requests should have appropriate timeout behavior.
At the application level:
HTTP Request
|
v
CancellationToken
|
v
OpenAI Request
Do not let abandoned browser requests continue consuming resources unnecessarily.
Retry Policies
The official SDK documentation includes an example for automatically retrying errors.
However, not every error should be retried.
A sensible policy distinguishes:
Transient network error
|
v
Retry
Rate limit
|
v
Controlled backoff
Invalid request
|
v
Do not blindly retry
Authentication error
|
v
Fix configuration
Cancellation
|
v
Stop
This is a general application pattern rather than something to apply identically to every OpenAI operation.
Rate Limiting
Applications calling OpenAI should protect themselves from uncontrolled request volume.
In ASP.NET Core:
Client
|
v
Rate Limiter
|
v
AI Service
|
v
OpenAI
Possible dimensions:
requests per user
requests per IP
requests per subscription
requests per tenant
tokens per operation
AI Cost Controls
OpenAI usage can become expensive if prompts are unnecessarily large.
Control:
prompt length
conversation history
retrieved context
output size
request rate
model selection
Architecture:
Request
|
v
Input Limits
|
v
Prompt / Context
|
v
AI Model
|
v
Usage Tracking
Usage Tracking
Record relevant response metadata where available:
model
input usage
output usage
total usage
duration
request ID
operation
This is essential for production observability.
Logging OpenAI Requests
Do not log API keys.
Avoid blindly logging complete user prompts if they may contain sensitive data.
Prefer metadata:
Operation = CustomerSupport
Model = configured-model
DurationMs = ...
Success = true
Add detailed payload logging only under controlled diagnostics and appropriate data-protection rules.
OpenAI and HttpClient
The SDK handles HTTP communication internally.
For most applications, use the official SDK rather than writing:
HttpClient.PostAsync(...)
against the REST API manually.
Direct HTTP is still useful for:
very specialized protocol access
unsupported SDK features
diagnostic work
low-level integration
The official SDK also exposes protocol-level methods when more direct REST access is needed.
OpenAI Protocol Methods
The SDK documents protocol methods that accept binary request content and return binary response content.
This is useful when you need:
raw request body
raw API response
direct protocol access
The official repository describes these as lower-level protocol methods alongside the strongly typed APIs.
Most application code should prefer the strongly typed API.
Testing OpenAI Applications
Do not make every unit test call the real OpenAI API.
Separate:
Business Logic
|
+-- unit tests
|
v
AI Adapter
|
+-- integration tests
|
v
OpenAI
For example:
public interface IAITextGenerator
{
Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default);
}
Your business service depends on:
IAITextGenerator
rather than directly on ChatClient.
Then unit tests can use a fake implementation.
Mocking the OpenAI SDK
The official OpenAI .NET repository includes guidance for mocking clients as part of its advanced scenarios.
The principle is:
Application Service
|
v
IAIService
|
+-----+-----+
| |
v v
Fake OpenAI
Test Production
This keeps unit tests deterministic.
Integration Tests
Use real OpenAI requests for a smaller integration test suite.
For example:
Integration Test
|
v
OpenAI SDK
|
v
OpenAI API
Check:
request succeeds
response is usable
structured output parses
tool call works
streaming works
Don't make your entire CI pipeline dependent on remote model behavior unless that trade-off is intentional.
OpenAI and Prompt Management
The architecture from earlier topics fits directly:
Prompt Repository
|
v
Prompt Template
|
v
Rendered Messages
|
v
OpenAI Client
|
v
OpenAI Model
A good service interface is:
public interface IOpenAIService
{
Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default);
}
The prompt itself can live outside the C# service.
OpenAI and System Prompts
Use:
List<ChatMessage> messages =
[
new SystemChatMessage(
systemPrompt),
new UserChatMessage(
userPrompt)
];
This keeps:
System Prompt
separate from:
User Request
and matches the prompt-management architecture already established in the series.
OpenAI and Prompt Templates
Example:
Summarize this document for {{audience}}.
Document:
{{document}}
Render:
var prompt =
renderer.Render(
template,
new Dictionary<string, string>
{
["audience"] =
"senior .NET developers",
["document"] =
documentText
});
Then send to OpenAI:
ChatCompletion completion =
await client.CompleteChatAsync(
prompt);
This connects topics 25 through 31 into one architecture.
OpenAI and Structured Responses
A typed output can use:
var response =
await chatClient.GetResponseAsync<
TicketAnalysis>(
messages);
or the direct SDK can use CreateJsonSchemaFormat with ChatCompletionOptions.
Therefore:
Prompt Template
|
v
System/User Messages
|
v
OpenAI
|
v
Structured Response
|
v
C# Object
OpenAI and JSON
The direct SDK supports structured output and JSON Schema through chat completion options. The official example uses ChatResponseFormat.CreateJsonSchemaFormat and then parses the returned JSON using JsonDocument.
This is useful when you need OpenAI-specific control rather than the provider-neutral IChatClient abstraction.
OpenAI and RAG
At this stage of the series, a basic RAG architecture is:
Documents
|
v
Chunking
|
v
Embeddings
|
v
Vector Search
|
v
Retrieved Context
|
v
Prompt
|
v
OpenAI
|
v
Answer
The OpenAI SDK already provides an EmbeddingClient, and it also includes vector-store related client APIs.
Detailed RAG implementation will be covered later in the series.
OpenAI and ASP.NET Core
A production ASP.NET Core architecture might look like:
ASP.NET Core
|
+---------------+----------------+
| |
v v
Prompt Service Context Service
| |
+---------------+----------------+
|
v
AI Application
Service
|
+-------------+-------------+
| |
v v
ChatClient Other Clients
| |
+-------------+-------------+
|
v
OpenAI API
The key principle is not to scatter OpenAI calls throughout controllers.
OpenAI and Clean Architecture
A Clean Architecture implementation can be:
Presentation
|
v
Application
|
+-- AI Interfaces
|
v
Infrastructure
|
+-- OpenAI
For example:
public interface IAIChatService
{
Task<string> ChatAsync(
string prompt,
CancellationToken cancellationToken = default);
}
Infrastructure:
public sealed class OpenAIChatService
: IAIChatService
{
private readonly ChatClient _client;
public OpenAIChatService(
ChatClient client)
{
_client = client;
}
public async Task<string> ChatAsync(
string prompt,
CancellationToken cancellationToken = default)
{
var completion =
await _client.CompleteChatAsync(
prompt,
cancellationToken);
return completion.Content[0].Text;
}
}
Now the application layer does not depend directly on OpenAI.
OpenAI with Dependency Injection
Register the SDK client once:
builder.Services.AddSingleton(
_ =>
{
var apiKey =
builder.Configuration[
"OpenAI:ApiKey"]
?? throw new InvalidOperationException(
"OpenAI API key is missing.");
var model =
builder.Configuration[
"OpenAI:Model"]
?? throw new InvalidOperationException(
"OpenAI model is missing.");
return new ChatClient(
model,
apiKey);
});
Then:
builder.Services.AddScoped<IAIChatService, OpenAIChatService>();
This aligns with the official SDK's guidance that its clients are thread-safe and can be registered as singletons.
OpenAI with a Provider-Neutral Architecture
For a larger system, use:
IChatClient
and configure OpenAI as the implementation.
Application
|
v
IChatClient
|
v
OpenAI
Later you can change:
OpenAI
to:
Azure OpenAI
or another supported provider without rewriting your application-level AI interfaces.
OpenAI Compatible Endpoints
The official SDK also supports configuring a custom endpoint and API key, which can be useful for OpenAI-compatible APIs or proxy endpoints. The SDK documentation demonstrates this using OpenAIClientOptions.Endpoint.
Conceptually:
var client =
new ChatClient(
model: "configured-model",
credential:
new ApiKeyCredential(apiKey),
options:
new OpenAIClientOptions
{
Endpoint =
new Uri(
"https://your-endpoint/")
});
This should only be used when the target endpoint implements the API surface expected by the SDK.
Experimental APIs
The official OpenAI .NET SDK marks some APIs as experimental while their .NET surface is still evolving. The repository documents the need to explicitly suppress the associated diagnostic when using such APIs.
For example, the current Responses examples use:
#pragma warning disable OPENAI001
This is not something to add blindly throughout the application.
Prefer stable APIs when they meet the requirement.
For experimental features:
check version
check changelog
test carefully
isolate usage
Checking SDK Versions
The OpenAI package version should be visible in your project:
<PackageReference
Include="OpenAI"
Version="2.13.0" />
NuGet currently lists 2.13.0 as the verified latest package version for this article's date.
After updating:
dotnet restore
dotnet build
dotnet test
Review the OpenAI SDK changelog before upgrading production applications. The official changelog records API-surface changes such as the evolution of ResponsesClient.
OpenAI SDK Project Structure
A practical ASP.NET Core application could use:
MyAiApp/
|
+-- Controllers/
|
+-- Application/
| +-- AI/
| +-- IAIService.cs
| +-- AIService.cs
|
+-- Infrastructure/
| +-- OpenAI/
| +-- OpenAIService.cs
| +-- OpenAIOptions.cs
|
+-- Prompts/
| +-- Support/
| +-- Summary/
| +-- Classification/
|
+-- Models/
| +-- AI/
|
+-- Program.cs
+-- appsettings.json
This keeps:
OpenAI SDK
Prompt Management
Application Logic
separate.
Complete OpenAI Service Example
using OpenAI.Chat;
public interface IOpenAIService
{
Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default);
}
public sealed class OpenAIService
: IOpenAIService
{
private readonly ChatClient _client;
public OpenAIService(
ChatClient client)
{
_client = client;
}
public async Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(prompt))
{
throw new ArgumentException(
"Prompt is required.",
nameof(prompt));
}
ChatCompletion completion =
await _client.CompleteChatAsync(
prompt,
cancellationToken);
if (completion.Content.Count == 0)
{
throw new InvalidOperationException(
"OpenAI returned no content.");
}
return completion.Content[0].Text;
}
}
Complete ASP.NET Core Setup
using OpenAI.Chat;
var builder =
WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(
_ =>
{
var apiKey =
builder.Configuration[
"OpenAI:ApiKey"]
?? throw new InvalidOperationException(
"OpenAI API key is missing.");
var model =
builder.Configuration[
"OpenAI:Model"]
?? throw new InvalidOperationException(
"OpenAI model is missing.");
return new ChatClient(
model,
apiKey);
});
builder.Services.AddScoped<
IOpenAIService,
OpenAIService>();
var app =
builder.Build();
app.MapPost(
"/api/ai",
async (
AIRequest request,
IOpenAIService service,
CancellationToken cancellationToken) =>
{
var answer =
await service.GenerateAsync(
request.Prompt,
cancellationToken);
return Results.Ok(
new
{
answer
});
});
app.Run();
public sealed class AIRequest
{
public required string Prompt { get; init; }
}
Example Request
{
"prompt": "Explain dependency injection in ASP.NET Core."
}
Response:
{
"answer": "Dependency injection is a design pattern..."
}
OpenAI with System Prompts, Templates, and JSON
The topics covered so far combine into a single architecture:
Prompt Template
|
v
System Prompt
|
+
Runtime Context
|
+
User Request
|
v
OpenAI ChatClient
|
v
JSON / Structured Output
|
v
C# DTO
|
v
Validation
|
v
Business Logic
This is the foundation for most production AI services.
Practical Project: Build an OpenAI Chat API
Create an ASP.NET Core API with:
POST /api/chat
Request:
{
"message": "Explain dependency injection."
}
The system prompt:
You are a professional .NET programming assistant.
Rules:
- Prefer current .NET concepts.
- Use practical C# examples.
- Explain important assumptions.
- Do not invent APIs.
Architecture:
POST /api/chat
|
v
ChatRequest
|
v
Chat Service
|
v
ChatClient
|
v
OpenAI
|
v
Response
Practical Project: Build a JSON Ticket Analyzer
Input:
{
"message": "My credit card was charged twice."
}
Output:
{
"category": "Billing",
"priority": "High",
"summary": "Customer reports a duplicate charge."
}
Implement:
OpenAI ChatClient
+
ChatCompletionOptions
+
JSON Schema
+
System.Text.Json
+
ASP.NET Core
This combines the current and previous two topics.
Practical Project: Build an OpenAI Document Assistant
Architecture:
Document
|
v
Text Extraction
|
v
Prompt Template
|
v
OpenAI
|
v
Structured Answer
|
+-- answer
+-- sources
+-- confidence
|
v
ASP.NET Core API
The later RAG topics will expand this into retrieval and vector search.
Practical Project: Build a Streaming AI Chat Application
Architecture:
Browser
|
v
ASP.NET Core
|
v
AI Service
|
v
ChatClient
|
v
OpenAI
|
+-- partial response
+-- partial response
+-- partial response
|
v
Browser
Use:
CompleteChatStreamingAsync
from the official SDK.
Practical Project: Build an OpenAI Tool-Calling Assistant
Build:
User
|
v
OpenAI
|
v
Tool Call
|
+-- getOrderStatus
+-- searchProducts
+-- getCustomer
|
v
.NET Application
|
v
Tool Result
|
v
OpenAI
|
v
Final Response
The application should validate every tool request before execution.
Common Mistakes
Hard-coding API keys
Avoid:
new ChatClient(
"model",
"real-secret-key");
Use environment variables or secure configuration.
Creating a client on every request
Use DI and singleton lifetime where appropriate. The official SDK documents its clients as thread-safe.
Mixing OpenAI calls into controllers
Use an application service.
Using model identifiers everywhere
Centralize model configuration.
Ignoring cancellation
Pass CancellationToken.
Treating all errors as retryable
Different failures need different handling.
Assuming experimental APIs are stable
Current Responses-related surfaces include experimental APIs in the official SDK.
Parsing AI text manually when structured output exists
Use structured output or JSON Schema when the application needs a fixed contract.
Executing tool calls without authorization
The application must control tool access.
Logging sensitive prompts
Use privacy-aware logging.
Coupling the entire application to ChatClient
Use IChatClient when provider portability is important.
Frequently Asked Questions
What is the official OpenAI SDK for .NET?
The official package is:
OpenAI
The official repository is openai/openai-dotnet.
What is the current verified OpenAI .NET package version?
The current package verified for this article is 2.13.0, released August 10, 2026.
What is ChatClient?
ChatClient is the official OpenAI .NET client for chat-completion operations.
What is ResponsesClient?
ResponsesClient is the official SDK client for the Responses API. The current .NET SDK exposes synchronous, asynchronous, and streaming response methods.
Should I use ChatClient or ResponsesClient?
Use the client surface that matches the OpenAI API capability your application needs.
For straightforward chat-completion scenarios:
ChatClient
For newer Responses API workflows:
ResponsesClient
Check the SDK version and current API documentation because the Responses .NET surface is still marked experimental in the verified current SDK.
Can I use OpenAI with IChatClient?
Yes. Microsoft provides AsIChatClient(ChatClient) and other OpenAI adapters.
Why use IChatClient instead of ChatClient?
IChatClient reduces provider coupling and lets application code share a common abstraction across different AI providers.
Is the OpenAI .NET SDK thread-safe?
The official repository states that its clients are thread-safe and can be registered as singletons in ASP.NET Core.
Can I stream OpenAI responses with C#?
Yes. ChatClient provides CompleteChatStreaming and CompleteChatStreamingAsync, and ResponsesClient provides corresponding streaming methods.
Can OpenAI with .NET perform tool calling?
Yes. The official SDK supports tools and function calling through the chat completion APIs.
Can OpenAI with .NET produce JSON?
Yes. The official SDK supports structured outputs and JSON Schema-based response formats.
Can I use embeddings?
Yes. The SDK provides EmbeddingClient.
Can I process images?
The SDK includes an ImageClient.
Can I work with audio?
The SDK includes AudioClient and the official repository documents audio examples.
Can I work with files?
Yes. The SDK exposes a file client and other file-related APIs.
Can I use vector stores?
The SDK includes VectorStoreClient.
Can I use a custom OpenAI-compatible endpoint?
The official SDK documents configuring a custom base URL using OpenAIClientOptions.Endpoint.
Should I use OpenAI directly from a browser?
Do not expose a long-lived secret API key in client-side code. Put OpenAI access behind a controlled server-side application.
Can I use OpenAI in ASP.NET Core?
Yes. The official SDK provides dependency-injection patterns, and Microsoft also provides IChatClient integration.
Can I use OpenAI with Blazor?
Yes. For server-side Blazor, the server can securely call OpenAI. For browser-executed code, keep the API key on the server and expose an application API.
Can I use OpenAI with .NET MAUI?
Yes, but the API key should not be embedded directly in the shipped client application. A mobile application should generally call your secured backend.
Are all OpenAI .NET APIs stable?
No. Some current APIs are marked experimental. The official SDK documentation explains that experimental APIs can change as their .NET design evolves.
Interview Questions
What NuGet package provides the official OpenAI .NET SDK?
OpenAI
The package is maintained by OpenAI.
What is ChatClient?
The OpenAI .NET client for chat-completion requests.
What is ResponsesClient?
The OpenAI .NET client for Responses API operations.
What is OpenAIClient?
A parent client that can create feature-specific OpenAI clients and share common underlying implementation details.
How do you authenticate?
Use an OpenAI API key, preferably supplied through secure configuration or environment variables rather than source code.
How do you call OpenAI asynchronously?
Use methods such as:
CompleteChatAsync(...)
CreateResponseAsync(...)
The SDK provides asynchronous counterparts to its synchronous API methods.
How do you stream a chat response?
Use:
CompleteChatStreamingAsync(...)
and enumerate the returned updates asynchronously.
How do you use OpenAI with IChatClient?
Use:
new OpenAIClient(apiKey)
.GetChatClient(model)
.AsIChatClient();
Microsoft documents this adapter directly.
How do you create structured output with the direct SDK?
Use ChatResponseFormat.CreateJsonSchemaFormat with ChatCompletionOptions. The official OpenAI SDK includes a structured-output example using this approach.
How should OpenAI clients be registered in ASP.NET Core?
The official SDK documents its clients as thread-safe and suitable for singleton registration.
Why should controllers not directly call OpenAI?
Separating the AI infrastructure into a service improves testing, observability, error handling, and architecture.
Why use an abstraction such as IChatClient?
It prevents business/application code from being tightly coupled to one provider.
How do you secure an OpenAI API key?
Keep it server-side and store it in secure configuration, an environment variable, or a secret-management service.
What is the difference between model configuration and prompt configuration?
Model configuration identifies the model and request settings.
Prompt configuration controls the instructions and runtime content sent to the model.
How do you test code that uses OpenAI?
Use interfaces and fakes/mocks for unit tests, then use a smaller set of real integration tests against OpenAI.
The official repository also documents mocking and testing-related advanced scenarios.
Best Practices
Keep API keys out of source code
Use:
environment variables
User Secrets
secret-management systems
Register clients once
Use DI and singleton lifetime where appropriate. The SDK documents its clients as thread-safe.
Use cancellation tokens
Especially in ASP.NET Core.
Centralize model configuration
Use:
OpenAI:Model
instead of scattering model identifiers through the codebase.
Separate prompts from OpenAI infrastructure
Use the prompt-template architecture already established in the series.
Keep OpenAI behind an application service
This makes your application easier to test and evolve.
Use IChatClient when provider abstraction matters
Direct SDK use is still appropriate for OpenAI-specific features.
Prefer asynchronous methods
Use:
CompleteChatAsync
CreateResponseAsync
in web applications and background services.
Use streaming when the UI benefits from incremental output
Chat applications are a common example.
Validate tool calls
Never treat a tool request as automatic authorization.
Validate structured responses
JSON syntax is not business correctness.
Track usage
Record model, duration, request metadata, and usage data as appropriate.
Review experimental APIs before production adoption
Current Responses-related SDK APIs are marked experimental in the verified SDK.
Keep model selection configurable
This makes model upgrades and environment-specific configuration easier.
Prefer strongly typed SDK APIs over raw HTTP
Use raw protocol methods only when you actually need lower-level access. The official SDK provides both layers.
OpenAI .NET Architecture
A practical production architecture is:
ASP.NET Core
|
v
AI Application Service
|
+---------------+---------------+
| |
v v
Prompt Management Context Management
| |
+---------------+---------------+
|
v
IChatClient
|
v
OpenAI ChatClient
|
v
OpenAI API
|
+---------------------+----------------------+
| | |
v v v
Chat Tools Structured
Output
For OpenAI-specific services:
OpenAIClient
|
+--------------------+--------------------+
| | |
v v v
ChatClient EmbeddingClient ImageClient
| | |
+--------------------+--------------------+
|
v
OpenAI API
OpenAI .NET Learning Path
A practical sequence is:
1. OpenAI with .NET
|
v
2. OpenAI API with C#
|
v
3. OpenAI SDK with .NET
|
v
4. OpenAI Chat Applications
|
v
5. OpenAI Text Generation
|
v
6. OpenAI Streaming
|
v
7. OpenAI Function Calling
|
v
8. OpenAI Tool Calling
|
v
9. OpenAI Structured Outputs
|
v
10. OpenAI Embeddings
|
v
11. OpenAI Vision
|
v
12. OpenAI Multimodal AI
This matches the progression of the .NET + AI roadmap.
Key Takeaways
The official OpenAI .NET SDK gives C# applications a strongly typed way to work with OpenAI services.
The core client types are:
OpenAIClient
ChatClient
ResponsesClient
EmbeddingClient
ImageClient
AudioClient
OpenAIFileClient
VectorStoreClient
ModerationClient
RealtimeClient
The official SDK documents these client areas directly.
For ordinary chat:
ChatClient client =
new(model, apiKey);
ChatCompletion completion =
await client.CompleteChatAsync(
prompt);
For streaming:
await foreach (
StreamingChatCompletionUpdate update
in client.CompleteChatStreamingAsync(
prompt))
{
Console.Write(
update.ContentUpdate);
}
For the Responses API:
ResponsesClient client =
new(apiKey);
ResponseResult response =
await client.CreateResponseAsync(
"configured-model",
prompt);
The current official Responses examples mark this API surface as experimental, so check the installed SDK version and changelog before using it in a long-lived production codebase.
For provider-neutral .NET architecture:
IChatClient chatClient =
new OpenAIClient(apiKey)
.GetChatClient(model)
.AsIChatClient();
Microsoft documents this adapter as part of the OpenAI integration for Microsoft.Extensions.AI.
The key architectural separation is:
Application Logic
|
v
AI Abstraction
|
v
OpenAI SDK
|
v
OpenAI API
rather than:
Controller
|
+-- API key
+-- prompt
+-- model configuration
+-- OpenAI request
+-- error handling
+-- business logicThe next topic in the roadmap is OpenAI API with C#, where the OpenAI API itself will be examined more deeply: requests, authentication, endpoints, payloads, response objects, chat versus Responses API concepts, error handling, and direct API-level control.
.jpg)
Post a Comment