How to Integrate Hugging Face Inference API with ASP.NET Core 10 Using C# (2026)

A model can appear in the Hugging Face Hub without being served by your chosen inference provider. Chat completion and feature extraction also use different request shapes. A reliable API checks task availability before wiring a model identifier into production.

This guide builds two ASP.NET Core 10 endpoints for Hugging Face Inference Providers. One calls the OpenAI-compatible chat router. The other calls feature extraction for a model served by HF Inference.

Which endpoint should the API call?

Chat uses the OpenAI-compatible router. Feature extraction uses a configured model served by HF Inference.
Chat uses the OpenAI-compatible router. Feature extraction uses a configured model served by HF Inference.
Task Route under router.huggingface.co Request
Chat completion /v1/chat/completions Model and role-based messages
Feature extraction /hf-inference/models/<namespace>/<model>/pipeline/feature-extraction Inputs for the selected feature extraction model

Provider and model availability can change. Verify a chat model from the Inference Providers UI and separately choose an embedding model that explicitly supports HF Inference feature extraction. A text-generation model ID is not automatically an embedding model. See Inference Providers and feature extraction.

Configure a token and implement the gateway

Install .NET 10. Create a Hugging Face fine-grained token with the permissions required for Inference Providers, and check access if your selected model is gated. Store it as Hf__Token in the server process. Configure Hf__ChatModel and Hf__EmbeddingModel separately. The latter must use the namespace/model form. The sample binds to loopback and forwards successful JSON for debugging. A production API should validate and project responses into a smaller application contract.

Lab.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup><TargetFramework>net10.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable></PropertyGroup>
</Project>
Program.cs
using System.Net.Http.Headers;
using System.Text.Json;
var b = WebApplication.CreateBuilder(args);
var key = b.Configuration["Hf:Token"] ?? throw new InvalidOperationException("Set Hf__Token.");
var chat = b.Configuration["Hf:ChatModel"] ?? throw new InvalidOperationException("Set Hf__ChatModel.");
var embed = b.Configuration["Hf:EmbeddingModel"] ?? throw new InvalidOperationException("Set Hf__EmbeddingModel.");
var segments = embed.Split('/');
if (segments.Length != 2 || segments.Any(s => s.Length == 0 || s is "." or ".." ||
    s.Any(ch => !char.IsAsciiLetterOrDigit(ch) && ch is not ('_' or '-' or '.'))))
    throw new InvalidOperationException("Use namespace/model for Hf__EmbeddingModel.");
b.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 8192);
b.Services.AddHttpClient("hf", c =>
{
    c.BaseAddress = new Uri("https://router.huggingface.co/");
    c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key);
    c.Timeout = TimeSpan.FromSeconds(25);
}).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AllowAutoRedirect = false });
var app = b.Build();
app.MapPost("/chat", (Input q, IHttpClientFactory f, CancellationToken ct) =>
    Forward(q, "v1/chat/completions", new { model = chat,
        messages = new[] { new { role = "user", content = q.Text } }, max_tokens = 300 }, f, ct));
app.MapPost("/embed", (Input q, IHttpClientFactory f, CancellationToken ct) =>
    Forward(q, "hf-inference/models/" + embed + "/pipeline/feature-extraction",
        new { inputs = q.Text }, f, ct));
await app.RunAsync();

static async Task<IResult> Forward(Input q, string path, object body, IHttpClientFactory f, CancellationToken ct)
{
    if (string.IsNullOrWhiteSpace(q.Text) || q.Text.Length > 2000)
        return Results.BadRequest("Use 1–2000 characters.");
    try
    {
        using var client = f.CreateClient("hf");
        using var reply = await client.PostAsJsonAsync(path, body, ct);
        if ((int)reply.StatusCode == 429) return Results.StatusCode(429);
        if (!reply.IsSuccessStatusCode) return Results.Problem(statusCode: 502);
        using var data = await JsonDocument.ParseAsync(await reply.Content.ReadAsStreamAsync(ct),
            cancellationToken: ct);
        return Results.Json(data.RootElement.Clone());
    }
    catch (OperationCanceledException) when (!ct.IsCancellationRequested)
    { return Results.Problem(statusCode: 504); }
    catch (Exception e) when (e is HttpRequestException or JsonException)
    { return Results.Problem(statusCode: 502); }
}
record Input(string Text);

The named client fixes the upstream host, disables redirects, and applies a deadline. It keeps the bearer token out of client requests and responses. The model path is derived from server configuration and validated so it cannot contain traversal, query parameters or a new host. The sample maps 429, timeout and other provider failures to safe application responses.

Test both tasks

Start the API and request chat and embeddings
dotnet run --no-launch-profile -- --urls http://127.0.0.1:5080

# In another terminal:
curl -i http://127.0.0.1:5080/chat -H 'Content-Type: application/json' \
  -d '{"text":"Describe vector search in one sentence."}'
curl -i http://127.0.0.1:5080/embed -H 'Content-Type: application/json' \
  -d '{"text":"vector search"}'

A supported chat response should contain choices with generated content. Feature extraction should return numerical data with the shape documented for that model and provider. Check whether it represents one sentence vector or token-level features before saving it. Verify finite values and dimension consistency. A retrieval index must use compatible document and query embeddings. See the ASP.NET PB RAG chatbot guide.

The C# project compiled on .NET 10. Live Hugging Face model availability and response shapes were not exercised.

Operational checks

Keep a small test fixture for each task and rerun it when changing providers. Observe rate limits and usage on the provider dashboard. Avoid blind retries after a partial result or timeout. Provider quotas and billing rules vary. Use a per-user quota in your API, verify model licenses and data routing, and make expected costs visible to operators. Log task, provider outcome and duration without storing credentials or input text.

Symptom Check
401 or 403 upstream Token permission and gated model access
Model or route unavailable Model task and provider compatibility
429 Account or provider rate limit
Wrong vector shape Model pooling and task response contract
Inconsistent search results Mixed embedding models or dimensions

FAQ and references

Can the same token be used for both calls? It can when it has the required provider permissions and access to both models. Restrict permissions to the workload.

Does Hub availability guarantee hosted inference? No. Confirm that the exact model is served for the exact task and provider.

Should I store raw inference responses? Prefer the fields your application needs, and apply a retention policy if those responses contain sensitive data.

Validate a small chat and embedding fixture before putting either route behind a customer-facing workflow.

Comments