Internet speed tests in Windows from CMD and PowerShell

Testing a connection through a browser means loading a page, waiting for ads and reading a gauge that rounds everything. On a server with no browser it is not an option at all.

Speedtest CLI is Ookla’s official command line client. It reports the same measurement as the web test, in a form you can log, schedule and compare.

What follows is the install, a single test, pinning a specific server so results are comparable, and turning the whole thing into a scheduled measurement in PowerShell.

Step 1: Download Speedtest CLI for Windows

Start by downloading the CLI tool from the official site:

Download Speedtest CLI (Windows)

Unzip the archive. Inside is speedtest.exe, a single self-contained binary with no installer. Put it somewhere permanent, for example C:\Tools\Speedtest.

Add that folder to the system PATH so the command works from any directory.

Step 2: Run Your First Speed Test

Open Command Prompt or PowerShell, and type:

C:\Tools\Speedtest\speedtest.exe
Speedtest CLI running in the Windows Command Prompt

You’ll see:

  • Your ISP
  • Server location
  • Ping (latency)
  • Download speed
  • Upload speed
  • A shareable Result URL

The first run asks you to accept Ookla’s licence and GDPR terms. In a script, pass --accept-license and --accept-gdpr so it does not stop and wait for input.

Want to Pick a Specific Server?

By default the client picks the nearest server. To compare like with like over time, pin one server explicitly:

1. List Nearby Servers:

speedtest.exe --servers

2. Run the Test with a Specific Server ID:

speedtest.exe --server-id 12345

That matters when you benchmark against a specific datacentre or ISP node. A different server on every run produces numbers that cannot be compared.

Here’re the switches you can use with speedtest.exe

Switch Description
-h, --helpShow help information
-vIncrease verbosity (use multiple times like -vvv)
-V, --versionPrint version number
--accept-licenseAccept the license agreement (needed for automation)
--accept-gdprAccept GDPR agreement (required in the EU)
--servers, -LList nearby servers
--server-id=<id>, -sUse a specific server by ID
--host=<hostname>, -oUse server by hostname
--selection-detailsShow server selection information
--format=jsonOutput in compact JSON
--format=json-prettyOutput in pretty-printed JSON
--format=csv, tsv, jsonlOutput in various machine-readable formats
--output-headerInclude header row for CSV/TSV
--unit=MB/s, Mbps, MiB/s, etc.Customize output unit (for human-readable only)
--precision=<n>Set decimal precision (default: 2)
--interface=<name>, -IBind to a specific network interface
--ip=<address>, -iBind to a specific local IP address
--ca-certificate=<path>Use custom CA certificate (advanced use case)
--progress=yes|no, -pEnable/disable the progress bar
--progress-update-interval=<ms>Set interval (100–1000 ms) for progress updates

Basic PowerShell Automation

Running the same command by hand every hour is not a measurement. This is where PowerShell takes over.

A minimal script that runs one test and appends the result to a log:

# Define full path to speedtest.exe (change this if needed)
$speedtestPath = "C:\Tools\Speedtest\speedtest.exe"

# Check if speedtest.exe exists
if (-Not (Test-Path $speedtestPath)) {
    Write-Error "speedtest.exe not found at $speedtestPath. Please verify the path."
    exit
}

# Run the speed test and parse JSON output
try {
    $output = &amp; $speedtestPath --accept-license --accept-gdpr --format json | ConvertFrom-Json
}
catch {
    Write-Error "Failed to execute speedtest. Error: $_"
    exit
}

# Create a structured log object
$log = [PSCustomObject]@{
    Timestamp = Get-Date
    Ping      = $output.ping.latency
    Download  = [math]::Round($output.download.bandwidth / 125000, 2)  # Mbps
    Upload    = [math]::Round($output.upload.bandwidth / 125000, 2)    # Mbps
    Server    = $output.server.name
    ISP       = $output.isp
}

# Display results in console
Write-Host "`n=== Speedtest Results ===" -ForegroundColor Cyan
$log | Format-List

# OPTIONAL: Save results to CSV (uncomment the next line to enable logging)
# $log | Export-Csv -Path "C:\Tools\Speedtest\speedtest-log.csv" -Append -NoTypeInformation

Note: dividing bandwidth by 125000 converts to Mbps. The API reports bytes per second, and 1 Mbps is 125,000 bytes per second.

A more complete script

A longer version that keeps the server name and the jitter as well:

$speedtestcmd = &amp; "C:\Tools\Speedtest\speedtest.exe" --accept-license -s 37149 -f json
$speedtestresult = $speedtestcmd | ConvertFrom-Json
$result = [PSCustomObject]@{
    Timestamp     = Get-Date
    ISP           = $speedtestresult.isp
    Server        = $speedtestresult.server.name
    Location      = "$($speedtestresult.server.location), $($speedtestresult.server.country)"
    PingLatencyMs = $speedtestresult.ping.latency
    JitterMs      = $speedtestresult.ping.jitter
    PacketLoss    = if ($speedtestresult.packetLoss) { "$($speedtestresult.packetLoss * 100)%" } else { "N/A" }
    DownloadMbps  = [math]::Round($speedtestresult.download.bandwidth / 1MB * 8, 2)
    UploadMbps    = [math]::Round($speedtestresult.upload.bandwidth / 1MB * 8, 2)
    ResultURL     = $speedtestresult.result.url
}
$result
Speedtest CLI results showing ping, download and upload speed

Alerts and toast notifications

The same result can raise an alert when throughput drops below what the line is supposed to deliver:

if ($result.DownloadMbps -lt 100) {
    Write-Warning "Your download speed is under 100 Mbps!"
}

Or a desktop notification:

[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
$toastXml = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)
$toastXml.GetElementsByTagName("text")[0].AppendChild($toastXml.CreateTextNode("Speedtest Results"))
$toastXml.GetElementsByTagName("text")[1].AppendChild($toastXml.CreateTextNode("Download: $($result.DownloadMbps) Mbps, Upload: $($result.UploadMbps) Mbps"))
$toast = [Windows.UI.Notifications.ToastNotification]::new($toastXml)
$notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Speedtest CLI")
$notifier.Show($toast)

The notification fires from the script itself, so the alert arrives whether or not anyone is watching the console.

What Are Toast Notifications?

Toast notifications are the small pop-ups that appear in the bottom-right corner of your screen (like when Outlook says “You’ve got mail”). PowerShell can trigger these using Windows Runtime APIs — yes, you can be fancy now.

Full Script with Toast Notification & Comments

# ========================================
# Speedtest CLI with Toast Notification
# ========================================

# Path to speedtest.exe (adjust as needed)
$speedtestPath = "C:\Tools\Speedtest\speedtest.exe"

# Ensure speedtest.exe exists
if (-Not (Test-Path $speedtestPath)) {
    Write-Error "speedtest.exe not found at: $speedtestPath"
    exit
}

# Run speedtest with license/GDPR acceptance and JSON output
try {
    $output = &amp; $speedtestPath --accept-license --accept-gdpr --format json | ConvertFrom-Json
}
catch {
    Write-Error "Error while running speedtest: $_"
    exit
}

# Extract and convert results into a friendly object
$result = [PSCustomObject]@{
    Timestamp     = Get-Date
    DownloadMbps  = [math]::Round($output.download.bandwidth / 125000, 2)
    UploadMbps    = [math]::Round($output.upload.bandwidth / 125000, 2)
    PingLatencyMs = $output.ping.latency
    ISP           = $output.isp
    Server        = $output.server.name
}

# Display results in console
Write-Host "`n=== Speedtest Results ===" -ForegroundColor Cyan
$result | Format-List

# ========================================
# Toast Notification Section
# ========================================

# Load Windows Runtime namespace for notifications
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null

# Use a two-line text template
$toastTemplate = [Windows.UI.Notifications.ToastTemplateType]::ToastText02
$toastXml = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent($toastTemplate)

# Set the notification's text content safely
$textNodes = $toastXml.GetElementsByTagName("text")
if ($textNodes.Count -ge 2) {
    $textNodes.Item(0).InnerText = "Speedtest Results"
    $textNodes.Item(1).InnerText = "Download: $($result.DownloadMbps) Mbps, Upload: $($result.UploadMbps) Mbps"
}
else {
    Write-Warning "Could not retrieve expected &lt;text> elements from toast template."
    exit
}

# Create the toast notification object
$toast = [Windows.UI.Notifications.ToastNotification]::new($toastXml)

# Show the notification using a named toast notifier
$notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Speedtest CLI")
$notifier.Show($toast)

What This Script Does:

  1. Runs Speedtest CLI
    Executes a test and captures bandwidth, ping, ISP, etc.
  2. Displays the results in PowerShell
    Easy to check manually in the terminal
  3. Sends a toast notification
    With download & upload speed info, so you can see it even if the terminal is minimized
Windows toast notification reporting the speed test result

Schedule regular tests with Task Scheduler

  1. Open Task Scheduler
  2. Create a new task
  3. Set a trigger (e.g., every hour)
  4. Action: Start a program → powershell.exe
  5. Add argument: -ExecutionPolicy Bypass -File "C:\Scripts\SpeedTest.ps1"

With this in place the machine measures itself on a schedule and reports a degraded line before a user does. The schtasks guide covers creating and auditing that task from the command line.

The full option reference

The archive also contains speedtest.md, a full command line reference listing options the built-in help does not mention:

  • Use custom units (MB/s, GiB/s, kibps, etc.)
  • Output formats like csv, tsv, json-pretty, and jsonl
  • Adjust decimal precision with –precision
  • Bind tests to a specific network interface or IP
  • Control progress display and verbosity
  • Handle exit codes and errors with style

Those options are the starting point for feeding results into monitoring: CSV and JSONL output, integration with Zabbix or Prometheus, or posting to a dashboard.

Summary

With speedtest.exe and a short PowerShell wrapper you have:

  • A lightweight, fast, ad-free way to test internet speed
  • Logging and historical monitoring
  • Custom scripts with alerts, notifications, and flexible output
  • A professional way to say, “It’s not me, it’s the ISP.”

None of it needs a browser, which is what makes it usable on a server.


Related tools


Related guides