📦 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": {
"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.
A path that is missing or not executable is refused by name; anything runnable 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.
Metadata
Release files for speedkit 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| speedkit-0.2.2.tar.gz | 25.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| speedkit-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 44.9 kB
Release files / speedkit-0.2.2.tar.gz
| Download URL | speedkit-0.2.2.tar.gz |
|---|---|
| Size | 25.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
429533c62100ddf519d0fb64201b611931dd7e5fe943718dd35eef32d4b1ceb6
|
|
BLAKE2b-256 checksum How to use checksums |
268150e91bdf24b032942de7c135471d5389ba281abbc901a04a94908e06c453
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Release files / speedkit-0.2.2-py3-none-any.whl
| Download URL | speedkit-0.2.2-py3-none-any.whl |
|---|---|
| Size | 19.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2df650633fda737d7b5a932a5cd9797c98b358a5b4db0d1cd1e5bd83515c4994
|
|
BLAKE2b-256 checksum How to use checksums |
b661e523c244a5d1b7940fd1a69e31802ca341c7a789b59aecd7ab92d976a66e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Security for speedkit 0.2.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.