SDK guides
C# / .NET
Connect api14 from C# and .NET with the official OpenAI .NET package, with setup, chat, streaming and error handling examples.
Use the official OpenAI .NET library with api14. Set OpenAIClientOptions.Endpoint to api14 and pass a model id such as openai/gpt-5-mini when you create a client.
Install
dotnet add package OpenAI
export API14_API_KEY="sk-api14-..."
Configure
using System.ClientModel;
using OpenAI;
using OpenAI.Chat;
var credential = new ApiKeyCredential(Environment.GetEnvironmentVariable("API14_API_KEY")!);
var options = new OpenAIClientOptions { Endpoint = new Uri("https://api14.com/api/v1") };
var client = new ChatClient(model: "openai/gpt-5-mini", credential: credential, options: options);
Each ChatClient is bound to one model; create one per model id you use.
Read the key from the environment, user secrets or your secret store, never from appsettings.json committed to source control. In ASP.NET Core, register one client as a singleton; it is thread-safe.
Chat
ChatCompletion completion = await client.CompleteChatAsync("Explain vector databases in two sentences.");
Console.WriteLine(completion.Content[0].Text);
With a system prompt:
ChatCompletion completion = await client.CompleteChatAsync(
[
new SystemChatMessage("You are terse."),
new UserChatMessage("What is RAG?"),
]);
Streaming
var claude = new ChatClient("anthropic/claude-sonnet-4.5", credential, options);
await foreach (StreamingChatCompletionUpdate update in claude.CompleteChatStreamingAsync("Write a haiku about APIs."))
{
foreach (ChatMessageContentPart part in update.ContentUpdate)
Console.Write(part.Text);
}
See Streaming.
Tool calling
using System.Text.Json;
ChatTool getWeather = ChatTool.CreateFunctionTool(
functionName: "get_weather",
functionDescription: "Current weather for a city",
functionParameters: BinaryData.FromString("""
{ "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] }
"""));
var toolOptions = new ChatCompletionOptions { Tools = { getWeather } };
List<ChatMessage> messages = [new UserChatMessage("What's the weather in Istanbul?")];
ChatCompletion completion = await client.CompleteChatAsync(messages, toolOptions);
while (completion.FinishReason == ChatFinishReason.ToolCalls)
{
messages.Add(new AssistantChatMessage(completion));
foreach (ChatToolCall call in completion.ToolCalls)
{
using JsonDocument args = JsonDocument.Parse(call.FunctionArguments);
string city = args.RootElement.GetProperty("city").GetString()!;
messages.Add(new ToolChatMessage(call.Id, GetWeather(city))); // your function
}
completion = await client.CompleteChatAsync(messages, toolOptions);
}
Console.WriteLine(completion.Content[0].Text);
See Tool calling.
Structured output
var jsonOptions = new ChatCompletionOptions
{
ResponseFormat = ChatResponseFormat.CreateJsonSchemaFormat(
jsonSchemaFormatName: "person",
jsonSchema: BinaryData.FromString("""
{
"type": "object",
"properties": { "name": { "type": "string" }, "born": { "type": "integer" } },
"required": ["name", "born"],
"additionalProperties": false
}
"""),
jsonSchemaIsStrict: true),
};
ChatCompletion result = await client.CompleteChatAsync(
[new UserChatMessage("Extract: Ada Lovelace, born 1815.")], jsonOptions);
using JsonDocument person = JsonDocument.Parse(result.Content[0].Text);
Console.WriteLine(person.RootElement.GetProperty("born").GetInt32());
See Structured outputs.
Image input
var vision = new ChatClient("google/gemini-2.5-flash", credential, options);
ChatCompletion described = await vision.CompleteChatAsync(
[
new UserChatMessage(
ChatMessageContentPart.CreateTextPart("What is in this picture?"),
ChatMessageContentPart.CreateImagePart(new Uri("https://example.com/photo.jpg"))),
]);
For a local file, use ChatMessageContentPart.CreateImagePart(BinaryData.FromBytes(File.ReadAllBytes("photo.png")), "image/png"). See Vision & files.
Embeddings
using OpenAI.Embeddings;
var embeddings = new EmbeddingClient("openai/text-embedding-3-small", credential, options);
OpenAIEmbedding embedding = await embeddings.GenerateEmbeddingAsync("The quick brown fox");
Console.WriteLine(embedding.ToFloats().Length);
Errors
Failed calls throw ClientResultException; the raw response carries the status and headers:
try
{
await client.CompleteChatAsync("Hi");
}
catch (ClientResultException ex) when (ex.Status == 402)
{
Console.Error.WriteLine("Balance too low, add funds (insufficient_balance).");
}
catch (ClientResultException ex) when (ex.Status == 429)
{
string? retryAfter = null;
ex.GetRawResponse()?.Headers.TryGetValue("Retry-After", out retryAfter);
Console.Error.WriteLine($"Rate limited, retry after {retryAfter ?? "60"}s.");
}
See Errors.
Timeouts and retries
The library retries 429 and 5xx automatically with exponential backoff. Tune the retry count and network timeout on the options you pass to every client:
using System.ClientModel.Primitives;
var options = new OpenAIClientOptions
{
Endpoint = new Uri("https://api14.com/api/v1"),
NetworkTimeout = TimeSpan.FromMinutes(2),
RetryPolicy = new ClientRetryPolicy(maxRetries: 4),
};
Pass a CancellationToken to any ...Async call to bound the total time across retries. Retrying does not help with daily_limit_reached or insufficient_balance. See Rate limits.
All capabilities
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api14.com/api/v1",
apiKey: process.env.API14_API_KEY,
});
const result = await client.chat.completions.create({
"model": "openai/gpt-5-mini",
"messages": [
{
"role": "user",
"content": "Explain vector databases in two sentences."
}
]
});
console.log(result.choices[0].message.content);