The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Deserialize an API responseInvoke-RestMethod -Uri $Uri -Method GetView examples
Inspect an HTTP response$Response = Invoke-WebRequest -Uri $Uri -Method GetView examples
Encode GET query valuesInvoke-RestMethod -Uri $Uri -Method Get -Body @{ q = ` 'blue widget'; page = 2 }View examples
Serialize JSON deliberately$Json = @{ name = 'widget'; enabled = $true } | ConvertTo-Json -Depth 10 -CompressView examples
Post UTF-8 JSONInvoke-RestMethod -Uri $Uri -Method Post -Body $Json ` -ContentType 'application/json; charset=utf-8'View examples
Request a response media typeInvoke-RestMethod -Uri $Uri -Headers @{ Accept = ` 'application/json' }View examples
Prompt for a bearer tokenInvoke-RestMethod -Uri $Uri -Authentication Bearer ` -Token (Read-Host -AsSecureString)View examples
Capture a web sessionInvoke-WebRequest -Uri $Uri -SessionVariable ApiSessionView examples
Reuse cookies and session stateInvoke-RestMethod -Uri $Uri -WebSession $ApiSessionView examples
Use a client certificateInvoke-RestMethod -Uri $Uri -Certificate (Get-Item ` "Cert:\CurrentUser\My\$Thumbprint")View examples
Constrain the TLS protocolInvoke-RestMethod -Uri $Uri -SslProtocol Tls12View examples
Use an explicit proxyInvoke-RestMethod -Uri $Uri -Proxy ` 'https://proxy.example.com:8443'View examples
Follow bounded relation linksInvoke-RestMethod -Uri $Uri -FollowRelLink ` -MaximumFollowRelLink 5View examples
Capture status and headersInvoke-RestMethod -Uri $Uri -StatusCodeVariable Status ` -ResponseHeadersVariable HeadersView examples
Retry a bounded requestInvoke-RestMethod -Uri $Uri -MaximumRetryCount 3 ` -RetryIntervalSec 2View examples
Bound connection and idle readsInvoke-RestMethod -Uri $Uri -ConnectionTimeoutSeconds 10 ` -OperationTimeoutSeconds 30View examples
Cancel a request jobStop-Job -Job $RequestJobView examples
Download to an exact pathInvoke-WebRequest -Uri $Uri -OutFile '.\artifact.zip'View examples
Resume a partial downloadInvoke-WebRequest -Uri $Uri -OutFile ` '.\artifact.zip.part' -ResumeView examples
Calculate a SHA-256 digest(Get-FileHash -LiteralPath '.\artifact.zip' -Algorithm ` SHA256).HashView examples
Process HTTP error bodiesInvoke-RestMethod -Uri $Uri -SkipHttpErrorCheck ` -StatusCodeVariable StatusView examples
Catch request failurestry { Invoke-RestMethod -Uri $Uri -ErrorAction Stop } ` catch { Write-Error $_ }View examples
Use safe Windows PowerShell parsingInvoke-WebRequest -UseBasicParsing -Uri $UriView examples
Invoke native curl explicitlycurl.exe --fail --output artifact.zip ` https://example.com/artifact.zipView examples

PowerShell's web cmdlets cover both object-oriented REST calls and lower-level response inspection. Reliable automation makes the HTTP contract explicit: encode inputs, set the intended media type, keep credentials out of source and logs, validate TLS, bound pagination and waits, retry only operations that can be repeated safely, and treat downloaded bytes as untrusted until their identity is verified.

Step by step

Detailed examples

01

Choose objects or response control

Invoke-RestMethod is the natural default for JSON and XML APIs because it deserializes supported response bodies. Invoke-WebRequest returns a response wrapper whose StatusCode, Headers, Content, RawContent, and download options are useful when the wire response matters. In PowerShell 7, HTML handling is basic rather than a browser DOM. One subtle pipeline rule remains important: when Invoke-RestMethod receives a JSON array, it can write that array as one pipeline object; use parentheses or Write-Output when downstream commands must enumerate every member.

Select API objects and inspect response metadata
$ApiUri = 'https://api.example.com/v1/widgets'
$Items = Invoke-RestMethod -Uri $ApiUri -Method Get
$Items | Write-Output | Select-Object -Property id, name

$Response = Invoke-WebRequest -Uri $ApiUri -Method Head
[pscustomobject]@{
    Status      = $Response.StatusCode
    ContentType = $Response.Headers['Content-Type']
}
Back to quick reference ↑
02

Encode the request contract explicitly

For a GET request, a dictionary supplied to Body is URL-encoded and added to the URI as query parameters. This avoids hand-concatenating spaces, ampersands, and other reserved characters, although array and nested-value conventions still depend on the API. For JSON, serialize the object yourself, choose enough ConvertTo-Json depth for the model, and set ContentType rather than relying on method-dependent defaults. PowerShell 7.4 changed the default request encoding to UTF-8 and makes ContentType take precedence if Headers also contains Content-Type, so specifying a charset produces clearer cross-version behavior.

Query and create resources with encoded values
$BaseUri = 'https://api.example.com/v1/widgets'
$Query = @{
    q    = 'blue widget'
    page = 2
}
$Matches = Invoke-RestMethod -Uri $BaseUri -Method Get -Body $Query

$Payload = @{
    name = 'blue widget'
    tags = @('new', 'fragile')
} | ConvertTo-Json -Depth 10
$Created = Invoke-RestMethod -Uri $BaseUri -Method Post `
    -Body $Payload -ContentType 'application/json; charset=utf-8'
Back to quick reference ↑
03

Keep credentials out of source, history, and diagnostics

Use Headers for ordinary negotiation metadata and a supported authentication parameter for secrets. In PowerShell 7, Authentication Bearer with Token accepts a SecureString, allowing an interactive prompt or secret provider to supply the token without a literal in the script. Do not print splatted parameters, sessions, headers, verbose traces, or exception bodies until they have been reviewed for tokens and personal data. Authentication and Token are unavailable in Windows PowerShell 5.1; if compatibility requires an Authorization header, retrieve its value at runtime from an approved secret store and keep it out of command-line arguments and logs. Credentials are rejected over plain HTTP by default in PowerShell 7.

Send a prompted bearer token over HTTPS
$Token = Read-Host 'API token' -AsSecureString
$Headers = @{
    Accept = 'application/json'
}
try {
    Invoke-RestMethod -Uri 'https://api.example.com/v1/profile' `
        -Authentication Bearer -Token $Token -Headers $Headers
}
finally {
    $Token.Dispose()
    Remove-Variable -Name Token
}

Note: For unattended automation, replace the prompt with an approved secret provider that returns a SecureString.

Back to quick reference ↑
04

Scope cookies to a deliberate session

SessionVariable creates a WebRequestSession in a variable whose name is supplied without a dollar sign. WebSession reuses that object on subsequent calls; the two parameters cannot be used together on one request. Sessions can contain authentication cookies, credentials, headers, proxy settings, and a user agent, so keep them in memory only as long as needed and never serialize them into diagnostics. Verify the destination before reusing a session, especially when an API returns an absolute next-page or redirect URI.

Capture and reuse a cookie session
$BaseUri = 'https://api.example.com'
Invoke-WebRequest -Uri "$BaseUri/session" -SessionVariable ApiSession | Out-Null
try {
    Invoke-RestMethod -Uri "$BaseUri/v1/profile" -WebSession $ApiSession
}
finally {
    Remove-Variable -Name ApiSession -ErrorAction SilentlyContinue
}
Back to quick reference ↑
05

Preserve server identity through TLS and proxies

HTTPS protects credentials only when certificate validation remains enabled. Do not normalize SkipCertificateCheck: it disables expiration, revocation, trust-chain, and hostname protections. Install the correct internal CA instead. Certificate accepts an X.509 object for mutual TLS; CertificateThumbprint is Windows-only. PowerShell 7 can use proxy environment variables or explicit Proxy and NoProxy parameters, while Windows PowerShell 5.1 behavior differs. Treat proxy credentials as secrets. Authorization is stripped on redirects by default in PowerShell 7; preserve it only after proving the redirect target is in the same trust boundary.

Use a reviewed client certificate and TLS policy
$Thumbprint = 'REPLACE_WITH_REVIEWED_THUMBPRINT'
$Certificate = Get-Item -LiteralPath "Cert:\CurrentUser\My\$Thumbprint"
Invoke-RestMethod -Uri 'https://api.example.com/v1/secure' `
    -Certificate $Certificate -SslProtocol Tls12
Back to quick reference ↑
06

Bound pagination and validate every next link

FollowRelLink understands Link response headers and MaximumFollowRelLink caps how many additional relation links are followed. APIs that return cursors or next URLs in the body need an explicit loop. Bound both the page count and collected data, detect repeated cursors, and reject a next URL whose scheme or authority (host and effective port) crosses the intended trust boundary before reusing credentials. ResponseHeadersVariable and StatusCodeVariable accept bare variable names and make rate-limit and pagination metadata available without replacing the body result.

Walk a body-based cursor with hard limits
$Origin = [uri]'https://api.example.com/'
$Next = [uri]'https://api.example.com/v1/widgets?limit=100'
$Seen = [System.Collections.Generic.HashSet[string]]::new()
for ($Page = 1; $Next -and $Page -le 20; $Page++) {
    if ($Next.Scheme -ne $Origin.Scheme -or $Next.Authority -ne $Origin.Authority) {
        throw "Rejected pagination target: $Next"
    }
    if (-not $Seen.Add($Next.AbsoluteUri)) { throw 'Pagination cycle detected' }
    $Result = Invoke-RestMethod -Uri $Next -Method Get
    $Result.items
    $Next = if ($Result.next) { [uri]::new($Origin, [string]$Result.next) } else { $null }
}
if ($Next) { throw 'Pagination exceeded 20 pages' }
Back to quick reference ↑
07

Bound waits, retries, and cancellation

MaximumRetryCount retries HTTP status codes from 400 through 599, plus 304, so it is broader than a transient-error-only policy. Use it primarily for idempotent reads or writes protected by a server-supported idempotency key; a retry can duplicate a POST after an ambiguous failure. For 429 responses with Retry-After, the cmdlet honors the server delay over RetryIntervalSec. In PowerShell 7.4+, ConnectionTimeoutSeconds bounds how long the request can remain pending, although DNS resolution can still take about 15 seconds, and OperationTimeoutSeconds bounds idle gaps between stream reads rather than total transfer duration. Earlier versions expose TimeoutSec without the separate operation timeout. The web cmdlets expose no CancellationToken parameter; Ctrl+C handles interactive cancellation, while automation can isolate a synchronous request in a job and stop it under an outer deadline.

Apply layered limits to an idempotent read
$Uri = 'https://api.example.com/v1/health'
$RequestJob = Start-Job -ScriptBlock {
    Invoke-RestMethod -Uri $using:Uri -Method Get `
        -ConnectionTimeoutSeconds 10 -OperationTimeoutSeconds 30 `
        -MaximumRetryCount 3 -RetryIntervalSec 2
}
try {
    Wait-Job -Job $RequestJob -Timeout 120 | Out-Null
    if ($RequestJob.State -notin @('Completed', 'Failed')) {
        Stop-Job -Job $RequestJob
        throw 'Request exceeded the 120-second workflow deadline'
    }
    Receive-Job -Job $RequestJob -ErrorAction Stop
}
finally {
    Remove-Job -Job $RequestJob -Force -ErrorAction SilentlyContinue
}
Back to quick reference ↑
08

Stage downloads and verify identity before use

OutFile writes bytes directly to a literal path and does not also return them unless PassThru is present. Download into a staging name, then compare SHA-256 with a value obtained through the publisher's trusted release channel before moving the file into place. Resume, available since PowerShell 6.1, is only a best-effort size comparison; it does not prove the local prefix belongs to the current remote object. A successful HTTPS transfer authenticates the connection, not the artifact's release identity.

Verify a staged download before publishing it
$Uri = 'https://example.com/releases/artifact.zip'
$Staging = Join-Path $env:TEMP 'artifact.zip.part'
$Destination = Join-Path $PWD 'artifact.zip'
$ExpectedSha256 = 'REPLACE_WITH_PUBLISHER_SHA256'
Invoke-WebRequest -Uri $Uri -OutFile $Staging
$ActualSha256 = (Get-FileHash -LiteralPath $Staging -Algorithm SHA256).Hash
if ($ActualSha256 -ne $ExpectedSha256) {
    Remove-Item -LiteralPath $Staging -Force
    throw "Artifact digest mismatch: $ActualSha256"
}
Move-Item -LiteralPath $Staging -Destination $Destination

Note: Obtain the expected digest independently from the publisher; do not trust a hash downloaded from the same unverified location.

Back to quick reference ↑
09

Separate HTTP status from transport failure

By default, non-success HTTP responses become terminating errors. PowerShell 7's SkipHttpErrorCheck instead writes the error response to the pipeline, and StatusCodeVariable makes an explicit status branch possible; it does not suppress DNS, TLS, timeout, or deserialization failures. For code that must also run on Windows PowerShell 5.1, use try/catch and inspect ErrorRecord. Exception response types differ between 5.1 and 7, and a connection failure may have no Response at all. Treat error bodies as untrusted and potentially sensitive before logging them.

Branch on HTTP status in PowerShell 7
$Body = Invoke-RestMethod -Uri 'https://api.example.com/v1/widgets/42' `
    -SkipHttpErrorCheck -StatusCodeVariable Status `
    -ResponseHeadersVariable ResponseHeaders
if ($Status -ge 400) {
    $RequestId = $ResponseHeaders['Request-Id']
    throw "API request failed with HTTP $Status; request ID $RequestId"
}
$Body
Catch failures across PowerShell generations
try {
    Invoke-RestMethod -Uri 'https://api.example.com/v1/widgets/42' -ErrorAction Stop
}
catch {
    $Status = if ($_.Exception.Response) {
        [int]$_.Exception.Response.StatusCode
    } else {
        $null
    }
    Write-Error "Request failed; HTTP status: $Status; category: $($_.CategoryInfo.Category)"
}
Back to quick reference ↑
10

Feature-test Windows PowerShell 5.1 and PowerShell 7

Windows PowerShell 5.1 uses legacy .NET web request types and Internet Explorer-based HTML parsing. After Microsoft's December 2025 security update, Invoke-WebRequest can prompt about script execution unless UseBasicParsing is supplied; use that switch for untrusted content. PowerShell 7 always uses basic HTML parsing, treats UseBasicParsing as a no-op, uses HttpClient response types, and has no ParsedHtml or Forms properties. Features such as Authentication, Token, SkipHttpErrorCheck, StatusCodeVariable, ResponseHeadersVariable, FollowRelLink, retries, Resume, and SslProtocol arrived after 5.1; the separate ConnectionTimeoutSeconds and OperationTimeoutSeconds parameters arrived in 7.4, with TimeoutSec retained as an alias for the former. Feature-test parameters when supporting multiple versions. Also, curl is an Invoke-WebRequest alias in 5.1 but not PowerShell 7; spell curl.exe when the native tool is intended.

Branch on an available web-cmdlet feature
$Command = Get-Command Invoke-RestMethod
$Parameters = @{
    Uri    = 'https://api.example.com/v1/health'
    Method = 'Get'
}
if ($Command.Parameters.ContainsKey('StatusCodeVariable')) {
    $Parameters.StatusCodeVariable = 'Status'
}
Invoke-RestMethod @Parameters
Name the native curl executable unambiguously
Get-Command curl -ErrorAction SilentlyContinue | Select-Object Name, CommandType
curl.exe --fail --output artifact.zip https://example.com/artifact.zip

Note: In Windows PowerShell 5.1, plain curl resolves to Invoke-WebRequest; PowerShell 7 does not define that alias.

Back to quick reference ↑

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. Microsoft LearnInvoke-RestMethodlearn.microsoft.com
  2. Microsoft LearnInvoke-WebRequestlearn.microsoft.com
  3. Microsoft LearnDifferences between Windows PowerShell 5.1 and PowerShell 7.xlearn.microsoft.com
  4. Microsoft LearnInvoke-WebRequest for Windows PowerShell 5.1learn.microsoft.com
  5. Microsoft Learncurl on Windowslearn.microsoft.com
  6. Microsoft LearnGet-FileHashlearn.microsoft.com
  7. Microsoft Learnabout_Splattinglearn.microsoft.com

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback