PowerShell Invoke-WebRequest: Download Files and Call APIs

PowerShell Invoke-WebRequest: Download Files and Call APIs

PowerShell Tips Editor 3 min read
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]::Tls12 at 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 $url returns a WebResponseObject. Assigning this to a variable and trying to use it as a string won’t work. Use $response.Content for the body text, or $response.Content | ConvertFrom-Json for 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.

Send-Item -To