CurlDotNet 9.6.0

dotnet add package CurlDotNet --version 9.6.0
                    
NuGet\Install-Package CurlDotNet -Version 9.6.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CurlDotNet" Version="9.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CurlDotNet" Version="9.6.0" />
                    
Directory.Packages.props
<PackageReference Include="CurlDotNet" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CurlDotNet --version 9.6.0
                    
#r "nuget: CurlDotNet, 9.6.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CurlDotNet@9.6.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CurlDotNet&version=9.6.0
                    
Install as a Cake Addin
#tool nuget:?package=CurlDotNet&version=9.6.0
                    
Install as a Cake Tool

CurlDotNet - curl for C# and .NET

NuGet Version Downloads Build Status License Coverage

CurlDotNet - Why .NET Needs a POSIX/GNU Userland

<div align="center"> <img src="https://github.com/jacob-mellor/curl-dot-net/blob/master/src/CurlDotNet/icon128.png?raw=true" alt="CurlDotNet Icon" width="128" height="128"/> </div>

🆕 New to curl? Start Here!

📖 Complete Beginner's Guide to curl in C# →

The Industry Standard curl Experience for C# and .NET Developers

CurlDotNet brings the power and simplicity of curl to the .NET ecosystem. Execute curl commands directly in C#, make HTTP requests with curl's battle-tested reliability, and leverage decades of curl development - all with pure .NET code.

🚀 Quick Start with curl for C# and .NET

using CurlDotNet;

// Execute curl commands directly in C#
var result = await Curl.ExecuteAsync("curl -X GET https://api.github.com/users/octocat -H 'Accept: application/json'");

// Or use the fluent API for type safety
var response = await Curl.GetAsync("https://api.github.com/users/octocat")
    .WithHeader("Accept", "application/vnd.github.v3+json")
    .ExecuteAsync();

// Simple one-liners for common operations
var json = await Curl.GetJsonAsync<GitHubUser>("https://api.github.com/users/octocat");

📊 Code Coverage

  • Line Coverage: 65.9%
  • Branch Coverage: 72%
  • Method Coverage: 59.9%
  • Tests: 657 total, 619 passing, 38 failing
  • Last Updated: 2025-11-18

📦 Installation

# .NET CLI
dotnet add package CurlDotNet

# Package Manager Console
Install-Package CurlDotNet

# PackageReference
<PackageReference Include="CurlDotNet" Version="*" />

✨ Why CurlDotNet?

curl Compatibility in .NET

  • 100% curl behavior - Your C# code works exactly like curl commands
  • Parse any curl command - Copy from documentation and paste into your code
  • Industry standard - Used by millions of developers worldwide

Superior to HttpClient

  • More features - Proxies, authentication chains, retries, rate limiting
  • Better debugging - curl-style verbose output
  • Simpler API - One-line operations that would take 20+ lines with HttpClient

Pure .NET Implementation

  • No native dependencies - 100% managed C# code
  • Universal compatibility - Runs anywhere .NET runs: Windows, Linux, macOS, iOS, Android, IoT devices, Docker, cloud
  • Safe and secure - No P/Invoke, no unmanaged memory

🎯 Key Features

HTTP/REST Operations

// GET request
var data = await Curl.GetAsync("https://api.example.com/users");

// POST JSON
var user = await Curl.PostJsonAsync("https://api.example.com/users",
    new { name = "Alice", email = "alice@example.com" });

// PUT with authentication
await Curl.PutAsync("https://api.example.com/users/123")
    .WithBearerToken(token)
    .WithJson(updatedData)
    .ExecuteAsync();

// DELETE
await Curl.DeleteAsync("https://api.example.com/users/123");

// PATCH
await Curl.PatchAsync("https://api.example.com/users/123")
    .WithJson(new { status = "active" })
    .ExecuteAsync();

Authentication Methods

// Bearer Token
await Curl.GetAsync("https://api.example.com")
    .WithBearerToken("your-token")
    .ExecuteAsync();

// Basic Auth
await Curl.GetAsync("https://api.example.com")
    .WithBasicAuth("username", "password")
    .ExecuteAsync();

// API Key
await Curl.GetAsync("https://api.example.com")
    .WithApiKey("X-API-Key", "your-api-key")
    .ExecuteAsync();

// OAuth 2.0
await Curl.GetAsync("https://api.example.com")
    .WithOAuth2("access-token", "Bearer")
    .ExecuteAsync();

// Custom headers
await Curl.GetAsync("https://api.example.com")
    .WithHeader("X-Custom-Auth", "token123")
    .ExecuteAsync();

AWS Signature V4 Authentication (--aws-sigv4)

// Sign requests with AWS SigV4 - works with AWS, GCP, and custom providers
var result = await Curl.ExecuteAsync(
    @"curl --aws-sigv4 ""aws:amz:us-east-1:s3"" -u ""AKID:SECRET"" https://s3.us-east-1.amazonaws.com/my-bucket");

// API Gateway with request body
var result = await Curl.ExecuteAsync(
    @"curl --aws-sigv4 ""aws:amz:eu-central-1:execute-api"" -u ""AKID:SECRET""
    --json '{""action"":""invoke""}' https://abc123.execute-api.eu-central-1.amazonaws.com/prod/fn");

// Google Cloud Platform
var result = await Curl.ExecuteAsync(
    @"curl --aws-sigv4 ""gcp:goog:us-central1:storage"" -u ""KEY:SECRET""
    https://storage.googleapis.com/my-bucket/my-object");

// Temporary credentials with session token
var result = await Curl.ExecuteAsync(
    @"curl --aws-sigv4 ""aws:amz:us-east-1:s3"" -u ""AKID:SECRET""
    -H ""x-amz-security-token: SESSION_TOKEN"" https://s3.amazonaws.com/bucket");

File Operations

// Download file with progress
await Curl.DownloadFileAsync(
    "https://example.com/large-file.zip",
    "local-file.zip",
    progress: (percent) => Console.WriteLine($"Progress: {percent:F1}%")
);

// Upload file
await Curl.PostAsync("https://api.example.com/upload")
    .WithFile("file", "/path/to/file.pdf")
    .ExecuteAsync();

// Multipart form data
await Curl.PostAsync("https://api.example.com/upload")
    .WithMultipartForm(form => form
        .AddString("name", "John")
        .AddFile("document", "/path/to/doc.pdf")
        .AddFile("image", "/path/to/image.jpg"))
    .ExecuteAsync();

JSON API Requests (--json flag)

// Use curl's --json flag - automatically sets Content-Type and Accept headers
var result = await Curl.ExecuteAsync(
    "curl --json '{\"query\":\"search term\"}' https://api.example.com/search");

// Read JSON body from a file (just like curl) - @file reads file contents at execution time
var result = await Curl.ExecuteAsync(
    @"curl --json @""c:\data\query.json"" https://api.example.com/search");

// Combine with other curl flags - proxies, auth, output, etc.
var result = await Curl.ExecuteAsync(@"curl -X POST
    -H ""Authorization: Bearer token""
    --json @query.json
    -L -k -o result.json
    https://graph.microsoft.com/beta/security/runHuntingQuery");

@file References for Request Data

// -d @file reads file contents and sends as request body (like curl)
var result = await Curl.ExecuteAsync(@"curl -d @data.json https://api.example.com");

// --data-binary @file also reads file contents
var result = await Curl.ExecuteAsync(@"curl --data-binary @payload.bin https://api.example.com");

// --data-raw @file sends the literal string "@file" (no file reading - matches curl behavior)
var result = await Curl.ExecuteAsync(@"curl --data-raw @not-a-file https://api.example.com");

Binary File Downloads

// Download binary files (Office docs, PDFs, ZIPs, etc.) - automatically detected
var result = await Curl.ExecuteAsync("curl -o report.xlsx https://example.com/report.xlsx");

// Force binary mode for servers with incorrect Content-Type headers
var report = await CurlRequestBuilder.Get("https://example.com/report.xlsx")
    .AsBinary()
    .WithOutput("report.xlsx")
    .ExecuteAsync();

// Register custom MIME types as binary
var data = await CurlRequestBuilder.Get("https://example.com/data")
    .WithBinaryContentType("application/x-custom-format")
    .ExecuteAsync();
data.SaveToFile("output.bin");

Proxy Support

// HTTP proxy
await Curl.GetAsync("https://api.example.com")
    .WithProxy("http://proxy.example.com:8080")
    .WithProxyAuth("username", "password")
    .ExecuteAsync();

// SOCKS5 proxy (Tor)
await Curl.GetAsync("https://check.torproject.org")
    .WithSocks5Proxy("socks5://127.0.0.1:9050")
    .ExecuteAsync();

// Residential proxy
await Curl.GetAsync("https://api.example.com")
    .WithProxy("http://gate.smartproxy.com:10000")
    .WithProxyAuth("user-country-us", "password")
    .ExecuteAsync();

// Rotating proxy
await Curl.GetAsync("https://api.example.com")
    .WithProxy("http://proxy.provider.com:8000")
    .WithProxyAuth($"user-session-{Guid.NewGuid()}", "password")
    .ExecuteAsync();

// Backconnect proxy
await Curl.GetAsync("https://api.example.com")
    .WithBackconnectProxy("proxy.provider.com", 20001)
    .ExecuteAsync();

Advanced Features

// Retry with exponential backoff
await Curl.GetAsync("https://api.example.com")
    .WithRetry(maxAttempts: 3)
    .WithExponentialBackoff()
    .ExecuteAsync();

// Rate limiting
await Curl.GetAsync("https://api.example.com")
    .WithRateLimit(requestsPerMinute: 60)
    .ExecuteAsync();

// Timeout handling
await Curl.GetAsync("https://api.example.com")
    .WithTimeout(TimeSpan.FromSeconds(30))
    .WithConnectTimeout(TimeSpan.FromSeconds(10))
    .ExecuteAsync();

// Follow redirects
await Curl.GetAsync("https://bit.ly/shortened")
    .FollowRedirects(maxRedirects: 5)
    .ExecuteAsync();

// Cookie management
var cookieJar = new CookieContainer();
await Curl.GetAsync("https://example.com")
    .WithCookieContainer(cookieJar)
    .ExecuteAsync();

📊 Performance & Reliability

  • ✅ 244 Unit Tests - 100% pass rate
  • ✅ Self-Contained Testing - No external dependencies
  • ✅ Cross-Platform CI/CD - Windows, Linux, macOS
  • ✅ Production Ready - Used in enterprise applications

🏗️ Platform Support

Platform Version Support
.NET 10, 9, 8, 7, 6, 5 ✅ Full Support
.NET Core 3.1, 3.0, 2.1 ✅ Full Support
.NET Framework 4.8 ✅ Native (Windows, Mac, Linux via Mono)
.NET Framework 4.7.2+ ✅ via .NET Standard 2.0
.NET Standard 2.0+ ✅ Maximum Compatibility
Windows 10, 11, Server 2016+ ✅ Native
Linux Ubuntu, Debian, RHEL, Alpine ✅ Native
macOS 10.14+, Apple Silicon ✅ Native
iOS 12+ ✅ via .NET Standard/MAUI
Android API 21+ ✅ via .NET Standard/MAUI
IoT Raspberry Pi, Arduino ✅ via .NET IoT
Docker All Linux images ✅ Full Support
Azure App Service, Functions, Container Instances ✅ Cloud Native
AWS Lambda, ECS, Fargate ✅ Cloud Native

📖 Documentation

💡 Use Cases

REST API Integration

// GitHub API example
var repos = await Curl.GetJsonAsync<List<Repository>>(
    "https://api.github.com/users/octocat/repos"
);

Web Scraping

// Scrape with proper headers
var html = await Curl.GetAsync("https://example.com")
    .WithUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
    .WithHeader("Accept-Language", "en-US")
    .ExecuteAsync();

Microservices Communication

// Service-to-service with retry
var response = await Curl.PostAsync("http://service-b/api/process")
    .WithJson(requestData)
    .WithRetry(3)
    .WithTimeout(TimeSpan.FromSeconds(5))
    .ExecuteAsync();

CI/CD Automation

// Deploy webhook
await Curl.PostAsync("https://deploy.example.com/webhook")
    .WithJson(new {
        version = "1.2.3",
        environment = "production"
    })
    .WithBearerToken(deployToken)
    .ExecuteAsync();

🔧 Advanced Usage

Custom Middleware

// Add custom middleware for logging, caching, etc.
var result = await Curl.GetAsync("https://api.example.com")
    .UseMiddleware(async (context, next) => {
        Console.WriteLine($"Request: {context.Request.Url}");
        var response = await next();
        Console.WriteLine($"Response: {response.StatusCode}");
        return response;
    })
    .ExecuteAsync();

Code Generation

// Convert curl to other languages
var pythonCode = Curl.ToPythonRequests("curl -X GET https://api.example.com");
var jsCode = Curl.ToJavaScriptFetch("curl -X POST https://api.example.com -d '{}'");
var httpClientCode = Curl.ToHttpClient("curl https://api.example.com");

Debugging

// Enable verbose output like curl -v
var result = await Curl.GetAsync("https://api.example.com")
    .Verbose(true)
    .ExecuteAsync();

// Access detailed timing information
Console.WriteLine($"DNS Lookup: {result.Timings.DnsLookup}ms");
Console.WriteLine($"Connect: {result.Timings.Connect}ms");
Console.WriteLine($"TLS Handshake: {result.Timings.TlsHandshake}ms");
Console.WriteLine($"First Byte: {result.Timings.FirstByte}ms");
Console.WriteLine($"Total: {result.Timings.Total}ms");

Detailed Trace Logging (--trace / --trace-ascii / --trace-time)

When you need to know exactly where a request succeeds or gets blocked — especially behind a corporate proxy — write a full diagnostic trace to a log file. The trace captures connection and proxy selection, TLS certificate verification, the exact request/response headers and bodies, transfer timings, and the precise error if the request fails.

// Paste-a-curl-command style: write a full hex+ASCII trace to a file
await Curl.ExecuteAsync("curl --trace curl-trace.log https://api.example.com");

// Compact, ASCII-only trace with timestamps on every line
await Curl.ExecuteAsync("curl --trace-ascii curl-trace.txt --trace-time https://api.example.com");

// Send the trace to standard output instead of a file
await Curl.ExecuteAsync("curl --trace - https://api.example.com");

// Fluent builder equivalent
var result = await CurlRequestBuilder
    .Get("https://api.example.com")
    .WithTrace("curl-trace.log", includeTimestamps: true)
    .ExecuteAsync();

A trace file looks like this:

== Info: CurlDotNet trace started 2026-06-03 14:03:27
== Info: Command: curl --trace curl-trace.log https://api.example.com
== Info: Trying api.example.com:443...
== Info: Using explicit proxy http://proxy.corp.local:8080 (with credentials)
== Info: Connected to api.example.com (api.example.com) port 443
== Info: TLS: certificate verification enabled
=> Send header, 142 bytes (0x8e)
0000: 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a  GET / HTTP/1.1..
...
<= Recv header, 173 bytes (0xad)
0000: 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d  HTTP/1.1 200 OK.
...
== Info: Transfer complete: HTTP 200, 1256 bytes received.

If the request is blocked, the exact failure is recorded so you can see which stage rejected it:

== Error: Connection failed: The proxy tunnel request to proxy 'http://proxy.corp.local:8080' failed with status code '407'.
== Error:   caused by: HttpRequestException: Proxy authentication required

Code Generation

CurlDotNet can transpile curl commands into code for other languages. This is perfect for building developer tools or converting documentation examples.

// Convert curl to PowerShell
var powershellCode = Curl.ToPowershellCode("curl -X POST https://api.example.com -d 'data'");

// Convert curl to Python Requests
var pythonCode = Curl.ToPythonCode("curl -X POST https://api.example.com -d 'data'");

// Convert curl to JavaScript Fetch
var jsCode = Curl.ToFetchCode("curl -X POST https://api.example.com -d 'data'");

// Convert curl to C# HttpClient
var csharpCode = Curl.ToHttpClientCode("curl -X POST https://api.example.com -d 'data'");

❓ Troubleshooting

SSL/TLS Issues

If you encounter SSL errors (e.g., self-signed certificates), you can disable verification for development:

// Global setting (affects all requests)
Curl.DefaultInsecure = true;

// Per-request setting
await Curl.Execute("curl -k https://self-signed.local");

Timeouts

If requests are timing out, increase the timeout settings:

// Set global timeout to 60 seconds
Curl.DefaultMaxTimeSeconds = 60;

// Or per request
await Curl.Execute("curl --max-time 60 https://slow-api.example.com");

Proxy Authentication

If your proxy requires authentication:

await Curl.Execute("curl -x http://user:pass@proxy.example.com:8080 https://api.example.com");

Where are my requests being blocked? (proxy / firewall debugging)

If requests work locally but fail behind a proxy or firewall, add --trace to capture a detailed log of every stage of the transfer. The log shows which proxy was selected, whether the TLS handshake completed, the request/response headers, and the precise error if the connection is rejected:

await Curl.ExecuteAsync("curl --trace-ascii proxy-debug.log -x http://proxy.corp.local:8080 https://api.example.com");
// Then open proxy-debug.log and look for the first "== Error:" line.

Note: As of v9.6.0, -x/--proxy, --proxy-user, --socks5 and -k/--insecure are honored for HTTP/HTTPS requests (previously they were only applied to FTP).

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

# Clone the repository
git clone https://github.com/jacob-mellor/curl-dot-net.git

# Build the project
dotnet build

# Run tests
dotnet test

# Run with coverage
dotnet test /p:CollectCoverage=true

📊 Examples

Comprehensive examples are available in the examples directory:

📄 License

CurlDotNet is MIT licensed. See LICENSE for details.

🌟 Why Developers Choose CurlDotNet

"Finally, I can just paste curl commands from API docs!" - Senior Developer

"The proxy support is fantastic for our web scraping needs." - Data Engineer

"Migrating from HttpClient saved us hundreds of lines of code." - Tech Lead

"The retry logic and rate limiting just work." - DevOps Engineer

🚦 Getting Started

  1. Install the package: dotnet add package CurlDotNet
  2. Add using: using CurlDotNet;
  3. Make your first request: await Curl.GetAsync("https://api.example.com");

That's it! You're now using the power of curl in C# and .NET.

🆘 Support

📎 Additional Resources

📖 Our Story

Read about how CurlDotNet is bringing curl superpowers to every corner of the .NET 10 & C# stack: 📰 Featured Article: CurlDotNet - Bringing curl Superpowers to Every Corner of the .NET 10 & C# Stack

🌐 Part of UserLand.NET

CurlDotNet is part of the UserLand.NET initiative - bringing Unix/Linux tools to .NET through pure C# implementations.


Keywords: curl C#, curl .NET, C# HTTP client, .NET curl, REST API C#, HTTP requests .NET, web scraping C#, proxy C#, curl for Windows, curl alternative, HttpClient alternative

Author: Jacob Mellor | Sponsored by IronSoftware.com

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
9.6.0 181 6/3/2026
9.5.2 826 2/19/2026
9.5.1 114 2/13/2026
9.5.0 87 2/12/2026
9.4.1 93 2/12/2026
9.4.0 90 2/10/2026
9.3.3 96 2/8/2026
9.2.1 845 12/1/2025
9.2.0 505 12/1/2025
9.1.0 486 12/1/2025
9.0.0 497 12/1/2025
3.0.0 482 12/1/2025
1.3.5 490 12/1/2025
1.2.27 458 11/18/2025
1.2.25 391 11/18/2025
1.2.23 358 11/17/2025
1.2.15 308 11/17/2025
1.1.1 236 11/16/2025
1.1.0 243 11/16/2025
1.0.0 159 11/16/2025

Version 9.6.0 - Detailed trace logging (--trace / --trace-ascii / --trace-time) + proxy/TLS fixes
NEW: --trace <file> writes a full diagnostic trace (hex+ASCII) of the whole transfer
NEW: --trace-ascii <file> writes a compact, ASCII-only trace
NEW: --trace-time prefixes every trace line with a high-resolution timestamp
NEW: Fluent API WithTrace()/WithTraceAscii() on CurlRequestBuilder
  - Captures connection, proxy selection, TLS verification, request/response headers and bodies,
    transfer timings, and the precise error when a request is blocked - ideal for proxy troubleshooting
FIX: --proxy / -x, --proxy-user, --socks5 and -k/--insecure are now honored for HTTP/HTTPS
  - Previously these were parsed but silently ignored on the shared HttpClient
Frameworks: netstandard2.0, net472, net48, net8.0, net9.0, net10.0
Sponsored by IronSoftware

Documentation: https://jacob-mellor.github.io/curl-dot-net/
Repository: https://github.com/jacob-mellor/curl-dot-net
NuGet: CurlDotNet