PowerShell Invoke-RestMethod: Consume REST APIs Easily

Every modern cloud service, monitoring platform, and infrastructure tool exposes a REST API. PowerShell Invoke-RestMethod is the cleanest way to consume them — it automatically deserializes JSON responses into PowerShell objects you can immediately filter, sort, and pipeline. No manual ConvertFrom-Json, no response object unwrapping. This guide covers GET, POST, PUT, DELETE, authentication, pagination, and error handling for real-world API integrations.
Quick Answer / TL;DR
# GET request — response auto-parsed as object
$data = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell'
$data.stargazers_count # Access properties directly
Basic GET with Auto JSON Parse
The key advantage over Invoke-WebRequest: the JSON response is automatically converted to a PowerShell object:
# Auto-parsed JSON response
$repo = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell'
$repo.name
$repo.stargazers_count
$repo.language
# Array response
$releases = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases'
$releases.Count
$releases[0].tag_name
# Filter the array immediately
$releases | Where-Object { $_.prerelease -eq $false } |
Select-Object tag_name, published_at |
Select-Object -First 5
PowerShell
30000
C#
10
v7.4.1
tag_name published_at
-------- ------------
v7.4.1 2024-01-18T00:00:00Z
v7.3.9 2023-11-13T00:00:00Z
POST Request with JSON Body
Build your request body as a hashtable and convert it to JSON:
# POST with JSON body
$body = @{
title = 'New Issue'
body = 'Please fix this bug'
labels = @('bug', 'priority-high')
} | ConvertTo-Json -Depth 5
$response = Invoke-RestMethod `
-Uri 'https://api.github.com/repos/owner/repo/issues' `
-Method POST `
-Body $body `
-ContentType 'application/json' `
-Headers @{ 'Authorization' = "Bearer $token" }
$response.number # Issue number assigned by GitHub
$response.html_url # URL to the new issue
42
https://github.com/owner/repo/issues/42
Always pass the body as a string (not a hashtable) to -Body — use ConvertTo-Json first. Also set -ContentType 'application/json' to tell the API what format to expect.
Setting Authorization Header
Most APIs require authentication. The most common patterns:
# Bearer token (most common for modern APIs)
$token = $env:API_TOKEN # Load from environment variable
$headers = @{
'Authorization' = "Bearer $token"
'Accept' = 'application/json'
}
$data = Invoke-RestMethod -Uri 'https://api.example.com/data' -Headers $headers
# API key in header
Invoke-RestMethod -Uri 'https://api.example.com/endpoint' `
-Headers @{ 'X-API-Key' = 'your-key-here' }
# Basic authentication
$username = 'user'
$password = 'pass'
$base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${username}:${password}"))
Invoke-RestMethod -Uri 'https://api.example.com/data' `
-Headers @{ 'Authorization' = "Basic $base64" }
PUT and DELETE Requests
# PUT — update an existing resource
$update = @{ status = 'resolved'; resolution = 'Fixed in v2.1' } | ConvertTo-Json
Invoke-RestMethod `
-Uri 'https://api.example.com/issues/42' `
-Method PUT `
-Body $update `
-ContentType 'application/json' `
-Headers @{ 'Authorization' = "Bearer $token" }
# PATCH — partial update
$patch = @{ title = 'Updated title' } | ConvertTo-Json
Invoke-RestMethod `
-Uri 'https://api.example.com/items/123' `
-Method PATCH `
-Body $patch `
-ContentType 'application/json' `
-Headers $headers
# DELETE
Invoke-RestMethod `
-Uri 'https://api.example.com/items/123' `
-Method DELETE `
-Headers $headers
Handling Pagination
Many APIs return paginated results. Loop through pages until no more data is returned:
# Page-by-page collection
$allResults = [System.Collections.Generic.List[object]]::new()
$page = 1
$perPage = 100
do {
$url = "https://api.example.com/items?page=$page&per_page=$perPage"
$data = Invoke-RestMethod -Uri $url -Headers $headers
if ($data.Count -eq 0) { break }
$allResults.AddRange($data)
$page++
} while ($data.Count -eq $perPage)
Write-Output "Total items: $($allResults.Count)"
# Link header pagination (common in GitHub API)
$url = 'https://api.github.com/orgs/PowerShell/members?per_page=100'
do {
$response = Invoke-WebRequest -Uri $url -Headers $headers
($response.Content | ConvertFrom-Json) | ForEach-Object { $allResults.Add($_) }
$linkHeader = $response.Headers['Link']
$url = if ($linkHeader -match '<([^>]+)>; rel="next"') { $Matches[1] } else { $null }
} while ($url)
Error Handling for 4xx and 5xx
HTTP errors throw terminating errors in Invoke-RestMethod:
# Handle HTTP errors
try {
$data = Invoke-RestMethod -Uri 'https://api.example.com/notfound' -ErrorAction Stop
} catch {
$statusCode = $_.Exception.Response.StatusCode.value__
$body = $_.ErrorDetails.Message
switch ($statusCode) {
401 { Write-Error 'Authentication failed — check your token' }
403 { Write-Error 'Authorization failed — insufficient permissions' }
404 { Write-Warning "Resource not found: $url" }
429 { Write-Warning 'Rate limited — wait before retrying' }
500 { Write-Error "Server error — retry may succeed: $body" }
default { Write-Error "HTTP $statusCode : $body" }
}
}
WARNING: Resource not found: https://api.example.com/notfound
Common Errors and Fixes
-
Body must be a string — convert hashtable with ConvertTo-Json first: Passing a hashtable directly to
-Bodysends it as form-encoded data, not JSON. Always convert first:-Body ($data | ConvertTo-Json). Set-ContentType 'application/json'to ensure the API interprets it correctly. -
401 errors with bearer token — Authorization header format matters: The exact format is
"Bearer TOKEN"(capital B, space, then the token). Missing the space, using lowercase, or omitting “Bearer” all cause 401 errors. Double-check with$headers['Authorization']before debugging further.
Related Cmdlets / See Also
Wrapping Up
Invoke-RestMethod is the premier tool for REST API consumption in PowerShell — it auto-parses JSON into objects, handles all HTTP methods, and integrates naturally with the pipeline. Always use ConvertTo-Json for request bodies, set -ContentType 'application/json', and wrap calls in try/catch for HTTP error handling. For paginated APIs, build a do/while loop that collects all pages. Your next step: pick an API you use manually today and automate it with Invoke-RestMethod.


