Skip to the content.

How to use curl in ASP.NET Core

To use curl in ASP.NET Core, add the CurlDotNet package, register a configured LibCurl instance as a singleton in Program.cs, and inject it into your minimal API handlers or controllers. For one-off calls, the static Curl.ExecuteAsync works without any registration.

dotnet add package CurlDotNet

Register LibCurl as a singleton

LibCurl owns one HttpClient, so a single shared instance reuses connections across requests. Configure its defaults once, at startup:

using CurlDotNet.Lib;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton(sp => new LibCurl()
    .WithBearerToken(builder.Configuration["Upstream:Token"] ?? "")
    .WithUserAgent("MyService/1.0")
    .WithTimeout(TimeSpan.FromSeconds(10)));

var app = builder.Build();

The With... methods change the instance’s shared defaults. Call them only during registration, never inside a request handler, or concurrent requests will see each other’s settings. Per-request options go in the Action<CurlOptions> argument instead (see below). The container disposes the singleton at shutdown.

Minimal API

using CurlDotNet.Lib;

var app = WebApplication.CreateBuilder(args).Build();
using var curl = new LibCurl();

app.MapGet("/weather/{city}", async (string city, HttpContext ctx) =>
{
    var url = $"https://wttr.in/{Uri.EscapeDataString(city)}?format=j1";
    var result = await curl.GetAsync(url, cancellationToken: ctx.RequestAborted);
    return result.IsSuccess
        ? Results.Content(result.Body, "application/json")
        : Results.StatusCode(502);
});

app.Run();

In a real app, take LibCurl curl as a handler parameter so it comes from the container.

Controller

using CurlDotNet.Lib;
using Microsoft.AspNetCore.Mvc;

[ApiController, Route("api/[controller]")]
public class ReposController(LibCurl curl) : ControllerBase
{
    [HttpGet("{owner}/{repo}")]
    public async Task<IActionResult> Get(string owner, string repo, CancellationToken ct)
    {
        var url = $"https://api.github.com/repos/{Uri.EscapeDataString(owner)}/{Uri.EscapeDataString(repo)}";
        var result = await curl.GetAsync(url, o => o.Headers["Accept"] = "application/vnd.github+json", ct);
        return StatusCode(result.StatusCode, result.Body);
    }
}

The lambda adds a header to this call only. LibCurl merges its defaults into a fresh CurlOptions for each call, so per-call changes do not leak.

Using curl strings in a service

Running a copied curl command is fine for fixed requests. Never paste user input into a command string: a value such as x -o /app/appsettings.json would be read as extra curl flags. Use LibCurl or CurlRequestBuilder for anything user-controlled:

using CurlDotNet.Core;

static Task<CurlResult> Search(string userQuery, CancellationToken ct) =>
    CurlRequestBuilder
        .Get($"https://httpbin.org/get?q={Uri.EscapeDataString(userQuery)}")
        .WithTimeout(TimeSpan.FromSeconds(5))
        .ExecuteAsync(ct);

Notes