K9LOAD
DOCS →
✦ exit code 99, like k6
● coordinated omission: corrected
RUST × PYTHON Python describes, Rust does the work

Load testing with k6's UX, a Rust engine and a Python API.

Write tests in Python or YAML, with the executors, checks, thresholds and metric names you know from k6. The part that sends requests and measures time is Rust, so Python only describes the work.

Declarative plans 1.4–1.5×

★ k6's requests/s

One Python process 3–4×

Locust's single process

k6 executors All 7

Arrival rates included

License MIT/Apache

Dual-licensed, your pick

Measured on one machine (i9-13900K, Linux under WSL2). How we measured →

⚡ one engine, four ways in

Write it like k6. Run it on Rust.

script.py
import k9

options = {
    "vus": 50,
    "duration": "1m",
    "thresholds": {"http_req_duration": ["p(95)<300"]},
}

async def default():
    res = await k9.http.get("https://test.k6.io/")
    k9.check(res, {"status is 200": lambda r: r.status == 200})
k9 run · the summary you know
running (00m10.1s), 00/10 VUs, 990 complete and 0 interrupted iterations

     data_received......: 129 kB 13 kB/s
     data_sent..........: 70 kB  7.0 kB/s
     http_req_duration..: avg=101.88ms med=101.79ms p(95)=102.62ms
     http_req_failed....: 0.00%  ✓ 0    ✗ 990
     http_reqs..........: 990    98.100557/s
     iteration_duration.: avg=101.9ms  p(95)=102.62ms
     iterations.........: 990    98.100557/s
     vus................: 10     min=10 max=10

# add thresholds: crossing one exits with code 99, as in k6
★ Reality check

Why another load tester?

k6's script shape and metrics, Goose's engine ideas, and Python for the tests. If Locust serves you well, keep using it: K9 is for when the load generator's own overhead or accuracy matters.

k6LocustGoose⚡ K9
Test languageJavaScriptPythonRust (compiled)Python
EngineGoPython (gevent)RustRust
Open-model (arrival-rate) executors✓✗ closed model✗✓
Coordinated omissionAvoided by arrival rates✗✓ corrected✓ both
Load tests inside pytest✗✗✗✓
LicenseAGPL-3.0MITApache-2.0MIT / Apache-2.0
✦ three tiers

Python where it helps. Rust where it counts.

k9 inspect tells you which tier a test runs in, so you know what it costs before you run it.

1
Declarative

YAML plans and k9.plan

Python builds the plan once, before the run. Templates, extractors and feeders keep data flowing with no Python in the hot path.

Python in the hot path
None: full Rust speed
2
Hooks

setup, vu_init, teardown

Log in once per VU, prepare data, write your own summary with handle_summary. Each runs once, so its cost doesn't grow with the load.

Python in the hot path
Negligible
3
Iteration functions

async def default()

Full Python on every iteration: thousands of async VUs on one event loop, or plain def on a thread per VU, across cores with --workers.

Python in the hot path
Every iteration; scale with workers
★ what's in the box

Everything a CI load test needs, and then some.

Grafana dashboards and CI configs built for k6 carry over: same metric names, same tags, same exit codes.

k6-compatible

Executors, checks & thresholds

All seven executors, the threshold syntax with abortOnFail, k6's options, environment variables and exit codes.

  • ramping-vus
  • arrival rates
  • exit 99
  • -e KEY=VALUE
Protocols

HTTP, WebSockets & gRPC

A hyper client with k6's per-phase timings, HTTP/2 by ALPN, experimental HTTP/3, WebSockets and gRPC unary calls and streams.

  • HTTP/1.1
  • HTTP/2
  • HTTP/3
  • WebSocket
  • gRPC
Outputs

Results where you look

k6's summary, a live terminal dashboard and a self-contained HTML report, and samples streamed to your metrics backend.

  • JSON / CSV
  • HTML report
  • Prometheus
  • InfluxDB
  • StatsD
  • OTLP
Scale out

Cores, then machines

--workers runs a test in one process per core; --agents spreads it across machines over gRPC. Totals stay exact.

  • --workers auto
  • --agents N
  • execution segments
Accuracy

Honest latency

Arrival-rate executors report iteration_duration_corrected, so a stalled server shows up, and k9 warns when it is itself the bottleneck.

  • coordinated omission
  • CPU & port warnings
Authoring

From zero to a test

k9 new writes a starter for each tier; k9 convert turns a browser's HAR recording into a plan, tokens and CSRF fields included.

  • k9 new
  • k9 convert
  • typed API
135k

requests/s

A declarative plan, 100 VUs, a fast local endpoint.

~70k

requests/s

The same load from one Python process, async def VUs.

153k

requests/s

A Python script on 8 workers at 256 VUs.

99%

of iterations

1,000 VUs on a 200 ms endpoint, one process, p95 203.6 ms.

Release build on an i9-13900K (24 cores) with Linux under WSL2. Details and the comparison with k6, oha and Locust: benchmarks.

⚡ from install to first run

Your first load test is three commands away.

Install the wheel (Linux, macOS, Windows; no Rust needed), let k9 new write the test, and point it at your service.

terminal
$ pip install k9-load                       # or: uv tool install k9-load
$ k9 new --url http://localhost:3000/health # a starter script.py
$ k9 run script.py -u 5 -d 10s
✓ Rust engine ✓ Python & YAML tests ✓ MIT or Apache-2.0