Command line interface for testing internet bandwidth using speedtest.net.
- Measure download speed, upload speed, and latency against speedtest.net.
- Filter to specific servers by ID, or exclude known-bad ones.
- Run against a self-hosted Speedtest Mini server.
- Bind to a specific source IP address.
- Machine-readable output: JSON and CSV, for logging, graphing, and scripting.
- Use as a library from Python to automate periodic speed tests.
- Secure by default: all communication with speedtest.net uses HTTPS, with optional hardened XML parsing via defusedxml.
- Python 3.9 or newer. Tested on CPython 3.9 through 3.13 and on PyPy3.
- Internet access to
speedtest.net. - Optional:
defusedxml. When installed, the XML configuration and server list responses are parsed throughdefusedxmlfor defence in depth. Without it,speedtest-clifalls back to the Python standard library'sxml.etree.ElementTreeparser.
No other third-party packages are required at runtime.
This is a hardened fork maintained at
pacnpal/speedtest-cli
with Python 3.9+ support, HTTPS by default, and optional defusedxml
hardening. It is not the build published to PyPI under the name
speedtest-cli — that is still upstream. Install directly from this
repository.
pip install git+https://github.com/pacnpal/speedtest-cli.git
or pin to a specific tag:
pip install git+https://github.com/pacnpal/speedtest-cli.git@<tag>
git clone https://github.com/pacnpal/speedtest-cli.git cd speedtest-cli pip install .
The module is also distributed as a single, dependency-free script.
Drop it anywhere on your PATH:
curl -Lo speedtest-cli https://raw.githubusercontent.com/pacnpal/speedtest-cli/master/speedtest.py chmod +x speedtest-cli
(Substitute wget -O for curl -Lo if you prefer.)
The script uses a shell/Python polyglot shebang, so it runs on systems
that only ship versioned interpreters (python3.13, python3.12,
…) instead of a plain python binary. It tries python3 first,
then python3.13 through python3.9, and finally python.
./speedtest-cli will therefore Just Work on modern distributions
that no longer ship /usr/bin/python.
speedtest-cli uses defusedxml for XML parsing when it is
available and falls back to the standard library parser otherwise. To
install the tool together with defusedxml in one step, use the
hardened extra:
pip install 'speedtest-cli[hardened] @ git+https://github.com/pacnpal/speedtest-cli.git'
or, if you already installed speedtest-cli from this repo, just:
pip install defusedxml
$ speedtest-cli -h
usage: speedtest-cli [-h] [--no-download] [--no-upload] [--single] [--bytes]
[--share] [--simple] [--csv]
[--csv-delimiter CSV_DELIMITER] [--csv-header] [--json]
[--list] [--server SERVER] [--exclude EXCLUDE]
[--mini MINI] [--source SOURCE] [--timeout TIMEOUT]
[--secure] [--no-secure] [--no-pre-allocate] [--version]
Command line interface for testing internet bandwidth using speedtest.net.
--------------------------------------------------------------------------
https://github.com/pacnpal/speedtest-cli
options:
-h, --help show this help message and exit
--no-download Do not perform download test
--no-upload Do not perform upload test
--single Only use a single connection instead of multiple. This
simulates a typical file transfer.
--bytes Display values in bytes instead of bits. Does not
affect the image generated by --share, nor output from
--json or --csv
--share Generate and provide a URL to the speedtest.net share
results image, not displayed with --csv
--simple Suppress verbose output, only show basic information
--csv Suppress verbose output, only show basic information
in CSV format. Speeds listed in bit/s and not affected
by --bytes
--csv-delimiter CSV_DELIMITER
Single character delimiter to use in CSV output.
Default ","
--csv-header Print CSV headers
--json Suppress verbose output, only show basic information
in JSON format. Speeds listed in bit/s and not
affected by --bytes
--list Display a list of speedtest.net servers sorted by
distance
--server SERVER Specify a server ID to test against. Can be supplied
multiple times
--exclude EXCLUDE Exclude a server from selection. Can be supplied
multiple times
--mini MINI URL of the Speedtest Mini server
--source SOURCE Source IP address to bind to
--timeout TIMEOUT HTTP timeout in seconds. Default 10
--secure Use HTTPS when communicating with speedtest.net
operated servers (default, kept for backward
compatibility)
--no-secure Use HTTP instead of HTTPS. Not recommended —
speedtest.net may reject plain HTTP requests and
results can be tampered with in transit
--no-pre-allocate Do not pre allocate upload data. Pre allocation is
enabled by default to improve upload performance. To
support systems with insufficient memory, use this
option to avoid a MemoryError
--version Show the version number and exit
Run a normal test and print the full human-readable output:
speedtest-cli
Concise one-line summary:
speedtest-cli --simple
Emit JSON for a logging pipeline:
speedtest-cli --json
Emit CSV with a custom delimiter:
speedtest-cli --csv-header speedtest-cli --csv --csv-delimiter ';'
Test against a specific server (by ID):
speedtest-cli --server 1234
List available servers sorted by distance:
speedtest-cli --list
Run against a local Speedtest Mini instance:
speedtest-cli --mini http://speedtest.local/mini
Bind outgoing connections to a specific interface address:
speedtest-cli --source 192.168.1.100
The CLI is a thin wrapper around the speedtest module. The same
functionality is available to import directly:
import speedtest
s = speedtest.Speedtest()
s.get_best_server()
s.download()
s.upload()
results = s.results.dict()
print(f"Download: {results['download'] / 1_000_000:.2f} Mbit/s")
print(f"Upload: {results['upload'] / 1_000_000:.2f} Mbit/s")
print(f"Ping: {results['ping']:.2f} ms")Pick a specific server, then run the test:
s = speedtest.Speedtest() s.get_servers(servers=[1234]) # filter to one server ID s.get_best_server() s.download() s.upload() print(s.results.json())
Useful constructor arguments:
secure(defaultTrue) — use HTTPS for all speedtest.net traffic. PassFalseonly if you have a specific need for HTTP; speedtest.net increasingly rejects plain-HTTP requests.source_address— an IPv4/IPv6 string to bind outgoing sockets to (equivalent to the--sourceCLI flag).timeout— HTTP timeout in seconds (default10).shutdown_event— athreading.Eventthe caller sets to stop in-flight download/upload threads early.
The SpeedtestResults object exposed as s.results provides
dict(), json(pretty=False), csv(delimiter=','), and
share() methods. share() POSTs the run to speedtest.net and
returns a https://www.speedtest.net/result/<id>.png URL.
HTTPS by default. All connections to speedtest.net-operated hosts
(config, server list, latency probes, download/upload endpoints, share
API) use HTTPS. Earlier versions defaulted to plaintext HTTP, which
leaked the client's IP and ISP in the clear and let a network attacker
tamper with the ping target or swap the chosen test server. The
--secure flag is retained as a backward-compatible no-op; the
--no-secure flag opts out and is not recommended — speedtest.net
may refuse plain-HTTP requests and results can no longer be trusted
against an on-path attacker.
Hardened XML parsing. If defusedxml is installed,
speedtest-cli routes XML parsing through it to reject DTDs,
external entity references, and entity-expansion bombs before they
reach the standard-library parser. Without defusedxml the tool
falls back to xml.etree.ElementTree.fromstring, which is already
safe in modern Python for the kinds of documents speedtest.net
returns, but defusedxml adds an extra belt-and-suspenders layer.
Certificate verification. TLS connections use the system trust
store via Python's ssl.create_default_context() with hostname
verification enabled. There is no fallback to unverified TLS.
It is not a goal of this application to be a reliable latency reporting tool.
Latency reported by this tool should not be relied on as a value indicative of ICMP-style latency. It is a relative value used for determining the lowest-latency server for performing the actual speed test against.
There is the potential for this tool to report results inconsistent with Speedtest.net. Several factors contribute to the potential inconsistency:
- Speedtest.net has migrated to using pure socket tests instead of HTTP-based tests.
- This application is written in Python.
- Different versions of Python will execute certain parts of the code faster than others.
- CPU and memory capacity and speed will play a large part in inconsistency between speedtest.net and even other machines on the same network.
Issues relating to inconsistencies will be closed as wontfix
without additional reason or context.
Clone the repository and run the test suite via tox:
git clone https://github.com/pacnpal/speedtest-cli.git cd speedtest-cli pip install tox tox # run against all available Python versions tox -e py312 # a specific CPython version tox -e pypy3 # PyPy3 tox -e flake8 # lint only
The default tox environment byte-compiles the module, runs a live
speed test against speedtest.net, and executes
tests/scripts/source.py (a portable smoke test of the
--source error path). See tox.ini and CONTRIBUTING.md for
details.
Apache License, Version 2.0. See LICENSE for the full text.