Curl to PowerShell: Convert curl Commands to Invoke-RestMethod
Convert a curl command to PowerShell's Invoke-RestMethod — headers, JSON bodies, and the gotchas curl.exe vs the PowerShell alias causes.
Try it now: cURL to JavaScript Fetch Converter — Paste a cURL command and get clean fetch() code with headers, body, auth and method preserved. Nothing is sent to a server.
curl to PowerShell Means Invoke-RestMethod, Not curl
Converting curl to PowerShell almost never means keeping the word curl in the script at all. The real target is Invoke-RestMethod (or its lower-level sibling, Invoke-WebRequest) — a cmdlet with named parameters instead of command-line flags, a hashtable for headers instead of repeated -Harguments, and a return value that's already a parsed object instead of raw response text you'd otherwise pipe through something else. The translation is mechanical once you know the mapping, but every flag lands on a slightly different shape than its curl equivalent, and that shape mismatch — not the syntax — is where converted scripts usually break.
This is the flag-by-flag, PowerShell-specific version of that translation. For the broader picture — the same curl command converted to fetch(), Axios, Node's httpsmodule, and Python's requests — see the curl to code converter overview; this article picks up where that one hands off to PowerShell.
Mapping curl Flags to Invoke-RestMethod Parameters
Four curl flags account for most real-world commands, and each has a direct, named Invoke-RestMethod parameter — not a positional argument, which is the first habit to unlearn coming from curl:
-H "Header: value"becomes an entry in a-Headershashtable:-Headers @{ "Authorization" = "Bearer abc123" }. Every repeated-Hflag becomes one more key in the same hashtable, not a separate parameter.-X POSTbecomes-Method Post. Unlike curl, PowerShell has no equivalent of curl's “-dsilently implies POST” default —Invoke-RestMethoddefaults toGETregardless of whether a body is attached, so a-Methodvalue has to be stated explicitly whenever the curl command relied on that implicit switch.-d,--databecomes-Body. A JSON body can be passed as the same literal JSON string curl used, wrapped in single quotes, or built as a PowerShell hashtable and converted withConvertTo-Json— the second form is worth the extra line the moment the body has more than a couple of fields, since a hashtable is easier to edit and diff than a hand-quoted JSON string sitting inside PowerShell's own quoting rules.-H "Content-Type: application/json"still needs to be set explicitly in the headers hashtable —Invoke-RestMethoddoes not infer a content type from the shape of-Body, so a JSON body sent without this header is frequently accepted by permissive APIs and silently misread as form data by strict ones.
curl -X POST https://api.example.com/users \
-H "Authorization: Bearer abc123" \
-H "Content-Type: application/json" \
-d '{"name": "Ada Lovelace", "role": "engineer"}'
# PowerShell equivalent
$headers = @{
"Authorization" = "Bearer abc123"
"Content-Type" = "application/json"
}
$body = @{
name = "Ada Lovelace"
role = "engineer"
} | ConvertTo-Json
Invoke-RestMethod -Uri "https://api.example.com/users" `
-Method Post -Headers $headers -Body $bodyTwo details in that example are easy to miss when converting by hand. First, Invoke-RestMethodalready parses a JSON response into a PowerShell object — there's no equivalent of piping curl's output through something else to read it; the returned value can be used directly, e.g. $response.id. Second, PowerShell's line-continuation character is the backtick (`` ` ``), not curl's backslash, and a trailing space after it breaks the continuation silently — a stray space at the end of a line is one of the most common reasons a pasted-in multi-line command fails with a confusing parser error.
The -u Flag: Where a Direct Translation Breaks
curl's -u user:passflag is the one place a literal, one-line translation into PowerShell doesn't work, and it's worth being precise about why. Invoke-RestMethod does have a -Credential parameter for Basic Auth, but it expects a real PSCredential object — a structured type built from a username and a SecureString password — not a plain "user:pass" string. Passing a string where -Credential expects a PSCredential fails outright rather than quietly working, which is what trips people up converting a curl command they expected to map one flag to one parameter.
There are two correct ways to handle it, and which one to reach for depends on whether the script runs interactively or unattended:
-Credential (Get-Credential)— prompts for a username and password at runtime and builds thePSCredentialobject correctly. The right choice for a script a person runs by hand, since nothing sensitive is hard-coded into the file.- An explicit
Authorization: Basicheader — base64-encodeuser:passyourself and set it as a header, exactly like curl does internally. This is the only practical option for an unattended script, a CI job, or anywhereGet-Credential's interactive prompt isn't available.
curl -u admin:s3cr3t https://api.example.com/status
# Option 1 — interactive
$cred = Get-Credential # prompts for username + password
Invoke-RestMethod -Uri "https://api.example.com/status" -Credential $cred
# Option 2 — unattended, matches curl's own Basic Auth mechanism
$pair = "admin:s3cr3t"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($pair)
$auth = "Basic " + [System.Convert]::ToBase64String($bytes)
Invoke-RestMethod -Uri "https://api.example.com/status" `
-Headers @{ "Authorization" = $auth }Both requests are byte-for-byte identical to what curl's -uflag sends — Basic Auth is nothing more than exactly this base64-encoded header under the hood — so the choice between them is purely about whether the credential can be typed in each time the script runs, not about which one is “more correct.”
Which curl Actually Runs on Your Machine
This is the detail most worth getting exactly right, because the honest answer depends on which PowerShell you have, not on a single blanket rule. Windows PowerShell 5.1 — the version that ships built into every copy of Windows — defines curl as an alias for Invoke-WebRequest, a cmdlet with a genuinely different parameter set and different default behavior than real curl.exe. A flag like -o or -Lis either interpreted completely differently or rejected outright, and a script written for real curl that happens to run under this alias without erroring can still silently behave differently — a follow-redirects default that doesn't match, a response object shaped nothing like curl's plain text output. There is no visible warning when this happens; the command just runs something else.
PowerShell 7 and later removed that alias by default, which flips the situation rather than simply fixing it: if a standalone curl.exe is installed and present on PATH (bundled with modern Windows 10/11 builds, or installed separately), typing curl in PowerShell 7+ runs the real thing. So the practical rule is this — check which situation applies to you rather than assume either direction:
- Windows PowerShell 5.1 (run
$PSVersionTable.PSVersionto confirm — major version5):curlis aliased toInvoke-WebRequest. Real curl syntax will not behave as documented. - PowerShell 7+, with
curl.exepresent:curlruns the real binary, and curl's actual documented flags apply as written. - PowerShell 7+, with no
curl.exeonPATH: the command fails outright rather than silently substituting something else — the one situation where the ambiguity at least surfaces as an error instead of a quiet behavior change.
Run Get-Command curl and it tells you outright which of the three you're in: an Alias command type pointing at Invoke-WebRequest, or an Application command type pointing at a real curl.exepath. That's a more reliable check than assuming based on the PowerShell version alone, since a real curl.exe can be installed (or missing) independently of which shell version you're running.
Convert curl to PowerShell Without the Guesswork
GenKitLab's cURL to Fetch Converter handles the same flag parsing described above — headers, JSON body, method, and Basic Auth — for the JavaScript fetch()target, and it's the closest live tool in the same curl-conversion family if the destination for a converted request ends up being a browser or Node script instead of a PowerShell one. Every conversion runs entirely client-side: parsing happens in your browser, and nothing you paste is uploaded anywhere, which matters as much for a curl command with a live bearer token as it does for one with a plaintext -u user:pass pair.
For the wider picture — the same curl command mapped to fetch, Axios, Node's https module, and Python's requests side by side, with the shared -d-implies-POST gotcha covered in full — the curl to code converter guide is the one to read next.
Frequently asked questions
›What is the PowerShell equivalent of curl?
Invoke-RestMethod, or the lower-level Invoke-WebRequest. Invoke-RestMethod is the closer match for API calls specifically — it parses a JSON response into a usable PowerShell object automatically, the same way curl's output would need a separate JSON parser piped after it.
›Is curl aliased to Invoke-WebRequest in PowerShell?
Only in Windows PowerShell 5.1, the version built into Windows by default — there, curl is an alias for Invoke-WebRequest, which has different parameters and defaults than real curl. PowerShell 7 and later removed that alias, so curl runs the real curl.exe there if one is installed and on PATH. Run Get-Command curl to see which applies on your machine.
›How do I convert curl -H headers to Invoke-RestMethod?
Build a hashtable with one key per header and pass it as -Headers, e.g. -Headers @{ "Authorization" = "Bearer abc123" }. Every repeated -H flag in the curl command becomes one more key in that same hashtable rather than a separate parameter.
›Why does Invoke-RestMethod -Credential fail with a plain string?
Because -Credential expects a real PSCredential object built from a username and a SecureString password, not a "user:pass" string like curl's -u flag. Use Get-Credential to build the object interactively, or, for an unattended script, base64-encode user:pass yourself and set it as an explicit Authorization: Basic header instead.
›Does -d in curl automatically switch to POST in PowerShell too?
No, and this is a real difference between the two. curl silently switches to POST when a -d/--data flag is present, even with no -X. Invoke-RestMethod has no equivalent default — it stays on GET regardless of whether -Body is set, so -Method Post has to be stated explicitly whenever the original curl command relied on that implicit switch.
›Can I just paste a curl command straight into PowerShell?
It depends entirely on which curl runs in your shell. In PowerShell 7+ with curl.exe installed, yes — the real binary reads the command as written. In Windows PowerShell 5.1, no — curl there is an alias for Invoke-WebRequest, a cmdlet with different flags and defaults, and the pasted command may run without an error while doing something different than intended.
Last updated