📦 Speedkit
Python SDK for network speed testing
Measures download, upload, and latency with the official Ookla Speedtest CLI and falls back to LibreSpeed when Speedtest is blocked or unavailable.
Features
- Zero setup — Ookla Speedtest first, LibreSpeed when it is blocked or fails.
- Automatic binaries — downloaded and cached for your OS on first run.
- Nearby servers — the lowest-latency server is picked automatically.
- Self-recovery — failed attempts are retried, broken servers excluded.
- Uniform results — same fields and units whichever provider ran; client info from geo-IP.
Installation
pip install speedkit
Usage
from speedkit import Speedkit
kit = Speedkit()
result = kit.run()
print(f"download: {result.download / 1_000_000:.1f} Mbit/s")
print(f"upload: {result.upload / 1_000_000:.1f} Mbit/s")
print(f"ping: {result.ping:.1f} ms")
data = result.to_dict() # plain JSON-serializable dict
Or from the command line:
speedkit # auto: Ookla, then LibreSpeed
speedkit -p librespeed # pin one provider
speedkit -t 180 # time budget per provider in seconds
speedkit -a 1 # disable retries
speedkit --no-geoip # no third-party geo-IP request
Speedkit() needs no configuration; everything it accepts:
kit = Speedkit(
provider="auto", # "auto" | "ookla" | "librespeed" — a pinned provider never falls back
timeout=120, # time budget per provider in seconds, covering all attempts
attempts=2, # measurement attempts within the budget; 1 disables retries
lookup_geoip=True, # resolve client info from a geo-IP service; False makes no such request
cache_dir=None, # where CLI binaries are cached; None = user cache directory
)
Result format
SpeedtestResult.to_dict() returns download/upload in bits per second and ping in milliseconds:
{
"client": {
"ip": "203.0.113.7",
"isp": "Example ISP",
"country": "ZZ",
"city": "Springfield"
},
"server": {
"url": "",
"name": "Springfield",
"country": "Freedonia",
"sponsor": "Example Sponsor",
"id": "1234",
"host": "speedtest.example.net:8080",
"latency": 0.681
},
"provider": "ookla",
"download": 293111520.0,
"upload": 292265640.0,
"ping": 0.681,
"timestamp": "2026-07-19T15:22:48Z",
"bytes_sent": 418465064,
"bytes_received": 426981996
}
client— from a geo-IP service, identical whichever provider measured.server— the test server that provider picked; unreported fields stay at""/0.0.provider— which measurer produced the result.
Geo-IP lookup
Client details come from the first service that answers: ipinfo.io → ipwho.is.
- The lookup takes a fraction of a second.
- If every service is unreachable, the measurement is still returned — with whatever client info the CLI reported.
This sends your IP address to the geo-IP service that answers.
Pass lookup_geoip=False (or --no-geoip) to skip it entirely. Nothing is then sent to a third
party, and client keeps what the measuring CLI itself reported — ip and isp from
Ookla, plus country from LibreSpeed. Fields neither reports, city above all, stay at
"", so client is no longer identical across providers.
Binaries
| Provider | Version | Platforms |
|---|---|---|
| Ookla Speedtest CLI | 1.2.0 | Linux x86_64/aarch64, macOS |
| librespeed-cli | 1.0.13 | Linux x86_64/aarch64, macOS |
Binaries are downloaded on first use and cached per platform:
- Linux —
~/.cache/speedkit - macOS —
~/Library/Caches/speedkit
Every archive is checked against a SHA-256 digest pinned in speedkit/binaries.py before
anything is extracted or made executable. A release asset that changed after it was pinned
raises BinaryDownloadError instead of running. All network access is HTTPS with certificate
verification; plain HTTP, and any redirect leaving TLS, is refused.
Set SPEEDKIT_OOKLA_BINARY / SPEEDKIT_LIBRESPEED_BINARY to a path of a preinstalled
binary to skip downloading entirely — useful for offline machines and locked-down networks.
The path is executed as given and bypasses the checksum, so it is on you to trust it.
To pre-download the binaries at image build time (Docker, CI):
python -c "from speedkit.binaries import ookla_binary, librespeed_binary; ookla_binary(); librespeed_binary()"
Running the Ookla provider passes
--accept-license --accept-gdpr, which implies acceptance of the Ookla EULA and privacy terms.
Errors
Every error derives from SpeedkitError and carries an actionable hint:
from speedkit import Speedkit, SpeedkitError
try:
result = Speedkit().run()
except SpeedkitError as error:
print(error) # cause, and a hint on how to fix it
UnsupportedPlatformError — no prebuilt binary for this OS/arch; BinaryDownloadError —
the binary could not be fetched; SpeedtestError — the measurement itself failed.
License
This repository is distributed under the MIT License.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file speedkit-0.2.0.tar.gz.
File metadata
- Download URL: speedkit-0.2.0.tar.gz
- Upload date:
- Size: 23.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f6714e5a63b5d76f2828ac59ae77474108f2e4fa3527249ac3e4e6cfe0a5e02f
|
|
| MD5 |
eb30efda8085ddac6534bf4b39f5d8bb
|
|
| BLAKE2b-256 |
04beafe1dee1f3ed32c70c8d8ca6b8e467fb2aeb177dd19b6af8d6557f23ddc8
|
File details
Details for the file speedkit-0.2.0-py3-none-any.whl.
File metadata
- Download URL: speedkit-0.2.0-py3-none-any.whl
- Upload date:
- Size: 18.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9954b53e235916e80979abbd51639c47c0d6064ba0af2c7a3b5b1ff260a9494
|
|
| MD5 |
b3855397d75eb13345ce5b974c10444b
|
|
| BLAKE2b-256 |
15c366463b978af54bb53f3a2a49d6063d8d39fcf86ce6138c42c39c94bd7c54
|