Skip to main content

Azure AI Search with C#: Build Smart Search (2026)

Learn Azure AI Search with C# — index data, run vector and hybrid queries, and add RAG to your .NET app. Start building intelligent search today. If your application's search box still runs a LIKE '%term%' query against SQL Server, your users are quietly suffering. They type "cheap laptop for uni" and get zero results because your catalogue says "affordable notebook for students." Azure AI Search with C# fixes exactly this problem: it combines classic keyword search, vector embeddings, and semantic reranking into a single managed service that you can drive from .NET with a few dozen lines of code. In this tutorial you'll build a working search index from scratch, run keyword, vector, and hybrid queries, and finish with a Retrieval Augmented Generation (RAG) pattern that grounds an LLM in your own data. This guide targets .NET 9 and the Azure.Search.Documents v11 SDK. Every snippet is runnable. We'll explain why each design choice matter...

Build an AI Chatbot with C# and OpenAI API (2026 Guide)

Learn how to build an AI chatbot in C# with the OpenAI API. Step-by-step .NET 8 tutorial with streaming, history & function calling. Start coding now!

Building an AI chatbot is one of the most in-demand skills for .NET developers in 2026, and the good news is that the C# OpenAI API integration story has never been better. With the official OpenAI .NET SDK, you can go from an empty console project to a fully functional, streaming, context-aware chatbot in under an hour. In this complete tutorial, you'll learn how to build an AI chatbot with C# and the OpenAI API from scratch — including conversation history, streaming responses, function calling, and the production best practices most tutorials skip.

We'll explain not just how each piece works, but why it's designed that way, so you can adapt this foundation to web apps, Blazor, ASP.NET Core APIs, or desktop applications.

What You'll Build and What You Need

By the end of this tutorial you'll have a console-based AI chatbot that:

  • Sends user messages to OpenAI's chat models and prints responses
  • Streams tokens in real time (like ChatGPT's typing effect)
  • Remembers conversation history across turns
  • Calls your own C# functions when the model needs live data (function calling / tools)
  • Handles errors, rate limits, and token budgets like a production app should

Prerequisites:

  • .NET 8 SDK or later (everything here also works on .NET 9)
  • An OpenAI API key from platform.openai.com
  • Basic C# knowledge — async/await and classes

Step 1: Set Up the Project and OpenAI .NET SDK

Create a new console project and add the official OpenAI NuGet package:

dotnet new console -n CSharpChatbot
cd CSharpChatbot
dotnet add package OpenAI

Why the official SDK? For years, C# developers relied on community libraries. The official OpenAI package is now the recommended path: it's strongly typed, actively maintained, supports streaming and tool calling natively, and tracks API changes quickly. It also plugs into Microsoft.Extensions.AI abstractions if you later want provider-agnostic code.

Store Your API Key Securely

Never hardcode your API key. A leaked key in a GitHub repo will be scraped and abused within minutes — this is the single most common (and most expensive) beginner mistake. Use an environment variable:

// PowerShell (persists for your user account):
// [Environment]::SetEnvironmentVariable("OPENAI_API_KEY", "sk-...", "User")

// macOS/Linux:
// export OPENAI_API_KEY="sk-..."

For ASP.NET Core apps, use User Secrets in development and a secret manager (Azure Key Vault, AWS Secrets Manager) in production.

Step 2: Your First Chat Completion in C#

Here's the minimal working chatbot — a complete, runnable Program.cs:

using OpenAI.Chat;

string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
    ?? throw new InvalidOperationException("Set the OPENAI_API_KEY environment variable.");

ChatClient client = new(model: "gpt-4o-mini", apiKey: apiKey);

ChatCompletion completion = await client.CompleteChatAsync(
    "Explain async/await in C# in two sentences.");

Console.WriteLine(completion.Content[0].Text);

Run it with dotnet run and you'll get a response in a couple of seconds.

Why gpt-4o-mini? Model choice is a cost/quality trade-off. Mini-class models are fast and cheap — ideal for development, prototyping, and most chatbot workloads. Swap the model string to a larger model (like gpt-4o or newer flagship models) only when you measurably need the extra reasoning quality. Because the model is just a constructor parameter, upgrading later is a one-line change.

Step 3: Add Conversation History (Memory)

Here's the part beginners often miss: the OpenAI API is stateless. The model has no memory of your previous requests. Every call must include the full conversation so far. "Chat memory" is something you implement by maintaining a list of messages:

using OpenAI.Chat;

string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
    ?? throw new InvalidOperationException("Set the OPENAI_API_KEY environment variable.");

ChatClient client = new(model: "gpt-4o-mini", apiKey: apiKey);

List<ChatMessage> messages =
[
    new SystemChatMessage(
        """
        You are a helpful assistant for C# developers.
        Answer concisely and include code examples when useful.
        """)
];

Console.WriteLine("C# Chatbot ready. Type 'exit' to quit.\n");

while (true)
{
    Console.Write("You: ");
    string? input = Console.ReadLine();
    if (string.IsNullOrWhiteSpace(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
        break;

    messages.Add(new UserChatMessage(input));

    ChatCompletion completion = await client.CompleteChatAsync(messages);
    string reply = completion.Content[0].Text;

    messages.Add(new AssistantChatMessage(reply));
    Console.WriteLine($"\nBot: {reply}\n");
}

Three message roles matter here, and understanding why they exist makes you a better chatbot developer:

  • System — sets the bot's persona, rules, and constraints. The model weights this heavily. This is where prompt engineering lives.
  • User — what the human typed.
  • Assistant — the model's previous replies. Feeding these back is what creates the illusion of memory.

Pitfall: unbounded history. Every message you append costs input tokens on every subsequent request, and models have a finite context window. A long-running chat will get slower, more expensive, and eventually fail. Production chatbots trim old messages, keep a sliding window of the last N turns, or summarize older history into a single system note. Even a simple if (messages.Count > 40) messages.RemoveRange(1, 2); (preserving the system message at index 0) prevents runaway costs.

Step 4: Stream Responses Like ChatGPT

Waiting several seconds for a full response feels broken to users. Streaming prints tokens as they're generated, which dramatically improves perceived performance — the same reason ChatGPT types its answers. The C# OpenAI API SDK makes this an await foreach:

Console.Write("\nBot: ");

AsyncCollectionResult<StreamingChatCompletionUpdate> stream =
    client.CompleteChatStreamingAsync(messages);

var fullReply = new System.Text.StringBuilder();

await foreach (StreamingChatCompletionUpdate update in stream)
{
    foreach (ChatMessageContentPart part in update.ContentUpdate)
    {
        Console.Write(part.Text);
        fullReply.Append(part.Text);
    }
}

Console.WriteLine("\n");
messages.Add(new AssistantChatMessage(fullReply.ToString()));

Note that we still accumulate the complete reply in a StringBuilder — the conversation history needs the full assistant message, not fragments. In an ASP.NET Core API, you'd forward these chunks to the browser via Server-Sent Events or SignalR.

Step 5: Function Calling — Let the Chatbot Use Your C# Code

A chatbot that can only talk is limited: it can't check your database, look up an order, or fetch live weather. Function calling (also called tools) solves this. You describe your C# functions to the model; when a user's question needs one, the model responds with "call this function with these arguments" instead of text. Your code runs the function, returns the result, and the model composes a natural-language answer from it.

using System.Text.Json;
using OpenAI.Chat;

// 1. Describe the tool to the model
ChatTool getOrderStatusTool = ChatTool.CreateFunctionTool(
    functionName: "get_order_status",
    functionDescription: "Gets the shipping status of a customer order by order ID.",
    functionParameters: BinaryData.FromString("""
        {
            "type": "object",
            "properties": {
                "orderId": { "type": "string", "description": "The order ID, e.g. ORD-1042" }
            },
            "required": ["orderId"]
        }
        """));

ChatCompletionOptions options = new() { Tools = { getOrderStatusTool } };

// 2. Your actual business logic — the model never sees this code
static string GetOrderStatus(string orderId) =>
    orderId == "ORD-1042" ? "Shipped — arriving Thursday" : "Order not found";

// 3. The tool-calling loop
messages.Add(new UserChatMessage("Where is my order ORD-1042?"));

ChatCompletion completion = await client.CompleteChatAsync(messages, options);

while (completion.FinishReason == ChatFinishReason.ToolCalls)
{
    messages.Add(new AssistantChatMessage(completion));

    foreach (ChatToolCall toolCall in completion.ToolCalls)
    {
        using JsonDocument args = JsonDocument.Parse(toolCall.FunctionArguments);
        string orderId = args.RootElement.GetProperty("orderId").GetString()!;
        string result = GetOrderStatus(orderId);

        messages.Add(new ToolChatMessage(toolCall.Id, result));
    }

    completion = await client.CompleteChatAsync(messages, options);
}

Console.WriteLine(completion.Content[0].Text);
// "Your order ORD-1042 has shipped and should arrive Thursday."

Why a loop? The model may need multiple tool calls (or chained calls) before it can answer, so you keep executing tools and re-asking until the finish reason is a normal text response. This loop is the core pattern behind every AI agent framework — once you understand it, libraries like Semantic Kernel and Microsoft.Extensions.AI are much less magical.

Best Practices for a Production C# AI Chatbot

1. Handle Rate Limits and Transient Errors

The API will occasionally return 429 (rate limit) or 5xx errors. The SDK retries some failures automatically, but wrap calls defensively and consider Polly for exponential backoff in high-traffic services:

try
{
    ChatCompletion completion = await client.CompleteChatAsync(messages);
}
catch (ClientResultException ex) when (ex.Status == 429)
{
    Console.WriteLine("Rate limited — retry after a short delay.");
}

2. Control Costs with Token Budgets

Set MaxOutputTokenCount in ChatCompletionOptions to cap response length, trim conversation history aggressively, and log completion.Usage (input/output token counts) so you can see exactly what each conversation costs. Untracked token usage is how hobby projects end up with surprise bills.

3. Guard Against Prompt Injection

If your chatbot processes user-supplied documents or web content, treat that content as untrusted. Never put secrets in the system prompt, validate tool arguments before executing them (the orderId above should be checked against the authenticated user's own orders), and apply authorization in your code — never trust the model to enforce security rules.

4. Keep the ChatClient Long-Lived

Like HttpClient, create one ChatClient and reuse it. In ASP.NET Core, register it as a singleton in dependency injection rather than constructing it per request.

5. Common Pitfalls Recap

  • Hardcoding API keys — use environment variables or a secrets manager.
  • Forgetting to append assistant replies to history — the bot "forgets" its own answers.
  • Unbounded conversation history — costs and latency grow every turn.
  • Blocking on async calls with .Result — always await; blocking can deadlock UI and legacy ASP.NET apps.
  • Ignoring FinishReason — a response cut off by the token limit (Length) looks like a bug if you don't check why generation stopped.

Where to Go Next

You now have every building block of a real AI product. Natural next steps:

  • Web UI: wrap this logic in an ASP.NET Core minimal API and stream to a Blazor or React front end via SignalR.
  • RAG (Retrieval-Augmented Generation): embed your documentation with the embeddings API, store vectors in a database, and inject relevant chunks into the prompt so the bot answers from your data.
  • Abstractions: adopt Microsoft.Extensions.AI's IChatClient so you can swap OpenAI for Azure OpenAI or local models without rewriting your app.

Conclusion: Building an AI Chatbot with the C# OpenAI API

Building an AI chatbot with the C# OpenAI API comes down to five ideas: the API is stateless so you manage history yourself; the system message defines your bot's behavior; streaming makes responses feel instant; function calling connects the model to your real C# code; and production readiness means handling rate limits, token budgets, and security from day one.

Key takeaways:

  • The official OpenAI .NET SDK (dotnet add package OpenAI) is the modern, strongly-typed way to integrate GPT models into C# apps.
  • Conversation memory is just a List<ChatMessage> you send with every request — trim it to control cost.
  • Streaming with await foreach transforms user experience for almost no extra code.
  • The tool-calling loop is the foundation of AI agents — master it here and every framework makes sense.
  • Secure your API key, validate tool inputs, and never let the model be your authorization layer.

Copy the code above into a fresh console project, run dotnet run, and you'll have your own AI chatbot in C# running today. From there, the same patterns scale all the way to production-grade .NET AI applications.

About csharp-coder.com
Your go-to resource for C#, .NET, and modern software development. Follow along for daily tutorials, tips, and real-world examples.

Comments

Popular posts from this blog

.NET MAUI Tutorial 2026: Build Cross-Platform Apps in C#

Learn .NET MAUI in 2026 to build iOS, Android, Windows & Mac apps from one C# codebase. Start this cross-platform tutorial with code examples today. .NET MAUI (Multi-platform App UI) is Microsoft's framework for building native iOS, Android, Windows, and macOS apps from a single C# codebase . If you've ever wanted to ship a mobile app without learning Swift, Kotlin, and Win32 separately, this .NET MAUI tutorial for 2026 is your starting point. In this guide you'll learn what .NET MAUI is, why it matters for cross-platform app development in C#, and how to build your first working app — with runnable code examples and the best practices senior engineers actually use in production. What Is .NET MAUI and Why Use It in 2026? .NET MAUI is the evolution of Xamarin.Forms, fully integrated into the modern .NET runtime. With one project and one language — C# — you target four platforms. The framework compiles to native UI controls on each device, so a button on iOS...

Angular 14 : 404 error during refresh page after deployment

In this article, We will learn how to solve 404 file or directory not found angular error in production.  Refresh browser angular 404 file or directory not found error You have built an Angular app and created a production build with ng build --prod You deploy it to a production server. Everything works fine until you refresh the page. The app throws The requested URL was not found on this server message (Status code 404 not found). It appears that angular routing not working on the production server when you refresh the page. The error appears on the following scenarios When you type the URL directly in the address bar. When you refresh the page The error appears on all the pages except the root page.   Reason for the requested URL was not found on this server error In a Multi-page web application, every time the application needs to display a page it has to send a request to the web server. You can do that by either typing the URL in the address bar, clicking on the Me...

Angular 14 CRUD Operation with Web API .Net 6.0

How to Perform CRUD Operation Using Angular 14 In this article, we will learn the angular crud (create, read, update, delete) tutorial with ASP.NET Core 6 web API. We will use the SQL Server database and responsive user interface for our Web app, we will use the Bootstrap 5. Let's start step by step. Step 1 - Create Database and Web API First we need to create Employee database in SQL Server and web API to communicate with database. so you can use my previous article CRUD operations in web API using net 6.0 to create web API step by step. As you can see, after creating all the required API and database, our API creation part is completed. Now we have to do the angular part like installing angular CLI, creating angular 14 project, command for building and running angular application...etc. Step 2 - Install Angular CLI Now we have to install angular CLI into our system. If you have already installed angular CLI into your system then skip this step.  To install angular CLI ope...