The official OpenAI .NET SDK gives C# and .NET applications a strongly typed way to communicate with the OpenAI API.
Instead of manually building:
HTTP requests
JSON payloads
Authorization headers
Response parsers
Streaming handlers
Tool-call processing
you can use .NET client classes such as:
ChatClient
ResponsesClient
EmbeddingClient
ImageClient
AudioClient
OpenAIFileClient
VectorStoreClient
ModerationClient
RealtimeClient
OpenAIClient
The official SDK is generated from OpenAI's OpenAPI specification in collaboration with Microsoft. The current NuGet release verified for this article is OpenAI 2.14.0, released on September 15, 2026. The package supports .NET 8 and higher and .NET Standard 2.0.
The SDK's overall architecture is:
.NET Application
|
v
OpenAI SDK
|
+----------------+----------------+
| | |
v v v
ChatClient ResponsesClient Other Clients
| | |
+----------------+----------------+
|
v
OpenAI API
The official repository currently documents chat completions, streaming, tools and function calling, structured outputs, audio, Responses streaming and reasoning, file search, web search, embeddings, images, audio transcription, Azure OpenAI integration, testing, retries, and observability.
What Is the OpenAI SDK for .NET?
The OpenAI SDK is a .NET library that wraps the OpenAI REST API with C# client classes and request/response types.
Without an SDK:
C#
|
+-- HttpClient
+-- JSON
+-- Headers
+-- Authentication
+-- Response parsing
+-- Streaming parsing
+-- Error handling
|
v
OpenAI API
With the SDK:
C#
|
v
ChatClient
|
v
OpenAI API
or:
C#
|
v
ResponsesClient
|
v
OpenAI API
This is particularly valuable in larger applications because the SDK exposes API concepts through C# objects rather than requiring every service to construct HTTP payloads manually.
Current OpenAI NuGet Package
Install the current verified package:
dotnet add package OpenAI --version 2.14.0
The current NuGet package page lists 2.14.0 as the latest published version, dated September 15, 2026.
The project can contain:
<ItemGroup>
<PackageReference Include="OpenAI" Version="2.14.0" />
</ItemGroup>
For future projects, check NuGet before installing so the package version is current at the time the project is created.
.NET Version Compatibility
The current package targets .NET 8.0 and is also compatible with later versions, and the package also supports .NET Standard 2.0.
For a modern application, a typical project is:
dotnet new console -n OpenAISdkDemo
or:
dotnet new webapi -n OpenAISdkApi
For the examples in this article, .NET 10 syntax is used where convenient. The official SDK documentation currently uses .NET 10 examples.
API Key Setup
The SDK needs an OpenAI API key.
The official SDK documentation recommends keeping the key in a secure location and using environment variables or configuration instead of placing it in source control.
For Windows PowerShell:
$env:OPENAI_API_KEY="your-api-key"
For a persistent Windows environment variable:
setx OPENAI_API_KEY "your-api-key"
For Linux/macOS:
export OPENAI_API_KEY="your-api-key"
Read it from C#:
var apiKey =
Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new InvalidOperationException(
"OPENAI_API_KEY is not configured.");
Never place the real API key in:
Git
GitHub
appsettings.json committed to source control
JavaScript
browser code
Blazor WebAssembly
mobile application binaries
Understanding the SDK Namespace Organization
The current SDK organizes clients by API feature area.
The official README lists namespaces and their corresponding clients as follows: OpenAI.Chat → ChatClient, OpenAI.Responses → ResponsesClient, OpenAI.Audio → AudioClient, OpenAI.Embeddings → EmbeddingClient, OpenAI.Images → ImageClient, OpenAI.Files → OpenAIFileClient, OpenAI.Models → OpenAIModelClient, OpenAI.Moderations → ModerationClient, OpenAI.Realtime → RealtimeClient, and others.
The important mapping is:
OpenAI.Chat
-> ChatClient
OpenAI.Responses
-> ResponsesClient
OpenAI.Embeddings
-> EmbeddingClient
OpenAI.Images
-> ImageClient
OpenAI.Audio
-> AudioClient
OpenAI.Files
-> OpenAIFileClient
OpenAI.VectorStores
-> VectorStoreClient
OpenAI.Moderations
-> ModerationClient
OpenAI.Realtime
-> RealtimeClient
This organization makes it easier to locate the appropriate client for a capability.
The OpenAIClient Class
The SDK also provides a parent:
using OpenAI;
with:
OpenAIClient
The official documentation describes OpenAIClient as a convenience for creating multiple feature-specific clients while sharing implementation details.
Example:
using OpenAI;
var apiKey =
Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new InvalidOperationException(
"OPENAI_API_KEY is not configured.");
OpenAIClient client =
new(apiKey);
Then create feature clients:
var chatClient =
client.GetChatClient(model);
var audioClient =
client.GetAudioClient(audioModel);
This is useful when one application works with multiple OpenAI APIs.
Direct Client vs OpenAIClient
You can construct:
ChatClient chatClient =
new(model, apiKey);
or:
OpenAIClient openAIClient =
new(apiKey);
ChatClient chatClient =
openAIClient.GetChatClient(model);
Use the parent client when you expect multiple OpenAI feature clients to share configuration and implementation details.
For a tiny application, directly constructing ChatClient is perfectly reasonable.
Your First OpenAI SDK Application
Create:
dotnet new console -n OpenAISdkDemo
cd OpenAISdkDemo
dotnet add package OpenAI --version 2.14.0
Then:
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 official SDK documents CompleteChat and CompleteChatAsync as the basic ChatClient operations.
Understanding ChatClient
ChatClient belongs to:
OpenAI.Chat
It is designed for chat-completion operations.
The architecture is:
ChatClient
|
+-- CompleteChat
+-- CompleteChatAsync
+-- CompleteChatStreaming
+-- CompleteChatStreamingAsync
|
+-- tools
+-- structured outputs
+-- audio
The SDK documents the synchronous and asynchronous API pairs.
For ASP.NET Core applications, prefer the asynchronous operations:
await client.CompleteChatAsync(...);
rather than blocking calls.
Creating Chat Messages
For a simple prompt:
var completion =
await client.CompleteChatAsync(
"What is dependency injection?");
For a conversation with roles:
using OpenAI.Chat;
List<ChatMessage> messages =
[
new SystemChatMessage(
"You are a professional .NET instructor."),
new UserChatMessage(
"Explain dependency injection.")
];
ChatCompletion completion =
await client.CompleteChatAsync(
messages);
This gives you explicit role separation.
System Prompt
A system message can define application behavior:
var messages =
new List<ChatMessage>
{
new SystemChatMessage(
"""
You are a .NET programming assistant.
Prefer practical C# examples.
Do not invent API names.
Explain important assumptions.
"""),
new UserChatMessage(
"Explain dependency injection.")
};
Then:
var completion =
await client.CompleteChatAsync(
messages);
This integrates directly with the system-prompt and prompt-template architecture developed in earlier topics.
Conversation History
You can maintain messages yourself:
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?"));
ChatCompletion second =
await client.CompleteChatAsync(
messages);
The important pattern is:
System
|
User
|
Assistant
|
User
|
Assistant
This fits directly into the context-management concepts covered earlier in the series.
Reusing ChatClient
The official SDK states that its clients are thread-safe and can safely be registered as singletons in ASP.NET Core dependency injection.
Therefore, avoid:
public async Task<string> AskAsync()
{
var client =
new ChatClient(model, apiKey);
...
}
on every request.
Prefer dependency injection.
Dependency Injection with ASP.NET Core
The current official repository documents dependency-injection support for the OpenAI clients and states that they can be registered as singletons.
A typical application architecture is:
Program.cs
|
v
ChatClient
|
v
AI Service
|
v
Controller / Endpoint
For example:
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:
public sealed class AIService
{
private readonly ChatClient _client;
public AIService(ChatClient client)
{
_client = client;
}
}
Using OpenAIClient with Dependency Injection
When several capabilities are required:
builder.Services.AddSingleton(
_ =>
{
var apiKey =
builder.Configuration["OpenAI:ApiKey"]
?? throw new InvalidOperationException(
"OpenAI API key is missing.");
return new OpenAIClient(apiKey);
});
Then create feature-specific clients inside a factory or service composition layer.
For example:
public sealed class OpenAIServiceFactory
{
private readonly OpenAIClient _client;
private readonly IConfiguration _configuration;
public OpenAIServiceFactory(
OpenAIClient client,
IConfiguration configuration)
{
_client = client;
_configuration = configuration;
}
public ChatClient CreateChatClient()
{
var model =
_configuration["OpenAI:Model"]
?? throw new InvalidOperationException(
"OpenAI model is missing.");
return _client.GetChatClient(model);
}
}
For larger systems, registering the exact feature clients your services consume is usually clearer.
Configuration
Use:
{
"OpenAI": {
"Model": "your-model-name"
}
}
Keep the API key in:
User Secrets
Environment Variables
Azure Key Vault
Secure Secret Store
For local development:
dotnet user-secrets init
dotnet user-secrets set "OpenAI:ApiKey" "your-api-key"
Then:
var apiKey =
configuration["OpenAI:ApiKey"];
Strongly Typed Configuration
Create:
public sealed class OpenAIOptions
{
public string? ApiKey { get; init; }
public string? Model { get; init; }
}
Register:
builder.Services.Configure<OpenAIOptions>(
builder.Configuration.GetSection("OpenAI"));
Then use:
public sealed class OpenAIChatService
{
private readonly ChatClient _client;
public OpenAIChatService(
IOptions<OpenAIOptions> options)
{
var settings = options.Value;
if (string.IsNullOrWhiteSpace(settings.ApiKey))
{
throw new InvalidOperationException(
"OpenAI API key is missing.");
}
if (string.IsNullOrWhiteSpace(settings.Model))
{
throw new InvalidOperationException(
"OpenAI model is missing.");
}
_client =
new ChatClient(
settings.Model,
settings.ApiKey);
}
}
For an actual application, registering the client itself as a singleton through DI is preferable to constructing it inside every service instance.
ChatCompletion
The result of a ChatClient request is:
ChatCompletion
You can inspect:
completion.Content
completion.FinishReason
completion.ToolCalls
completion.Usage
Conceptually:
ChatCompletion
|
+-- Content
+-- FinishReason
+-- ToolCalls
+-- Usage
+-- Metadata
The exact available members depend on the SDK version and request type.
Reading Text Safely
A basic example is:
var text =
completion.Content[0].Text;
For production code, don't blindly assume there is always at least one content item.
Use a helper:
private static string ExtractText(
ChatCompletion completion)
{
foreach (
ChatMessageContentPart part
in completion.Content)
{
if (!string.IsNullOrWhiteSpace(part.Text))
{
return part.Text;
}
}
return string.Empty;
}
Then:
var text =
ExtractText(completion);
This becomes more robust when responses contain different content parts.
Chat Options
The SDK provides:
ChatCompletionOptions
for request configuration.
For example:
ChatCompletionOptions options =
new()
{
MaxOutputTokens = 500
};
Then:
ChatCompletion completion =
await client.CompleteChatAsync(
messages,
options);
The exact properties available should be checked against the installed SDK version because OpenAI's API and model capabilities evolve.
Structured Outputs
The official SDK supports JSON Schema-based structured outputs for Chat Completions.
The current official example uses:
ChatResponseFormat.CreateJsonSchemaFormat(...)
and configures:
jsonSchemaIsStrict: true
before sending the request.
Example:
var options =
new ChatCompletionOptions
{
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);
Parse:
using JsonDocument json =
JsonDocument.Parse(
completion.Content[0].Text);
The official SDK's current structured-output example follows this general pattern.
Streaming with ChatClient
The SDK supports streaming chat completions.
Non-streaming:
ChatCompletion completion =
await client.CompleteChatAsync(
prompt);
Streaming:
AsyncCollectionResult<StreamingChatCompletionUpdate>
updates =
client.CompleteChatStreamingAsync(
prompt);
Then:
await foreach (
StreamingChatCompletionUpdate update
in updates)
{
foreach (
ChatMessageContentPart part
in update.ContentUpdate)
{
Console.Write(
part.Text);
}
}
The official README documents both CompleteChatStreaming and CompleteChatStreamingAsync.
Why Streaming Matters
Without streaming:
Request
|
v
Wait
|
v
Complete response
With streaming:
Request
|
+-- Chunk 1
+-- Chunk 2
+-- Chunk 3
+-- Chunk 4
|
v
Complete response
Streaming is useful for:
chat applications
AI assistants
long answers
interactive applications
real-time user interfaces
Streaming in ASP.NET Core
A service can expose an async stream:
public async IAsyncEnumerable<string> StreamAsync(
string prompt,
[EnumeratorCancellation]
CancellationToken cancellationToken = default)
{
AsyncCollectionResult<StreamingChatCompletionUpdate>
updates =
_client.CompleteChatStreamingAsync(
prompt,
cancellationToken);
await foreach (
var update in updates
.WithCancellation(cancellationToken))
{
foreach (
var content
in update.ContentUpdate)
{
if (!string.IsNullOrEmpty(content.Text))
{
yield return content.Text;
}
}
}
}
Then the ASP.NET Core layer can forward the chunks through:
SSE
HTTP streaming
SignalR
depending on the UI architecture.
Tools and Function Calling
The SDK provides first-class tool support.
The official README demonstrates creating tools with:
ChatTool.CreateFunctionTool(...)
and supplying them through:
ChatCompletionOptions.Tools
When the model returns ChatFinishReason.ToolCalls, the application executes the requested functions and sends their results back to the model.
The architecture is:
User
|
v
ChatClient
|
v
OpenAI
|
v
Tool Call
|
v
.NET Application
|
v
Execute Tool
|
v
Tool Result
|
v
OpenAI
|
v
Final Response
Creating a Tool
Example:
ChatTool getOrderStatusTool =
ChatTool.CreateFunctionTool(
functionName:
"get_order_status",
functionDescription:
"Gets the current status of an order.");
For arguments:
ChatTool getOrderTool =
ChatTool.CreateFunctionTool(
functionName:
"get_order",
functionDescription:
"Gets an order by its ID.",
functionParameters:
BinaryData.FromString(
"""
{
"type": "object",
"properties": {
"orderId": {
"type": "string"
}
},
"required": [
"orderId"
],
"additionalProperties": false
}
"""));
Supplying Tools
ChatCompletionOptions options =
new()
{
Tools =
{
getOrderStatusTool,
getOrderTool
}
};
Then:
ChatCompletion completion =
await client.CompleteChatAsync(
messages,
options);
Handling Tool Calls
Inspect:
if (completion.FinishReason ==
ChatFinishReason.ToolCalls)
{
...
}
The official SDK's documented pattern is:
1. Receive tool calls.
2. Add the assistant message containing those calls to history.
3. Execute the application functions.
4. Add tool results.
5. Call CompleteChat again.
6. Repeat if needed.
The SDK README explicitly warns that model-generated function arguments may be hallucinated and must be parsed and validated before the function is executed.
Tool Argument Validation
Suppose the model requests:
{
"orderId": "ORD-10025"
}
Parse:
using JsonDocument arguments =
JsonDocument.Parse(
toolCall.FunctionArguments);
if (!arguments.RootElement.TryGetProperty(
"orderId",
out JsonElement orderIdElement))
{
throw new InvalidOperationException(
"orderId is required.");
}
string? orderId =
orderIdElement.GetString();
Then validate:
if (string.IsNullOrWhiteSpace(orderId))
{
throw new InvalidOperationException(
"Order ID is invalid.");
}
The SDK documentation specifically emphasizes validation of tool arguments before calling the application function.
Tool Security
A model requesting:
deleteOrder
does not mean your application should execute it.
Use:
Authentication
Authorization
Input Validation
Tool Allowlist
Resource Ownership
Business Rules
The SDK handles the API interaction.
Your application controls the business operation.
Responses API with ResponsesClient
The current SDK also contains:
OpenAI.Responses.ResponsesClient
The official repository documents Responses support including:
streaming
reasoning
file search
web search
and other capabilities.
A basic example is:
#pragma warning disable OPENAI001
using OpenAI.Responses;
var client =
new ResponsesClient(apiKey);
ResponseResult response =
await client.CreateResponseAsync(
model,
"Explain dependency injection in ASP.NET Core.");
Console.WriteLine(
response.GetOutputText());
#pragma warning restore OPENAI001
The current .NET SDK marks some Responses APIs experimental, which is why the current official examples use the OPENAI001 suppression.
Why ResponsesClient Is Important
The SDK exposes both:
ChatClient
and:
ResponsesClient
The distinction is:
ChatClient
|
+-- Chat Completions API
ResponsesClient
|
+-- Responses API
For new application architecture, it is useful to understand both rather than treating them as interchangeable client classes.
Responses Streaming
The official SDK documents streaming for Responses as well.
Conceptually:
ResponsesClient
|
v
CreateResponseStreamingAsync
|
v
StreamingResponseUpdate
This is useful for interactive applications and agent-style workflows.
Responses Reasoning
The current 2.14.0 SDK includes additional reasoning-related capabilities, including expanded reasoning-effort values and control over how reasoning context is preserved across turns. The 2.14.0 changelog documents ResponseReasoningContext with Auto, CurrentTurn, and AllTurns, plus a corresponding property on ResponseReasoningOptions.
This is an example of why pinning and reviewing SDK versions matters: the SDK's capabilities evolve quickly.
Custom Tools in Responses
The current 2.14.0 release also added support for Custom Tools in OpenAI.Responses.
Custom tools allow arbitrary string input instead of requiring the model to produce JSON arguments matching a function schema. The 2.14.0 changelog documents CustomTool, custom-tool call/output items, and CustomToolTextFormat and CustomToolGrammarFormat.
Conceptually:
Traditional Function Tool
|
v
JSON Arguments
Custom Tool
|
v
Arbitrary String / Grammar
This can be useful when the desired tool input is not naturally represented as an ordinary JSON object.
OpenAI 2.14.0 New Features
The current SDK release also added several capabilities.
Audio streaming
AudioClient gained streaming protocol methods including:
GenerateSpeechStreamingAsync
TranscribeAudioStreamingAsync
in version 2.14.0.
Image streaming
The release added streaming protocol methods for image generation and image editing.
Responses custom tools
Custom tools were added to Responses.
Cache-write usage information
Responses now expose the number of input tokens newly written to the cache through usage details.
OpenTelemetry GenAI conventions
Chat gained opt-in support for newer experimental OpenTelemetry GenAI semantic conventions.
These features show how the official SDK is moving beyond basic chat completion into a broader AI application platform.
Embeddings with EmbeddingClient
The SDK provides:
OpenAI.Embeddings.EmbeddingClient
for text embeddings.
The architecture is:
Text
|
v
EmbeddingClient
|
v
Embedding Vector
|
v
Vector Store
A simple application may use embeddings for:
semantic search
RAG
similarity
document indexing
recommendations
classification
The official SDK lists EmbeddingClient as part of its feature-specific client organization.
Embedding Architecture
Document
|
v
Chunk
|
v
EmbeddingClient
|
v
Vector
|
v
Vector Database
Later in the roadmap, embeddings and vector databases will be covered in much greater detail.
Image Generation with ImageClient
The SDK includes:
OpenAI.Images.ImageClient
which is the client for image-related operations.
Architecture:
Prompt
|
v
ImageClient
|
v
OpenAI
|
v
Image Result
The current 2.14.0 SDK also added streaming protocol methods for image generation and image editing.
Audio with AudioClient
The SDK includes:
OpenAI.Audio.AudioClient
for audio functionality.
Typical uses include:
speech generation
transcription
audio processing
voice applications
The 2.14.0 release adds streaming operations for speech generation and transcription.
Files with OpenAIFileClient
The SDK includes:
OpenAI.Files.OpenAIFileClient
for file operations.
File-based workflows can support:
document processing
retrieval
file search
AI knowledge bases
Vector Stores
The SDK also exposes:
OpenAI.VectorStores.VectorStoreClient
which becomes useful for retrieval-based architectures.
Later topics in this series will combine:
EmbeddingClient
+
VectorStoreClient
+
ResponsesClient
to build RAG applications.
Moderation
The SDK provides:
OpenAI.Moderations.ModerationClient
for moderation operations.
A conceptual application pipeline is:
User Input
|
v
Moderation
|
v
AI Request
|
v
Response
|
v
Application
The exact policy depends on the application and its safety requirements.
Realtime
The SDK also includes:
OpenAI.Realtime.RealtimeClient
for realtime functionality.
This becomes relevant for:
voice applications
realtime assistants
audio streaming
low-latency interactions
The 2.14.0 release also expands extensibility across realtime client commands, server updates, items, and tools.
OpenAI SDK and Microsoft.Extensions.AI
A major .NET architecture option is adapting the OpenAI SDK to:
IChatClient
Microsoft's Microsoft.Extensions.AI.OpenAI integration exposes:
AsIChatClient(ChatClient)
and also an overload for adapting ResponsesClient to IChatClient.
Example:
using Microsoft.Extensions.AI;
using OpenAI;
IChatClient chatClient =
new OpenAIClient(apiKey)
.GetChatClient(model)
.AsIChatClient();
This gives:
OpenAI SDK
|
v
ChatClient
|
v
AsIChatClient()
|
v
IChatClient
Why Use IChatClient?
Direct SDK:
Application
|
v
ChatClient
|
v
OpenAI
Provider-neutral:
Application
|
v
IChatClient
|
v
OpenAI
The second architecture allows the application layer to work with other providers later.
This is especially useful for:
multi-provider applications
local AI
Azure OpenAI
testing
AI abstraction layers
ResponsesClient as IChatClient
Microsoft's current OpenAI integration also provides:
AsIChatClient(
ResponsesClient,
defaultModelId)
for adapting Responses to IChatClient. The overload is currently marked experimental.
This means an application can use:
ResponsesClient
|
v
IChatClient
rather than writing a separate abstraction around Responses.
IEmbeddingGenerator
Microsoft's OpenAI extension also provides an adapter from:
EmbeddingClient
to:
IEmbeddingGenerator<TInput,TEmbedding>
The current extension API documents AsIEmbeddingGenerator.
This becomes particularly useful in later RAG and vector-search topics.
Other Microsoft AI Adapters
The current Microsoft OpenAI integration also exposes adapters for:
ImageClient
AudioClient
OpenAIFileClient
into abstractions such as:
IImageGenerator
ISpeechToTextClient
ITextToSpeechClient
IHostedFileClient
The current extension catalog lists these adapters.
This means an application can build around Microsoft abstractions while using the official OpenAI SDK underneath.
Choosing Direct SDK vs IChatClient
A useful architecture decision is:
Need provider-specific feature?
|
v
Official OpenAI SDK
or:
Need provider portability?
|
v
Microsoft.Extensions.AI
Both approaches can coexist.
For example:
Application
|
+---- IChatClient
| |
| v
| OpenAI
|
+---- OpenAI-specific service
|
v
ResponsesClient
ASP.NET Core Application
Create a Web API:
dotnet new webapi -n OpenAISdkApi
cd OpenAISdkApi
dotnet add package OpenAI --version 2.14.0
Create configuration:
{
"OpenAI": {
"Model": "your-model-name"
}
}
Keep the API key in User Secrets:
dotnet user-secrets init
dotnet user-secrets set "OpenAI:ApiKey" "your-api-key"
Registering ChatClient
using OpenAI.Chat;
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);
});
The official SDK documents singleton registration as safe because its clients are thread-safe.
AI Service
using OpenAI.Chat;
public sealed class AIService
{
private readonly ChatClient _client;
public AIService(
ChatClient client)
{
_client = client;
}
public async Task<string> AskAsync(
string question,
CancellationToken cancellationToken = default)
{
ChatCompletion completion =
await _client.CompleteChatAsync(
question,
cancellationToken);
foreach (
ChatMessageContentPart content
in completion.Content)
{
if (!string.IsNullOrWhiteSpace(
content.Text))
{
return content.Text;
}
}
return string.Empty;
}
}
Register:
builder.Services.AddScoped<AIService>();
Minimal API
app.MapPost(
"/api/ai",
async (
AIRequest request,
AIService service,
CancellationToken cancellationToken) =>
{
var answer =
await service.AskAsync(
request.Message,
cancellationToken);
return Results.Ok(
new
{
answer
});
});
Request:
public sealed class AIRequest
{
public required string Message { get; init; }
}
Architecture:
POST /api/ai
|
v
AIRequest
|
v
AIService
|
v
ChatClient
|
v
OpenAI
Official SDK Dependency Injection Configuration
The current OpenAI .NET repository also documents an IHostApplicationBuilder extension such as:
builder.AddChatClient("Clients:ChatClient");
for binding client settings from configuration and registering the client. The repository contains a dedicated ASP.NET Core dependency-injection example.
This can be useful when you want configuration-driven client construction instead of manually creating the client in Program.cs.
Configuration-Driven Client
A conceptual configuration structure is:
{
"Clients": {
"ChatClient": {
"Model": "your-model-name",
"Credential": {
"Key": "..."
}
}
}
}
The official ASP.NET Core sample similarly uses a named configuration section and client settings.
Because the exact configuration surface can evolve, use the version-matched SDK sample when adopting this pattern in production.
Custom Endpoint
The SDK supports custom base URLs.
The official README demonstrates supplying an ApiKeyCredential and OpenAIClientOptions.Endpoint.
Example:
using OpenAI;
using System.ClientModel;
var client =
new ChatClient(
model: "configured-model",
credential:
new ApiKeyCredential(apiKey),
options:
new OpenAIClientOptions
{
Endpoint =
new Uri(
"https://your-endpoint/")
});
This can be useful for:
OpenAI-compatible services
proxies
custom gateways
special deployments
The target endpoint must implement the API surface expected by the SDK.
SDK and OpenAI-Compatible APIs
An important advantage of the SDK's configurable endpoint is that your C# application can sometimes communicate with an OpenAI-compatible service through the same client abstractions.
Architecture:
Application
|
v
OpenAI SDK
|
v
Custom Endpoint
|
v
OpenAI-Compatible Service
Do not assume complete compatibility merely because an endpoint advertises an OpenAI-like API. Test the specific operations your application uses.
Async API
The official SDK provides asynchronous counterparts for client methods.
For example:
CompleteChat
has:
CompleteChatAsync
The current README explicitly documents this pattern.
For ASP.NET Core:
await client.CompleteChatAsync(...);
should generally be preferred.
CancellationToken
Pass cancellation tokens through your service layers:
var completion =
await client.CompleteChatAsync(
messages,
cancellationToken);
Architecture:
HTTP Request
|
v
CancellationToken
|
v
AI Service
|
v
OpenAI SDK
|
v
OpenAI
When the user cancels a request, your application can stop work instead of continuing unnecessarily.
Chat Tool Workflow
A complete tool workflow looks like:
User
|
v
ChatClient
|
v
OpenAI
|
+---- Stop ------> Final Answer
|
+---- ToolCalls -> Application
|
v
Validate Arguments
|
v
Authorize Tool
|
v
Execute Tool
|
v
ToolChatMessage
|
v
ChatClient
|
v
OpenAI
The SDK's official tool-calling example uses this loop.
Structured Output Workflow
A typed application can define:
public sealed class TicketAnalysis
{
public string? Category { get; set; }
public string? Priority { get; set; }
public string? Summary { get; set; }
}
Then define JSON Schema:
var options =
new ChatCompletionOptions
{
ResponseFormat =
ChatResponseFormat.CreateJsonSchemaFormat(
"ticket_analysis",
BinaryData.FromString(
"""
{
"type": "object",
"properties": {
"category": {
"type": "string"
},
"priority": {
"type": "string"
},
"summary": {
"type": "string"
}
},
"required": [
"category",
"priority",
"summary"
],
"additionalProperties": false
}
"""),
jsonSchemaIsStrict: true)
};
This uses the direct OpenAI SDK's Chat Completions structured-output API.
SDK Structured Output vs Microsoft.Extensions.AI
Direct SDK:
ChatCompletionOptions
|
v
ChatResponseFormat.CreateJsonSchemaFormat
|
v
ChatCompletion
Provider-neutral abstraction:
IChatClient
|
v
GetResponseAsync<T>
|
v
ChatResponse<T>
The latter is often cleaner for a provider-neutral application.
Prompt Templates with the SDK
The prompt-template layer from the previous article works directly with the SDK.
Template:
Summarize the following document for {{audience}}.
Document:
{{document}}
Render:
var prompt =
renderer.Render(
template,
new Dictionary<string, string>
{
["audience"] =
"senior .NET developers",
["document"] =
document
});
Then:
ChatCompletion completion =
await chatClient.CompleteChatAsync(
prompt);
The SDK should not own your prompt-management system.
Your application should.
System Prompts with the SDK
Use:
List<ChatMessage> messages =
[
new SystemChatMessage(
systemPrompt),
new UserChatMessage(
userPrompt)
];
Then:
ChatCompletion completion =
await client.CompleteChatAsync(
messages);
This gives you the separation:
System Prompt
=
behavior
User Prompt
=
request
OpenAI SDK
=
transport / model interaction
Context Management with the SDK
A production chat application should not blindly send unlimited history.
Architecture:
Conversation History
|
v
Context Manager
|
+-- reduction
+-- summary
+-- relevance
+-- token budget
|
v
ChatClient
This connects directly to the earlier topic, AI Context Management with .NET.
SDK and JSON Responses
You can use the SDK with:
ChatResponseFormat
for JSON.
For example:
ChatCompletionOptions options =
new()
{
ResponseFormat =
ChatResponseFormat.JsonObject
};
Use the exact response-format member supported by the SDK version you install; for strict contracts, prefer CreateJsonSchemaFormat.
Then:
ChatCompletion completion =
await client.CompleteChatAsync(
prompt,
options);
Parse with:
using JsonDocument json =
JsonDocument.Parse(
completion.Content[0].Text);
SDK and System.Text.Json
The OpenAI SDK does not eliminate the need to understand JSON.
You may still use:
JsonSerializer
JsonDocument
JsonElement
JsonNode
for:
tool arguments
structured-output processing
dynamic API payloads
custom schemas
logging
testing
The SDK gives you API-level C# types.
System.Text.Json handles general-purpose application JSON.
SDK and Embeddings
Create:
EmbeddingClient
for embedding operations.
A typical service might be:
public sealed class EmbeddingService
{
private readonly EmbeddingClient _client;
public EmbeddingService(
EmbeddingClient client)
{
_client = client;
}
}
Then:
Text
|
v
EmbeddingClient
|
v
Vector
|
v
Vector Store
The official SDK documents EmbeddingClient as the client for embeddings.
SDK and Images
Use:
ImageClient
for image-related operations.
Architecture:
Prompt
|
v
ImageClient
|
v
OpenAI
|
v
Image
The SDK also added streaming methods for image generation and image editing in version 2.14.0.
SDK and Audio
Use:
AudioClient
for audio functionality.
Version 2.14.0 adds:
GenerateSpeechStreamingAsync
TranscribeAudioStreamingAsync
to the audio client.
This allows:
Audio input
|
v
AudioClient
|
v
Transcription
Text input
|
v
AudioClient
|
v
Speech output
SDK and Realtime
For realtime scenarios:
RealtimeClient
is available.
Architecture:
Client Application
|
v
RealtimeClient
|
v
OpenAI Realtime
|
+-- audio
+-- events
+-- tools
+-- responses
Version 2.14.0 adds additional extensibility around realtime command/update/item/tool type hierarchies.
SDK and Files
Use:
OpenAIFileClient
for file operations.
This becomes useful when building:
document assistants
file search
knowledge systems
RAG
document extraction
SDK and Vector Stores
Use:
VectorStoreClient
for vector-store related operations.
A later RAG application might look like:
Documents
|
v
OpenAIFileClient
|
v
Vector Store
|
v
Retrieval
|
v
ResponsesClient
SDK and Moderation
Use:
ModerationClient
for moderation workflows.
A complete service might have:
Input
|
v
Moderation
|
v
Prompt
|
v
OpenAI
|
v
Output
The exact policy should depend on the application.
Error Handling
Do not wrap every OpenAI call in:
try
{
...
}
catch
{
return "Something went wrong.";
}
That hides useful diagnostic information.
A better structure is:
OpenAI SDK
|
v
Known SDK Exception
|
v
Application Error Mapping
|
+-- client error
+-- rate limit
+-- transient error
+-- cancellation
+-- model/configuration error
Then return appropriate API responses.
Cancellation
Always pass the cancellation token:
await client.CompleteChatAsync(
messages,
cancellationToken);
This is especially important in ASP.NET Core.
Retry Strategy
The official SDK documents automatic retry capabilities as an advanced scenario.
Still, application-level retry behavior should be carefully designed.
Use retries mainly for transient conditions.
Avoid blindly retrying:
invalid request
authentication failure
invalid tool arguments
schema configuration errors
SDK Testing
The official SDK documents mocking clients as an advanced testing scenario.
For application code, an additional abstraction can make testing even simpler:
public interface IAIChatService
{
Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default);
}
Production:
IAIChatService
|
v
OpenAI ChatClient
Test:
IAIChatService
|
v
Fake AI Service
This keeps unit tests independent of the network.
Integration Testing
Use real OpenAI calls for selected integration tests.
Test:
SDK authentication
model availability
structured output
tool calling
streaming
expected response shape
Do not make every unit test call a remote AI model.
Mocking at the SDK Boundary
Your application can depend on an interface:
public interface IAIClient
{
Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default);
}
Implementation:
public sealed class OpenAIClientAdapter
: IAIClient
{
private readonly ChatClient _client;
public OpenAIClientAdapter(
ChatClient client)
{
_client = client;
}
public async Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default)
{
var result =
await _client.CompleteChatAsync(
prompt,
cancellationToken);
return result.Content.Count == 0
? string.Empty
: result.Content[0].Text;
}
}
Test implementation:
public sealed class FakeAIClient
: IAIClient
{
public Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default)
{
return Task.FromResult(
"Test response");
}
}
Observability
The official SDK includes observability guidance, and the current 2.14.0 release added opt-in support for newer experimental OpenTelemetry GenAI semantic conventions in Chat.
A useful observability model is:
ASP.NET Trace
|
v
AI Operation
|
+-- Model
+-- Prompt Version
+-- Duration
+-- Token Usage
+-- Finish Reason
+-- Error
SDK Telemetry
The 2.13.0 SDK introduced structured SDK platform metadata headers and an opt-out mechanism for SDK telemetry via:
OpenAI.DisableTelemetry
or:
OPENAI_DISABLE_TELEMETRY
when enabled, according to the changelog.
This is different from your application's AI observability.
Your own telemetry should still record the application-level information necessary to operate the service.
Request Correlation
A production application can maintain:
TraceId
CorrelationId
OpenAI Request ID
Prompt Version
Model
Example:
TraceId: 4F8C...
Operation: ResumeAnalysis
PromptVersion: v3
Model: configured-model
OpenAIRequestId: ...
This makes failures much easier to diagnose.
Client Lifetime
The official SDK states that OpenAI clients are thread-safe and can be safely registered as singletons.
Therefore:
Good
DI
|
v
Singleton ChatClient
|
+-- Request 1
+-- Request 2
+-- Request 3
rather than:
Every HTTP Request
|
+-- new ChatClient
The singleton approach also supports more efficient resource and HTTP connection reuse according to the official documentation.
Multiple Models
One application might use different models for different operations:
Chat Service
|
+-- model A
Summarization Service
|
+-- model B
Embedding Service
|
+-- embedding model
Audio Service
|
+-- audio model
The parent OpenAIClient is useful for creating multiple feature clients that share implementation details.
Model Routing
A centralized model configuration can look like:
{
"OpenAI": {
"ChatModel": "your-chat-model",
"EmbeddingModel": "your-embedding-model",
"ImageModel": "your-image-model",
"AudioModel": "your-audio-model"
}
}
Then application services select the appropriate client.
Do not assume one model is optimal for every operation.
Cost Management
SDK integration should include usage tracking.
Track:
operation
model
prompt version
response schema
input usage
output usage
duration
success/failure
Then:
Resume Analysis
|
+-- requests
+-- tokens
+-- failures
+-- average latency
Customer Support
|
+-- requests
+-- tokens
+-- failures
This allows better cost analysis.
Prompt Version Tracking
Because the prompt is application behavior, record:
Prompt Name
Prompt Version
Model
SDK Version
Schema Version
A production request can then be reproduced more reliably.
SDK Version Tracking
Because the OpenAI SDK changes over time, record its version as part of application build metadata.
For this article:
OpenAI SDK: 2.14.0
Version 2.14.0 was released on September 15, 2026.
Do not silently mix code written for one SDK version with assumptions from another.
Experimental APIs
The official repository states that some client APIs are marked [Experimental] while their .NET design is still evolving, and using them requires explicit compiler-warning acknowledgement.
This matters especially for newer capabilities.
For example:
#pragma warning disable OPENAI001
// Experimental API usage.
#pragma warning restore OPENAI001
Do not suppress the warning globally without understanding which API is experimental.
A better strategy is:
Experimental Feature
|
v
Isolate in one service
|
v
Version carefully
|
v
Test during upgrades
SDK Upgrade Strategy
When upgrading:
dotnet add package OpenAI --version 2.14.0
then:
dotnet restore
dotnet build
dotnet test
Review:
CHANGELOG
breaking changes
experimental APIs
API behavior changes
serialization changes
new capabilities
The official changelog is the primary source for release-level changes.
Why SDK Version Matters
Imagine your application uses:
OpenAI SDK 2.10
and later upgrades to:
OpenAI SDK 2.14
Between those versions, the SDK added capabilities such as audio streaming, image streaming, Responses custom tools, expanded reasoning controls, additional usage information, and OpenTelemetry improvements.
This shows why SDK versions should be treated as part of your application's technical contract.
SDK and Clean Architecture
A strong enterprise structure is:
Presentation
|
v
Application
|
+-- IAIChatService
+-- IEmbeddingService
+-- IImageService
+-- IAudioService
|
v
Infrastructure
|
+-- OpenAIChatService
+-- OpenAIEmbeddingService
+-- OpenAIImageService
+-- OpenAIAudioService
|
v
OpenAI SDK
The application layer should not need to know:
ChatClient
ResponsesClient
OpenAI API endpoints
API keys
unless the architecture deliberately chooses to expose those details.
AI Service Interface
public interface IAIChatService
{
Task<string> GenerateAsync(
IReadOnlyList<ChatMessage> messages,
CancellationToken cancellationToken = default);
}
Implementation:
public sealed class OpenAIChatService
: IAIChatService
{
private readonly ChatClient _client;
public OpenAIChatService(
ChatClient client)
{
_client = client;
}
public async Task<string> GenerateAsync(
IReadOnlyList<ChatMessage> messages,
CancellationToken cancellationToken = default)
{
ChatCompletion result =
await _client.CompleteChatAsync(
messages,
cancellationToken);
foreach (
ChatMessageContentPart part
in result.Content)
{
if (!string.IsNullOrWhiteSpace(
part.Text))
{
return part.Text;
}
}
return string.Empty;
}
}
Now:
Application
|
v
IAIChatService
|
v
OpenAIChatService
|
v
ChatClient
OpenAI SDK and Prompt Management
The earlier prompt-management topics should remain independent.
Use:
PromptRepository
|
v
PromptTemplate
|
v
PromptRenderer
|
v
OpenAI SDK
The SDK's responsibility is:
model communication
Your application's prompt-management layer owns:
templates
versions
variables
evaluation
approval
rollback
OpenAI SDK and Context Management
Similarly:
Conversation
|
v
Context Manager
|
v
Selected Messages
|
v
ChatClient
The SDK should not decide your business-specific context policy.
Production Architecture
A complete production architecture can look like:
ASP.NET Core
|
v
Application Services
|
+---------------------+---------------------+
| | |
v v v
Prompt Management Context Management Business Services
| | |
+---------------------+---------------------+
|
v
AI Abstractions
|
+----------------+----------------+
| |
v v
Microsoft.Extensions.AI OpenAI SDK
| |
+----------------+----------------+
|
v
OpenAI API
|
+-------------------+-------------------+
| | |
v v v
Responses Chat Other APIs
| | |
+-------------------+-------------------+
|
v
Validation / Mapping
|
+----------------+----------------+
| | |
v v v
SQL Server Queue API
Practical Project: Build an OpenAI SDK Chat API
Create:
ASP.NET Core Web API
Install:
dotnet add package OpenAI --version 2.14.0
Implement:
POST /api/chat
Request:
{
"message": "Explain dependency injection."
}
Architecture:
POST /api/chat
|
v
ChatRequest
|
v
ChatService
|
v
ChatClient
|
v
OpenAI
|
v
ChatCompletion
|
v
API Response
Add:
system prompt
prompt template
conversation history
streaming
logging
cancellation
structured output
Practical Project: Build an AI Ticket Router
Input:
{
"message": "My credit card was charged twice."
}
AI output:
{
"category": "Billing",
"priority": "High",
"summary": "Customer reports a duplicate charge."
}
Pipeline:
User Ticket
|
v
Prompt Template
|
v
ChatClient
|
v
Structured Output
|
v
C# DTO
|
v
Validation
|
v
Routing
|
+-- Billing Queue
+-- Technical Queue
+-- Delivery Queue
+-- Account Queue
Practical Project: Build an AI Tool-Calling Assistant
Build tools:
getOrder
searchProducts
getCustomer
Application architecture:
ChatClient
|
v
OpenAI
|
+-------+-------+
| |
v v
Final Answer Tool Call
|
v
Tool Dispatcher
|
+------------+------------+
| | |
v v v
getOrder getCustomer searchProducts
|
v
Validation
|
v
Authorization
|
v
Execution
The SDK handles the model/tool-message protocol.
Your application owns tool security and business logic.
Practical Project: Build a Structured Resume Analyzer
Define:
public sealed class ResumeAnalysis
{
public string? CandidateName { get; set; }
public string? CurrentRole { get; set; }
public int? YearsOfExperience { get; set; }
public List<string> Skills { get; set; } = [];
public List<string> Companies { get; set; } = [];
}
Then use the OpenAI SDK with a JSON Schema response format.
After deserialization:
AI Result
|
v
ResumeAnalysis
|
v
Validation
|
v
Database / Search Index
Do not write the AI result directly into your production database without validation.
Practical Project: Build a Provider-Neutral AI Service
Create:
public interface IAIChatService
{
Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default);
}
Implementation 1:
OpenAI
Implementation 2 later:
Azure OpenAI
Implementation 3:
Ollama
Architecture:
IAIChatService
|
+-------------+-------------+
| | |
v v v
OpenAI Azure OpenAI Ollama
This is where the Microsoft IChatClient abstraction becomes particularly valuable.
Common Mistakes
Installing an outdated SDK version
Always verify the current NuGet version.
For this article, the verified current version is:
OpenAI 2.14.0
released September 15, 2026.
Copying old SDK tutorials
OpenAI's .NET SDK evolves quickly.
A tutorial written against an older release may contain obsolete APIs.
Hard-coding API keys
Use secure configuration.
Creating SDK clients for every request
Use dependency injection and appropriate singleton lifetime. The SDK documents its clients as thread-safe.
Putting OpenAI code inside controllers
Use an application service.
Ignoring cancellation
Pass CancellationToken.
Executing tool calls without validation
Tool arguments come from the model and must be validated. The official SDK documentation explicitly warns about hallucinated tool arguments.
Treating structured output as automatically correct
Schema compliance does not guarantee factual correctness.
Mixing prompt management with SDK infrastructure
Keep prompt templates and versions in your application layer.
Globally suppressing experimental warnings
Isolate experimental APIs and track the SDK version.
Using one model for every operation
Chat, embeddings, audio, image, and specialized tasks may require different models.
Returning SDK objects directly from public APIs
Map them to your own DTOs.
Frequently Asked Questions
What is the official OpenAI SDK package for .NET?
The official NuGet package is:
OpenAI
The current verified version for this article is 2.14.0.
What namespace contains ChatClient?
OpenAI.Chat
The official SDK namespace mapping documents ChatClient under OpenAI.Chat.
What namespace contains ResponsesClient?
OpenAI.Responses
What is OpenAIClient?
It is the parent SDK client that can create feature-specific clients while sharing implementation details.
Can OpenAI clients be registered as singletons?
Yes. The official SDK states that its clients are thread-safe and can safely be registered as singletons in ASP.NET Core.
Can I use streaming?
Yes. ChatClient provides CompleteChatStreaming and CompleteChatStreamingAsync, and the SDK also documents Responses streaming.
Can I use function calling?
Yes. ChatClient supports tools and function calling, including ChatTool.CreateFunctionTool.
Can I use structured outputs?
Yes. The official SDK supports JSON Schema-based structured outputs through ChatResponseFormat.CreateJsonSchemaFormat.
Can I use IChatClient with OpenAI?
Yes. Microsoft provides AsIChatClient adapters for OpenAI ChatClient and ResponsesClient.
Can EmbeddingClient be adapted to Microsoft.Extensions.AI?
Yes. The current OpenAI extension catalog includes AsIEmbeddingGenerator.
Can ImageClient be adapted to Microsoft.Extensions.AI?
Yes. The current extension catalog includes AsIImageGenerator.
Can AudioClient be adapted to Microsoft.Extensions.AI?
Yes. Current OpenAI extension APIs include adapters for speech-to-text and text-to-speech clients.
Can I use a custom endpoint?
Yes. The official SDK supports custom endpoints through OpenAIClientOptions.Endpoint.
Are some SDK APIs experimental?
Yes. The official README states that some client APIs are marked [Experimental] while their .NET design continues to evolve.
Why does OPENAI001 appear?
It is an experimental API diagnostic used by some SDK surfaces. Applications using those APIs must explicitly acknowledge the diagnostic according to the SDK's current design.
What was added in OpenAI SDK 2.14.0?
Version 2.14.0 added audio and image streaming protocol methods, Responses custom tools, cache-write token usage information, expanded reasoning options, and improvements around OpenTelemetry GenAI conventions and realtime extensibility.
Should application services directly depend on ChatClient?
They can, but IChatClient or your own application-level AI interface is often preferable when provider portability matters.
Should I use ChatClient or ResponsesClient?
Use the API/client surface that matches your application's needs. ChatClient targets Chat Completions, while ResponsesClient targets the Responses API.
Can I use the SDK from a worker service?
Yes. The same dependency-injection and asynchronous patterns work in BackgroundService and hosted services.
Can I use the SDK from Blazor?
Yes, but keep the API key on the server for browser-based Blazor applications. Client applications should call your protected backend rather than exposing the OpenAI secret.
Can I use the SDK from .NET MAUI?
Yes, but do not embed a long-lived OpenAI API key in the shipped mobile application. Use a controlled backend when secret protection is required.
Can I use the SDK for RAG?
Yes. The SDK exposes embeddings, files, vector-store functionality, Responses file search, and other building blocks useful for RAG.
Does the SDK replace prompt management?
No.
The SDK handles API communication.
Your application should manage:
prompts
templates
versions
context
evaluation
business rules
Interview Questions
What is the OpenAI .NET SDK?
It is the official .NET library that provides strongly typed access to the OpenAI REST API.
What NuGet package is used?
OpenAI
What is the current verified package version?
2.14.0
released September 15, 2026.
What is ChatClient?
The SDK client for Chat Completions.
What is ResponsesClient?
The SDK client for the Responses API.
What is OpenAIClient?
A parent convenience client used to create feature-specific OpenAI clients.
What is the difference between a direct feature client and OpenAIClient?
A feature client such as ChatClient directly performs one category of operations.
OpenAIClient can create multiple feature clients and share underlying implementation details.
Why register ChatClient as a singleton?
The official SDK states that its clients are thread-safe and can be safely registered as singleton services in ASP.NET Core.
How do you stream Chat Completions?
Use:
CompleteChatStreamingAsync(...)
and enumerate the returned async collection.
How do you define a function tool?
Use:
ChatTool.CreateFunctionTool(...)
and add the tool to ChatCompletionOptions.Tools.
How do you know the model requested a tool?
Check:
completion.FinishReason ==
ChatFinishReason.ToolCalls
The official SDK documents this workflow.
Why validate tool arguments?
Because the model can produce incorrect or hallucinated arguments. The SDK documentation explicitly warns about this.
How do you request structured output?
Use:
ChatResponseFormat.CreateJsonSchemaFormat(...)
through ChatCompletionOptions.ResponseFormat.
Why use IChatClient?
It provides a provider-neutral abstraction that can reduce application coupling to OpenAI.
How do you convert ChatClient to IChatClient?
Use:
chatClient.AsIChatClient()
from the Microsoft OpenAI integration.
Can ResponsesClient also be adapted?
Yes. Microsoft provides an AsIChatClient(ResponsesClient, String) overload. The current overload is marked experimental.
What is EmbeddingClient?
The OpenAI SDK client for embeddings.
What is ImageClient?
The OpenAI SDK client for image operations.
What is AudioClient?
The OpenAI SDK client for audio functionality.
What is VectorStoreClient?
The SDK client for vector-store operations.
What is RealtimeClient?
The SDK client for realtime operations.
Why isolate experimental APIs?
Because experimental APIs can change as the .NET SDK evolves. The official SDK explicitly identifies such APIs with [Experimental].
What should be tracked when upgrading the SDK?
Track:
SDK version
API surface
model configuration
prompt versions
schema versions
experimental APIs
regression tests
Best Practices
Pin the SDK version in production
For example:
<PackageReference
Include="OpenAI"
Version="2.14.0" />
Then upgrade deliberately.
Read the changelog before upgrading
The official changelog is the best source for version-specific changes.
Use singleton SDK clients
The official library states that its clients are thread-safe.
Keep API keys out of source control
Use secure configuration.
Use async methods
For web applications and workers:
await CompleteChatAsync(...)
Pass cancellation tokens
This avoids unnecessary work after request cancellation.
Put SDK calls behind an application service
Keep provider code out of controllers.
Use IChatClient when provider portability matters
Keep direct SDK clients for OpenAI-specific functionality where needed.
Validate tool arguments
Never blindly execute model-generated tool parameters.
Validate structured output
A correct JSON shape does not guarantee correct business data.
Keep prompts separate
Prompt management belongs in a dedicated application layer.
Track usage and diagnostics
Record the metadata needed for operations and troubleshooting.
Isolate experimental APIs
Do not spread experimental APIs throughout your entire codebase.
Keep model IDs configurable
Model availability and behavior change over time.
Test before SDK upgrades
Use:
unit tests
integration tests
AI evaluations
tool tests
structured-output tests
streaming tests
Complete OpenAI SDK Chat Service
A practical production-style service:
using OpenAI.Chat;
public interface IAIChatService
{
Task<string> GenerateAsync(
string prompt,
CancellationToken cancellationToken = default);
}
public sealed class OpenAIChatService
: IAIChatService
{
private readonly ChatClient _client;
private readonly ILogger<OpenAIChatService> _logger;
public OpenAIChatService(
ChatClient client,
ILogger<OpenAIChatService> logger)
{
_client = client;
_logger = logger;
}
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);
_logger.LogInformation(
"OpenAI chat request completed with finish reason {FinishReason}.",
completion.FinishReason);
foreach (
ChatMessageContentPart part
in completion.Content)
{
if (!string.IsNullOrWhiteSpace(part.Text))
{
return part.Text;
}
}
return string.Empty;
}
}
Register:
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<
IAIChatService,
OpenAIChatService>();
Complete ASP.NET Core Endpoint
app.MapPost(
"/api/chat",
async (
ChatRequest request,
IAIChatService service,
CancellationToken cancellationToken) =>
{
var response =
await service.GenerateAsync(
request.Message,
cancellationToken);
return Results.Ok(
new
{
response
});
});
public sealed class ChatRequest
{
public required string Message { get; init; }
}
The final architecture is:
HTTP Request
|
v
ChatRequest
|
v
IAIChatService
|
v
OpenAIChatService
|
v
ChatClient
|
v
OpenAI
|
v
ChatCompletion
|
v
API Response
Complete Provider-Neutral Architecture
For a larger application, use:
ASP.NET Core
|
v
AI Application Service
|
v
IChatClient
|
v
OpenAI Adapter
|
v
ChatClient
|
v
OpenAI API
Registration:
using Microsoft.Extensions.AI;
using OpenAI;
builder.Services.AddSingleton<IChatClient>(
_ =>
{
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 OpenAIClient(apiKey)
.GetChatClient(model)
.AsIChatClient();
});
Microsoft currently documents exactly this type of ChatClient → IChatClient adapter.
Practical Exercise
Create a console application that:
1. Installs OpenAI 2.14.0.
2. Reads the API key from an environment variable.
3. Reads the model from configuration.
4. Registers ChatClient.
5. Sends a system message.
6. Sends a user message.
7. Prints the response.
8. Supports cancellation.
Then extend it with:
streaming
conversation history
structured output
tool calling
logging
Advanced Exercise
Create an ASP.NET Core AI service with:
ChatClient
ResponsesClient
EmbeddingClient
ImageClient
AudioClient
Use:
dependency injection
configuration
singleton clients
application services
structured logging
error handling
cancellation
Then expose:
POST /api/chat
POST /api/summary
POST /api/ticket/analyze
POST /api/resume/analyze
Advanced Project: OpenAI SDK Gateway
Build:
Client Applications
|
v
ASP.NET Core API
|
v
AI Application Layer
|
+-------------------+-------------------+
| | |
v v v
Chat Service Embedding Service Media Service
| | |
v v v
ChatClient EmbeddingClient Audio/Image
| | |
+-------------------+-------------------+
|
v
OpenAI
Add:
prompt management
tenant configuration
model routing
usage tracking
rate limits
logging
metrics
tool authorization
structured output
streaming
retry policies
evaluation
Learning Path
The OpenAI SDK path is:
1. Install OpenAI NuGet package
|
v
2. Configure API key
|
v
3. Learn OpenAIClient
|
v
4. Learn ChatClient
|
v
5. Learn ChatMessage
|
v
6. Learn ChatCompletion
|
v
7. Learn ChatCompletionOptions
|
v
8. Learn streaming
|
v
9. Learn tools
|
v
10. Learn structured output
|
v
11. Learn ResponsesClient
|
v
12. Learn embeddings
|
v
13. Learn image/audio clients
|
v
14. Learn realtime
|
v
15. Integrate with Microsoft.Extensions.AI
|
v
16. Build production AI services
Key Takeaways
The official OpenAI .NET SDK provides strongly typed C# clients over the OpenAI REST API. It is generated from OpenAI's OpenAPI specification in collaboration with Microsoft.
The current verified package version is:
OpenAI 2.14.0
released September 15, 2026.
The major clients include:
ChatClient
ResponsesClient
EmbeddingClient
ImageClient
AudioClient
OpenAIFileClient
VectorStoreClient
ModerationClient
RealtimeClient
OpenAIClient
For ordinary chat:
ChatClient client =
new(model, apiKey);
var completion =
await client.CompleteChatAsync(
prompt);
For streaming:
await foreach (
var update
in client.CompleteChatStreamingAsync(
prompt))
{
...
}
For tools:
ChatTool.CreateFunctionTool(...)
and:
ChatCompletionOptions.Tools
For structured output:
ChatResponseFormat.CreateJsonSchemaFormat(...)
For provider-neutral architecture:
chatClient.AsIChatClient()
The most useful production architecture is:
ASP.NET Core
|
v
Application Service
|
v
IChatClient / AI Interface
|
v
OpenAI SDK
|
+-- ChatClient
+-- ResponsesClient
+-- EmbeddingClient
+-- AudioClient
+-- ImageClient
+-- Other Clients
|
v
OpenAI API
This allows the OpenAI SDK to remain an infrastructure component rather than becoming tightly coupled to every part of the application.
The next topic in the roadmap is OpenAI Chat Applications with ASP.NET Core where this SDK foundation can be turned into a complete web-based AI chat application with conversation state, APIs, dependency injection, streaming, prompt management, and production architecture.

Post a Comment