Skip to the content.

curl vs Invoke-WebRequest vs Invoke-RestMethod in PowerShell

In PowerShell, Invoke-WebRequest returns the raw HTTP response, Invoke-RestMethod returns the body already parsed (JSON becomes objects), and curl.exe is the native curl program. In Windows PowerShell 5.1, typing curl does not run curl at all: it is an alias for Invoke-WebRequest. CurlDotNet adds a fourth option: run the curl command text in-process with [CurlDotNet.Curl]::Execute(...).

The curl alias trap

Get-Command curl      # Windows PowerShell 5.1: Alias  curl -> Invoke-WebRequest
                      # PowerShell 7+:          Application  curl(.exe)
curl.exe --version    # always the real curl (included with Windows 10 1803 and later)

PowerShell 6 removed the curl and wget aliases, but Windows PowerShell 5.1 still ships them. A pasted curl -H "Accept: application/json" https://... in 5.1 fails with parameter-binding errors, because -H binds to Invoke-WebRequest -Headers, which expects a hashtable, not a string.

Why pasted curl commands break in PowerShell

Even when curl.exe really runs, commands written for bash often fail:

  1. Line continuation. Bash uses \ at the end of a line. PowerShell uses a backtick. With \, PowerShell runs the first line on its own, then tries to run -H ... as a command.
  2. Embedded double quotes. Windows PowerShell 5.1, and PowerShell 7 before 7.3, drop embedded " when passing arguments to native programs. -d '{"key":"value"}' reaches curl.exe as {key:value}. That is why Windows-oriented docs write -d '{\"key\":\"value\"}'. PowerShell 7.3 and later pass the quotes through by default (see $PSNativeCommandArgumentPassing).
  3. Variable expansion. In double-quoted strings PowerShell expands $TOKEN itself, often to an empty string.

How CurlDotNet sidesteps all three

The command is a .NET string handed to a method, not arguments to a native process, so PowerShell’s native-argument rules never apply. Inside a single-quoted here-string PowerShell changes nothing, and CurlDotNet’s parser handles the shell syntax:

$cmd = @'
curl -X POST https://httpbin.org/post `
  -H "Content-Type: application/json" `
  -d '{"key":"value"}'
'@
$r = [CurlDotNet.Curl]::Execute($cmd)
($r.Body | ConvertFrom-Json).json.key      # value

The same PowerShell-style command from C#, verified with 9.8.0 against httpbin.org:

using CurlDotNet;

var ps = "curl -X POST https://httpbin.org/post `\n  -H 'Content-Type: application/json' `\n  -d '{\"key\":\"value\"}'";
var result = await Curl.ExecuteAsync(ps);
Console.WriteLine(result.Body);  // "json": { "key": "value" }

Side by side

  Invoke-WebRequest Invoke-RestMethod curl.exe CurlDotNet
Input Cmdlet parameters Cmdlet parameters curl flags, subject to PowerShell quoting curl command as one string
Output Response object (StatusCode, Content, Headers) Parsed body Text on stdout CurlResult (StatusCode, Body, Headers, BinaryData)
4xx / 5xx Throws (PowerShell 7 adds -SkipHttpErrorCheck) Throws (same switch) Exit code 0 unless -f IsSuccess = $false, no throw unless -f
JSON ConvertFrom-Json Automatic ConvertFrom-Json ConvertFrom-Json (or ParseJson in C#)
Available Built in Built in Windows 10 1803+, macOS, Linux Load the DLL
Same code in C# No No No (Process.Start) Yes

Which to use

The PowerShell behaviors described here are standard PowerShell; the CurlDotNet snippets in PowerShell were not run in a PowerShell session for this page.