← Blog

Claude API .NET SDK Integration Guide

2026-10-11 · 5 min read · SubToAPI Team

Anthropic does not publish an official .NET SDK for the Claude API — only Python and TypeScript are officially maintained. If you're building a C# backend, a Blazor app, or an Azure Function that needs to talk to Claude, the practical path is to call the HTTPS API directly with HttpClient and System.Text.Json, or use a thin community wrapper around the same calls. This guide covers both approaches and shows working C# code for messages, streaming, and tool use.

The good news: Claude's API is a straightforward JSON-over-HTTPS interface, so there's nothing exotic to wire up. You need a typed request/response model, a POST to the messages endpoint, and (optionally) an SSE reader for streaming. Everything below works identically whether you're calling Anthropic directly or a Claude-compatible gateway like SubToAPI — the integration pattern doesn't change, only the base URL and key format.

Setting up the project

Create a class library or use your existing API project. You only need System.Net.Http.Json (built into modern .NET) — no third-party dependency required for basic usage.

dotnet new classlib -n ClaudeClient
cd ClaudeClient

Store your API key in configuration, not in source:

dotnet user-secrets init
dotnet user-secrets set "Claude:ApiKey" "sk-ant-..."

In production, pull it from environment variables or a secrets manager (Azure Key Vault, AWS Secrets Manager) rather than appsettings.json.

A minimal C# client

Here's a compact wrapper for non-streaming requests:

public class ClaudeClient
{
    private readonly HttpClient _http;

    public ClaudeClient(HttpClient http, string apiKey)
    {
        _http = http;
        _http.BaseAddress = new Uri("https://api.anthropic.com/v1/");
        _http.DefaultRequestHeaders.Add("x-api-key", apiKey);
        _http.DefaultRequestHeaders.Add("anthropic-version", "2023-06-01");
    }

    public async Task<ClaudeResponse> SendMessageAsync(string model, string userMessage)
    {
        var payload = new
        {
            model,
            max_tokens = 1024,
            messages = new[] { new { role = "user", content = userMessage } }
        };

        var response = await _http.PostAsJsonAsync("messages", payload);
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadFromJsonAsync<ClaudeResponse>()
               ?? throw new InvalidOperationException("Empty response");
    }
}

public record ClaudeResponse(string Id, string Model, List<ContentBlock> Content);
public record ContentBlock(string Type, string? Text);

Register it with dependency injection in Program.cs:

builder.Services.AddHttpClient<ClaudeClient>();
builder.Services.AddSingleton(sp =>
    new ClaudeClient(sp.GetRequiredService<HttpClient>(),
        builder.Configuration["Claude:ApiKey"]!));

This pattern — one class wrapping HttpClient, DTOs matching the JSON schema — is enough for most CRUD-style integrations: summarization endpoints, chat features, content generation jobs.

Handling streaming responses

Claude supports server-sent events for token-by-token output. In .NET, you read the response as a stream and parse data: lines manually, since there's no built-in SSE client in the BCL:

public async IAsyncEnumerable<string> StreamMessageAsync(string model, string userMessage)
{
    var payload = new
    {
        model,
        max_tokens = 1024,
        stream = true,
        messages = new[] { new { role = "user", content = userMessage } }
    };

    using var request = new HttpRequestMessage(HttpMethod.Post, "messages")
    {
        Content = JsonContent.Create(payload)
    };

    using var response = await _http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
    response.EnsureSuccessStatusCode();

    await using var stream = await response.Content.ReadAsStreamAsync();
    using var reader = new StreamReader(stream);

    while (!reader.EndOfStream)
    {
        var line = await reader.ReadLineAsync();
        if (string.IsNullOrEmpty(line) || !line.StartsWith("data:")) continue;

        var json = line["data:".Length..].Trim();
        if (json == "[DONE]") yield break;

        using var doc = JsonDocument.Parse(json);
        if (doc.RootElement.TryGetProperty("delta", out var delta) &&
            delta.TryGetProperty("text", out var text))
        {
            yield return text.GetString() ?? "";
        }
    }
}

Consume it from an ASP.NET Core endpoint to push chunks straight to the browser via IAsyncEnumerable or a Response.BodyWriter.

Tool use (function calling) in C#

Tool definitions are plain JSON objects, so you model them the same way as the message payload:

var tools = new[]
{
    new
    {
        name = "get_weather",
        description = "Get current weather for a location",
        input_schema = new
        {
            type = "object",
            properties = new { location = new { type = "string" } },
            required = new[] { "location" }
        }
    }
};

Attach tools to the request body alongside messages. When Claude's response contains a tool_use content block, deserialize the input field, run your actual C# method, and send the result back as a tool_result block in the next message. This round-trip pattern is identical regardless of language — the .NET-specific part is just getting your JSON serialization settings right (System.Text.Json with PropertyNamingPolicy = JsonNamingPolicy.CamelCase where needed).

Resilience and error handling

Wrap calls with Polly for retries on 429s and transient 5xxs:

builder.Services.AddHttpClient<ClaudeClient>()
    .AddTransientHttpErrorPolicy(policy =>
        policy.WaitAndRetryAsync(3, retry => TimeSpan.FromSeconds(Math.Pow(2, retry))));

Check the Retry-After header on rate-limit responses rather than retrying blindly — it tells you exactly how long to back off.

Where SubToAPI fits for .NET teams

If your .NET application sits behind an existing Claude subscription rather than a pay-per-token Anthropic account, SubToAPI exposes that access as a standard HTTPS API with its own sub_live_... application keys. The integration code above works unchanged — you just point BaseAddress at https://api.subtoapi.app/v1/ and swap the auth header for Authorization: Bearer $SUBTOAPI_KEY. It adds streaming, tool use, and usage metadata per key, which is useful if multiple .NET services or team members share one underlying Claude plan and you want per-application usage visibility in one dashboard. See /docs/quickstart, /docs/messages, /docs/streaming, and /docs/tools for request/response specifics, and /pricing for plan details. You can test it against your own .NET client via /signup.

Keeping the wrapper maintainable

As your usage grows, extract shared pieces: a typed ClaudeOptions class for model name and max tokens, a central exception type for API errors, and unit tests that mock HttpMessageHandler so you're not hitting the real API in CI. Avoid building a sprawling abstraction layer — Claude's request shape is stable enough that a 100-line wrapper class covers most production needs.

Questions

Does Anthropic offer an official .NET SDK for Claude? No. Anthropic maintains official Python and TypeScript SDKs only. .NET developers call the REST API directly with HttpClient or use a community-maintained NuGet package.

Can I use the same .NET integration code with a Claude-compatible gateway? Yes. Since the request/response shape is standard JSON over HTTPS, the same HttpClient wrapper works against Anthropic or a gateway like SubToAPI — you only change the base URL and auth header.

How do I handle streaming in ASP.NET Core without a built-in SSE client? Read the response body as a stream, parse lines manually for the data: prefix, and deserialize each JSON chunk with System.Text.Json. Expose the result to clients via IAsyncEnumerable or a custom SSE endpoint.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →