
ChatGPT was not yet publicly available whilst writing the last blog post, in which we built an image generator using OpenAI’s DALL·E 2 model. But alas, ChatGPT is now publicly available. So in this blog post, we’ll explore how to use the ChatGPT API in C# and walk through the process of creating a ChatGPT client in C# that can generate human-like responses to user queries. Our ChatGPT client will also allow us to retain and send conversation history to the API, making it possible to maintain context and provide more personalized responses. We will also parameterize our ChatGPT client so that we can change whether or not to include the conversation history when calling the API, which language model to use, and the nature of the response.
As artificial intelligence continues to advance, language models such as ChatGPT have become increasingly powerful tools for natural language processing. ChatGPT is a large-scale neural language model that has been trained on massive amounts of text data and is capable of generating human-like responses to a wide variety of natural language prompts. It can be used for a range of applications, from chatbots and virtual assistants to language translation and content generation.
To demonstrate the use of our ChatGPT C# client, we will be creating a chatbot in a console application using C# and .NET 7. However, our ChatGPT client will be suitable for use in a number of scenarios. In order to use OpenAI’s API, you will first need to sign up in order to obtain an API key.
The Importance of ChatGPT Retaining Conversation Context
Before creating our client, we’ll briefly look into the ChatGPT API’s default behavior and how it influences the design of our ChatGPT client.
Here is what happens if we don’t feed the API with all of the conversation’s previous messages:

By the time we ask our second question, the API has already forgotten what the conversation is about. But here’s what happens when we feed the API with all of the conversation’s previous messages:

By doing this, we are allowing the API to “remember” who “he” is and remember the context of the conversation. We can then design our ChatGPT C# client accordingly…
Creating the ChatGPT C# client
Let’s create our ChatGPTClient class:
public class ChatGPTClient { private readonly string chatRequestUri; private readonly string openAIAPIKey; private readonly List<Message> messageHistory; private readonly bool includeHistoryWithChatCompletion; private readonly OpenAIModels model; private readonly double temperature; private readonly double top_p; public ChatGPTClient(bool includeHistoryWithChatCompletion = true, OpenAIModels model = OpenAIModels.gpt_3_5_turbo, double temperature = 1, double top_p = 1) { chatRequestUri = "https://api.openai.com/v1/chat/completions"; openAIAPIKey = Environment.GetEnvironmentVariable("OpenAIAPIKey")!; messageHistory = new List<Message>(); this.includeHistoryWithChatCompletion = includeHistoryWithChatCompletion; this.model = model; this.temperature = temperature; this.top_p = top_p; } public async Task<ChatResponse?> SendMessage(string message) { var chatResponse = await Chat(message); if (chatResponse != null) { AddMessageToHistory(new Message { Role = "user", Content = message }); foreach (var responseMessage in chatResponse.Choices!.Select(c => c.Message!)) AddMessageToHistory(responseMessage); } return chatResponse; } private async Task<ChatResponse?> Chat(string message) { using var client = new HttpClient(); using var request = new HttpRequestMessage(HttpMethod.Post, chatRequestUri); request.Headers.Add("Authorization", $"Bearer {openAIAPIKey}"); var requestBody = new { model = GetModel(), temperature, top_p, messages = GetMessageObjects(message) }; request.Content = new StringContent(JsonSerializer.Serialize(requestBody), Encoding.UTF8, "application/json"); var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) { var chatResponse = await response.Content.ReadFromJsonAsync<ChatResponse>(); if (chatResponse != null && chatResponse.Choices != null && chatResponse.Choices.Any(c => c.Message != null)) return chatResponse; } return null; } private IEnumerable<object> GetMessageObjects(string message) { foreach (var historicMessage in includeHistoryWithChatCompletion ? messageHistory : Enumerable.Empty<Message>()) { yield return new { role = historicMessage.Role, content = historicMessage.Content }; } yield return new { role = "user", content = message }; } private void AddMessageToHistory(Message message) => messageHistory.Add(message); private string GetModel() => model.ToString().Replace("3_5", "3.5").Replace("_", "-"); }
The chat completion URI is assigned in the constructor, along with the API key. This is done through an environment variable which can be set by right-clicking on the project and selecting Properties, selecting Debug, clicking Open debug launch profiles UI, and setting an environment variable with the name OpenAIAPIKey along with your API key:

Through the constructor, we are also setting default parameters for our ChatGPT client, such as including chat history with our API call and the model used.
The SendMessage method is what we will call in order to generate our responses. This method calls the Chat method which sends our request to the API. Within the chat method, GetMessageObjects is called which retrieves either the current message only or all of the messages in the conversation. This allows the ChatGPT API to remember the context of the conversation. After the request is made, our message and the subsequent response are then saved to history using the AddMessageToHistory method.
Next, we need the classes that represent the response from the OpenAI API. These classes can be determined by using the sample response from the documentation:
public class ChatResponse { public string? Id { get; set; } public string? Object { get; set; } public int Created { get; set; } public List<Choice>? Choices { get; set; } public Usage? Usage { get; set; } } public class Choice { public int Index { get; set; } public Message? Message { get; set; } public string? Finish_Reason { get; set; } } public class Message { public string? Role { get; set; } public string? Content { get; set; } } public class Usage { public int Prompt_Tokens { get; set; } public int Completion_Tokens { get; set; } public int Total_Tokens { get; set; } }
Finally, we’ll create the class to represent the different models available in the OpenAI API:
public enum OpenAIModels { gpt_4, gpt_4_0314, gpt_4_32k, gpt_4_32k_0314, gpt_3_5_turbo, gpt_3_5_turbo_0301 }
Using the ChatGPT client
Now we’ve created our ChatGPT C# client, we’ll create a simple console application to demonstrate its usage:
var chatGPTClient = new ChatGPTClient(); var chatActive = true; while (chatActive) { var userMessage = Console.ReadLine(); if (string.IsNullOrWhiteSpace(userMessage)) { chatActive = false; } else { var chatResponse = await chatGPTClient.SendMessage(userMessage); if (chatResponse != null) { Console.WriteLine(); foreach (var assistantMessage in chatResponse.Choices!.Select(c => c.Message)) Console.WriteLine(assistantMessage!.Content!.Trim().Replace("\n", "")); Console.WriteLine(); } else { chatActive = false; } } }
Whilst the chatActive variable is true, our program will read our input, send it to the ChatGPT API, and display the response. The instances in which the chatActive flag becomes false is when we enter an empty message or there is no valid response from the API.
As we’ve parameterized our class, we can configure our client using the constructor:
var chatGPTClient = new ChatGPTClient(false, OpenAIModels.gpt_4);
Conclusion & GitHub Repository
That concludes this blog post and code example of creating a ChatGPT C# client. We have created a class that allows us to generate responses using the OpenAI API and ChatGPT and also retain the context of the conversation. We have also created a chatbot console application using ChatGPT and C#. The entire source code for this tutorial can be found in this GitHub repository.
Although access to GPT4 is currently limited, the client we have created should work with GPT4 providing that the request or response is no different. In the meantime, I will be keeping an eye on my email inbox for my invitation to GPT4!