/compare/powershell/invoke-webrequest-vs-invoke-restmethod
Invoke-WebRequest vs Invoke-RestMethod: which one to use in PowerShell
- tool
- PowerShell
- compares
- Invoke-WebRequest vs Invoke-RestMethod
Use Invoke-RestMethod for REST APIs: it parses a JSON (or XML) response into PowerShell objects, so $r.name works directly. Use Invoke-WebRequest when you need the HTTP details: it returns a response object with StatusCode, Headers and the raw Content string, and is the usual choice for HTML pages and downloads with -OutFile. Both send the same requests and accept the same main parameters (-Method, -Headers, -Body, -ContentType).
Same request, both cmdlets
A local Node.js server returns a small JSON document on 127.0.0.1:4500:
@'
require("http").createServer((req, res) => {
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({ id: 7, name: "widget", tags: ["a", "b"] }));
}).listen(4500, "127.0.0.1");
setTimeout(() => process.exit(0), 6000);
'@ | Set-Content server.js
$server = Start-Process node -ArgumentList server.js -NoNewWindow -PassThru
Start-Sleep -Milliseconds 1000
$url = 'http://127.0.0.1:4500/item'
$web = Invoke-WebRequest -Uri $url -UseBasicParsing
"Invoke-WebRequest -> $($web.GetType().Name), StatusCode $($web.StatusCode), Content-Type $($web.Headers['Content-Type'])"
"Content: $($web.Content)"
$rest = Invoke-RestMethod -Uri $url
"Invoke-RestMethod -> $($rest.GetType().Name), name=$($rest.name), tags=$($rest.tags -join ',')"
"Same thing by hand: $(($web.Content | ConvertFrom-Json).name)"
$server.WaitForExit()Output
Invoke-WebRequest -> BasicHtmlWebResponseObject, StatusCode 200, Content-Type application/json
Content: {"id":7,"name":"widget","tags":["a","b"]}
Invoke-RestMethod -> PSCustomObject, name=widget, tags=a,b
Same thing by hand: widgetCriterion by criterion
| Criterion | Invoke-WebRequest | Invoke-RestMethod |
|---|---|---|
| Returns | response object (Content, StatusCode, Headers, RawContent) | the parsed body: objects for JSON, XmlDocument for XML, string otherwise |
| JSON | string in .Content; pipe to ConvertFrom-Json | already converted |
| Status code and headers | .StatusCode, .Headers | PowerShell 7: -StatusCodeVariable, -ResponseHeadersVariable; 5.1: not available |
| HTML pages | .Links, .Images, .Forms (5.1 full parsing; limited in 7) | raw HTML string |
| Download a file | -OutFile file | -OutFile file |
| Non-2xx status | throws; PowerShell 7 has -SkipHttpErrorCheck | throws; PowerShell 7 has -SkipHttpErrorCheck |
| Paging (RSS, Atom, Link headers) | no | PowerShell 7: -FollowRelLink |
Windows PowerShell 5.1: use -UseBasicParsing
In Windows PowerShell 5.1, Invoke-WebRequest without -UseBasicParsing parses the page with the Internet Explorer engine. On current Windows builds it also asks for confirmation before doing so, and in a non-interactive session (scheduled task, CI, powershell -NonInteractive) that prompt fails. This was observed on Windows 11 with Windows PowerShell 5.1.26100:
@'
require("http").createServer((req, res) => res.end("<html><body>ok</body></html>")).listen(4501, "127.0.0.1");
setTimeout(() => process.exit(0), 5000);
'@ | Set-Content server.js
$server = Start-Process node -ArgumentList server.js -NoNewWindow -PassThru
Start-Sleep -Milliseconds 1000
try { Invoke-WebRequest -Uri 'http://127.0.0.1:4501/' | Out-Null; 'no prompt' } catch { $_.Exception.Message }
(Invoke-WebRequest -Uri 'http://127.0.0.1:4501/' -UseBasicParsing).StatusCode
$server.WaitForExit()Output
Windows PowerShell is in NonInteractive mode. Read and Prompt functionality is not available. 200
Add -UseBasicParsing to every Invoke-WebRequest in 5.1 scripts. In PowerShell 7 the parameter is accepted but has no effect: basic parsing is the only mode.
Sources
- learn.microsoft.comlearn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod
- learn.microsoft.comlearn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest
- learn.microsoft.comlearn.microsoft.com/en-us/powershell/scripting/whats-new/differences-from-windows-powershell