# Grafana k6

> A developer-centric load and performance testing tool scripted in JavaScript and executed in Go.

Source: https://apiguide.dev/tools/testing-mocking/k6
Category: testing-mocking
Website: https://grafana.com/oss/k6/
Pricing: open-source
Updated: 2026-10-14

## What is Grafana k6?

Grafana k6 is an open-source load testing tool maintained by Grafana Labs under the AGPL-3.0 license. The engine is written in Go and tests are scripts in JavaScript, or TypeScript run directly since k6 1.0, so a test is code that lives in a repository and runs in CI. Each script defines what a virtual user does, how many of them run and for how long, and the pass/fail criteria for the run. The same script runs locally with `k6 run`, distributed on Kubernetes, or on the paid Grafana Cloud k6 service.

## Why use it in the API Lifecycle?

* **Tests as code**: Scripts are plain modules with `options`, a default function per iteration, and `setup`/`teardown` hooks, so they review and version like any other code.
* **Checks and thresholds**: `check()` validates individual responses without stopping the run; `thresholds` turn metrics such as `http_req_duration` and `http_req_failed` into pass/fail criteria, and a failed threshold makes k6 exit non-zero.
* **Workload models**: Scenarios and executors cover both closed models (a fixed number of virtual users) and open models (a constant or ramping arrival rate), which matters when you want to know how an API behaves at a target request rate.
* **Protocol coverage**: HTTP/1.1 and HTTP/2, WebSockets and gRPC are built in, along with a browser module for front-end checks.
* **Extensions**: xk6 extensions add protocols and outputs that the core does not ship.
* **Metrics output**: Results go to the end-of-test summary or stream to external backends for correlation with server-side telemetry.

## Best Practices

* Gate on thresholds, not on eyeballing the summary. A block such as `http_req_failed: ['rate<0.01']` and `http_req_duration: ['p(95)<300']` makes the run fail the pipeline. Use percentiles, never the average, which hides the slow tail clients actually feel.
* Model arrival rate when you are testing capacity. A closed model with a fixed number of virtual users slows its own request rate as the API slows down, which masks the problem; `constant-arrival-rate` or `ramping-arrival-rate` keeps sending at the target rate and shows the queueing.
* Tag requests by endpoint and set thresholds per tag, for example `'http_req_duration{name:create-order}': ['p(95)<500']`. One aggregate latency number lets a slow write endpoint hide behind fast reads. Group URLs that contain IDs under a single `name` tag so each ID does not become its own metric series.
* Decide what counts as a failure. By default `http_req_failed` treats any status outside 200 to 399 as failed, so an expected [`404 Not Found`](/status-codes/404) or [`409 Conflict`](/status-codes/409) in the scenario inflates the error rate; set `http.setResponseCallback(http.expectedStatuses(...))` to match the scenario.
* Watch for [`429 Too Many Requests`](/status-codes/429) and [`503 Service Unavailable`](/status-codes/503). If the test trips the API's own [rate limiting](/guides/rate-limiting), you measured the limiter, not the service; either test with an exempt credential or treat the limiter's behavior as the subject of the test, including whether it sends [`Retry-After`](/headers/retry-after).
* Load test an environment shaped like production, with production-like data volumes, and never point a test at a third-party API you do not own. Correlate the run with server-side metrics and traces (see [Observability and Tracing](/guides/observability-tracing)) so a latency spike can be attributed to a cause.
* Keep a small smoke version of the same script, a handful of virtual users for a minute, and run it on every merge. The full load profile can run on a schedule.
