A modern .NET SDK for the OpenRouter API - providing a unified interface to access multiple LLM providers.
🚀 Simple and intuitive API 🔒 Type-safe with full async/await support 📦 Lightweight with minimal dependencies 🎯 Built for .NET 9.0+ 🔄 Full streaming support 🛠️ Tool calling (server-side auto-execute and client-side modes) 📄 Artifact support with incremental parsing ⚡ Async enumerable streaming (IAsyncEnumerable)
dotnet add package OpenRouter.NETOr via NuGet Package Manager:
Install-Package OpenRouter.NET
Want to use this SDK with your LLM? Check out llms.txt - give this file to your LLM of choice and it will be ready to implement features with OpenRouter.NET for you!
using OpenRouter.NET;
using OpenRouter.NET.Models;
var client = new OpenRouterClient("your-api-key");
var request = new ChatCompletionRequest
{
Model = "anthropic/claude-3.5-sonnet",
Messages = new List<Message>
{
Message.FromUser("Hello! What's the weather like?")
}
};
var response = await client.CreateChatCompletionAsync(request);
Console.WriteLine(response.Choices[0].Message.Content?.ToString() ?? "No response");using OpenRouter.NET;
using OpenRouter.NET.Models;
var client = new OpenRouterClient("your-api-key");
var request = new ChatCompletionRequest
{
Model = "anthropic/claude-3.5-sonnet",
Messages = new List<Message>
{
Message.FromUser("Tell me a story")
}
};
await foreach (var chunk in client.StreamAsync(request))
{
if (chunk.TextDelta != null)
{
Console.Write(chunk.TextDelta);
}
}Perfect for building real-time streaming endpoints that work with browsers using Server-Sent Events:
using OpenRouter.NET;
using OpenRouter.NET.Sse;
using OpenRouter.NET.Models;
// In your ASP.NET endpoint
app.MapPost("/api/chat/stream", async (HttpContext context, ChatRequest body) =>
{
var client = new OpenRouterClient("your-api-key");
var request = new ChatCompletionRequest
{
Model = "anthropic/claude-3.5-sonnet",
Messages = body.Messages
};
// One line to stream everything as SSE!
await client.StreamAsSseAsync(request, context.Response);
});
record ChatRequest(List<Message> Messages);Client-side (JavaScript/TypeScript):
Since this is a POST endpoint with a request body, use the fetch API with streaming:
async function streamChat(messages) {
const response = await fetch('/api/chat/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(line => line.startsWith('data: '));
for (const line of lines) {
const event = JSON.parse(line.slice(6));
if (event.type === 'text') {
appendText(event.textDelta);
}
}
}
}Note: The standard
EventSourceAPI only supports GET requests. For POST endpoints with request bodies, use fetch with streaming as shown above. See SSE Helper Documentation for complete client examples, event types, and TypeScript definitions.
This automatically handles:
- ✅ Text deltas
- ✅ Tool calls (server-side and client-side)
- ✅ Artifacts (started, content, completed)
- ✅ Completion events
- ✅ Proper SSE formatting
- ✅ Headers and connection management
Event types sent:
text- Text content deltastool_executing- Tool startingtool_completed- Tool finished with resulttool_error- Tool failedtool_client- Client-side tool call requestedartifact_started- Artifact beginningartifact_content- Artifact content chunkartifact_completed- Artifact finishedcompletion- Stream finishederror- Error occurred
using OpenRouter.NET;
using OpenRouter.NET.Models;
var client = new OpenRouterClient("your-api-key");
var request = new ChatCompletionRequest
{
Model = "anthropic/claude-3.5-sonnet",
Messages = new List<Message>
{
Message.FromUser("Create a hello world function")
}
};
// Enable artifact support
request.EnableArtifactSupport();
await foreach (var chunk in client.StreamAsync(request))
{
if (chunk.TextDelta != null)
{
Console.Write(chunk.TextDelta);
}
if (chunk.Artifact is ArtifactCompleted completed)
{
Console.WriteLine($"\n\nArtifact: {completed.Title}");
Console.WriteLine(completed.Content);
}
}using OpenRouter.NET;
using OpenRouter.NET.Models;
var client = new OpenRouterClient("your-api-key");
var conversationHistory = new List<Message>();
// Add user message
conversationHistory.AddUserMessage("What is the capital of France?");
var request = new ChatCompletionRequest
{
Model = "anthropic/claude-3.5-sonnet",
Messages = conversationHistory
};
var response = await client.CreateChatCompletionAsync(request);
var assistantMessage = response.Choices[0].Message.Content?.ToString() ?? "";
// Add assistant response to history
conversationHistory.AddAssistantMessage(assistantMessage);
// Continue conversation
conversationHistory.AddUserMessage("What's the population?");
request.Messages = conversationHistory;
response = await client.CreateChatCompletionAsync(request);
Console.WriteLine(response.Choices[0].Message.Content?.ToString() ?? "No response");Check out the samples/ directory for complete working examples:
- BasicCliSample - Interactive CLI to list models and chat with streaming
- OpenRouterWebApiSample - ASP.NET Core Web API showcasing all SDK features including streaming, tools, artifacts, and multimodal
To run a sample:
export OPENROUTER_API_KEY="your-key-here"
cd samples/BasicCliSample
dotnet runOr for the Web API sample:
export OPENROUTER_API_KEY="your-key-here"
cd samples/OpenRouterWebApiSample
dotnet run- .NET 9.0 or later
Full documentation coming soon.
Next.js SSE Streaming: Next.js compression buffers responses before sending, causing SSE streams to arrive as one big chunk. Set compress: false in next.config.js to fix.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
Built for the OpenRouter API platform.