PowerShell Invoke-WebRequest: Download Files and Call APIs

Downloading files, checking web service health, and calling HTTP APIs are tasks that IT scripts need constantly — and PowerShell Invoke-WebRequest handles all of them without any external dependencies. It makes HTTP requests, receives responses, and gives you full access to the response body, headers, and status code. This guide covers GET requests, file downloads, custom headers, POST requests, authentication, and HTML response parsing.
Quick Answer / TL;DR
# Download a file
Invoke-WebRequest -Uri 'https://example.com/file.zip' -OutFile 'C:\Downloads\file.zip'
# GET a JSON API
$response = Invoke-WebRequest -Uri 'https://api.example.com/status'
$data = $response.Content | ConvertFrom-Json
Basic GET Request
The simplest usage — make an HTTP GET request and examine the response:
# Make a GET request
$response = Invoke-WebRequest -Uri 'https://httpbin.org/get'
# Examine the response
$response.StatusCode # 200
$response.StatusDescription # OK
$response.Headers # Response headers hashtable
$response.Content # Response body as a string
200
OK
The response object has many properties. The most important: StatusCode (HTTP status number), Content (body as string), and RawContent (full HTTP response including headers). For JSON APIs, use Invoke-RestMethod which auto-parses JSON — Invoke-WebRequest is better when you need access to response headers, cookies, or raw HTML.
Downloading a File to Disk
Use -OutFile to save the response body directly to a file:
# Download a file
$url = 'https://github.com/PowerShell/PowerShell/releases/download/v7.4.1/PowerShell-7.4.1-win-x64.msi'
$savePath = 'C:\Downloads\PowerShell-7.4.1.msi'
Invoke-WebRequest -Uri $url -OutFile $savePath
# Verify download
$file = Get-Item $savePath
'Downloaded: {0} ({1:N2} MB)' -f $file.Name, ($file.Length/1MB)
# Download with progress bar (automatic in interactive sessions)
Invoke-WebRequest -Uri $url -OutFile $savePath
Downloaded: PowerShell-7.4.1.msi (104.83 MB)
For large file downloads, PowerShell 7 streams the response to disk rather than loading it into memory first. In Windows PowerShell 5.1, the entire response is buffered in memory, which can cause issues for very large files.
Sending Custom Headers
Pass a hashtable to -Headers to add custom HTTP headers:
# Add custom headers
$headers = @{
'Accept' = 'application/json'
'X-Api-Key' = 'your-api-key-here'
'X-Request-Id' = [Guid]::NewGuid().ToString()
}
$response = Invoke-WebRequest `
-Uri 'https://api.example.com/data' `
-Headers $headers
$response.Content | ConvertFrom-Json
# Bearer token authentication
$token = 'eyJhbGciOiJIUzI1NiJ9...'
$response = Invoke-WebRequest -Uri 'https://api.example.com/protected' `
-Headers @{ 'Authorization' = "Bearer $token" }
POST Request with Body
Send data to an endpoint with a POST request:
# POST with JSON body
$body = @{
Username = 'admin'
Password = 'secret'
} | ConvertTo-Json
$response = Invoke-WebRequest `
-Uri 'https://api.example.com/auth/login' `
-Method POST `
-Body $body `
-ContentType 'application/json'
$response.StatusCode
$token = ($response.Content | ConvertFrom-Json).token
# POST form data
$formData = @{
name = 'Alice'
email = '[email protected]'
}
Invoke-WebRequest `
-Uri 'https://example.com/form' `
-Method POST `
-Body $formData
200
Authentication with Credentials
# Basic authentication with PSCredential
$cred = Get-Credential # Interactive prompt
Invoke-WebRequest `
-Uri 'https://internal-api.corp.local/api/data' `
-Credential $cred
# Windows integrated authentication (Kerberos/NTLM)
Invoke-WebRequest `
-Uri 'https://internal-server.corp.local/report' `
-UseDefaultCredentials
# Basic auth without interactive prompt
$username = 'apiuser'
$password = 'apipassword'
$base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${username}:${password}"))
Invoke-WebRequest -Uri 'https://api.example.com/data' `
-Headers @{ 'Authorization' = "Basic $base64" }
Parsing the HTML Response
For web scraping, Invoke-WebRequest parses HTML and exposes DOM elements (Windows PowerShell only — requires IE engine):
# Windows PowerShell 5.1 — parsed HTML response
$page = Invoke-WebRequest -Uri 'https://example.com'
# Access parsed elements
$page.Links | Select-Object href, innerText
$page.Forms[0].Fields
# PowerShell 7+ — use regex or HtmlAgilityPack for HTML parsing
$page = Invoke-WebRequest -Uri 'https://example.com'
$page.Content | Select-String -Pattern 'href="([^"]+)"' -AllMatches |
ForEach-Object { $_.Matches.Groups[1].Value }
Common Errors and Fixes
-
TLS 1.2 not default on older PowerShell — set ServicePointManager: Windows PowerShell 5.1 defaults to TLS 1.0/1.1 which many APIs no longer accept. Fix:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12at the top of your script. PowerShell 7+ uses TLS 1.2 and 1.3 by default. -
Response is object not string — use .Content for body text:
$response = Invoke-WebRequest -Uri $urlreturns aWebResponseObject. Assigning this to a variable and trying to use it as a string won’t work. Use$response.Contentfor the body text, or$response.Content | ConvertFrom-Jsonfor JSON.
Related Cmdlets / See Also
Wrapping Up
Invoke-WebRequest makes HTTP requests from PowerShell with full control over method, headers, body, and authentication. Use -OutFile for downloads, -Headers for API keys and tokens, and -Method POST with -Body for submitting data. For JSON APIs specifically, Invoke-RestMethod auto-parses the response and is often simpler. Your next step: test an API endpoint you use frequently by calling it with Invoke-WebRequest and exploring the response object.


