PowerShell Invoke-RestMethod: Consume REST APIs Easily

PowerShell Invoke-RestMethod: Consume REST APIs Easily

PowerShell Tips Editor 3 min read
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 -Body sends 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.

Send-Item -To