API Load Testing with k6
k6 is the developer-first load testing tool that runs JavaScript scripts, scales to millions of requests, and integrates cleanly into CI/CD. This guide covers everything from first run to production SLA enforcement.
Installation
# macOS brew install k6 # Ubuntu/Debian sudo apt-get install k6 # Windows (via Chocolatey) choco install k6 # Docker (no install needed) docker run --rm -i grafana/k6 run - < script.js
Basic Load Test Script
// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
// Ramp up: 0 → 50 VUs in 1m, hold 5m, ramp down 1m
stages: [
{ duration: '1m', target: 50 },
{ duration: '5m', target: 50 },
{ duration: '1m', target: 0 },
],
// SLA thresholds — test fails if any is breached
thresholds: {
'http_req_duration': ['p(95) < 500', 'p(99) < 1000'], // ms
'http_req_failed': ['rate < 0.01'], // < 1% errors
},
};
export default function () {
const BASE_URL = 'https://api.example.com';
// GET request
const res = http.get(`${BASE_URL}/users`, {
headers: { Authorization: 'Bearer test-token' },
});
check(res, {
'status is 200': (r) => r.status === 200,
'response time OK': (r) => r.timings.duration < 500,
'has users array': (r) => JSON.parse(r.body).users !== undefined,
});
sleep(1); // think time between iterations
} # Run the test k6 run load-test.js # Quick smoke test (1 VU, 10 iterations) k6 run --vus 1 --iterations 10 load-test.js # Output results as JSON k6 run --out json=results.json load-test.js
POST with Authentication Flow
// auth-flow.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 100,
duration: '5m',
thresholds: {
'http_req_duration{name:login}': ['p(95) < 300'],
'http_req_duration{name:dashboard}': ['p(95) < 200'],
},
};
export default function () {
// Step 1: Login
const loginRes = http.post(
'https://api.example.com/auth/login',
JSON.stringify({ email: '[email protected]', password: 'pass123' }),
{
headers: { 'Content-Type': 'application/json' },
tags: { name: 'login' }, // tag for per-endpoint thresholds
}
);
check(loginRes, { 'login 200': (r) => r.status === 200 });
const token = loginRes.json('token');
// Step 2: Authenticated request
const dashRes = http.get(
'https://api.example.com/dashboard',
{
headers: { Authorization: `Bearer ${token}` },
tags: { name: 'dashboard' },
}
);
check(dashRes, { 'dashboard 200': (r) => r.status === 200 });
sleep(2);
} Load Test Types and When to Use Each
| Test Type | VU Pattern | Duration | Purpose |
|---|---|---|---|
| Smoke test | 1-5 VUs | 1-2 min | Verify script works, no errors |
| Load test | Ramp to expected VUs | 5-30 min | Validate performance under normal load |
| Stress test | Ramp beyond capacity | 15-60 min | Find breaking point, error behavior |
| Spike test | 0 → max instantly | 5-15 min | Sudden traffic surge (flash sale, viral) |
| Soak test | Moderate VUs steady | 2-24 hours | Memory leaks, connection pool exhaustion |
CI/CD Integration (GitHub Actions)
# .github/workflows/load-test.yml
name: API Load Test
on:
workflow_dispatch: # manual trigger
schedule:
- cron: '0 2 * * 1' # every Monday at 2am
jobs:
k6-load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: grafana/setup-k6-action@v1 # official k6 action
- name: Run load test
run: k6 run --out json=results.json load-test.js
env:
API_BASE_URL: ${{ vars.STAGING_API_URL }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: k6-results
path: results.json Frequently Asked Questions
What is k6 and how is it different from JMeter or Locust?▼
k6 is a modern, developer-focused load testing tool written in Go that runs JavaScript test scripts. Unlike JMeter (Java, XML configuration, GUI-heavy), k6 scripts are plain JavaScript files that version-control cleanly and integrate naturally into CI/CD pipelines. Unlike Locust (Python, concurrent greenlets), k6 uses Go goroutines under the hood, giving it dramatically lower resource consumption per virtual user — you can run thousands of VUs on a laptop that would struggle with Locust. k6 also has built-in thresholds that fail the test if SLA metrics are breached.
What is the difference between VUs and RPS in k6?▼
VUs (Virtual Users) represent concurrent simulated users — each VU runs through your scenario sequentially. RPS (Requests Per Second) is a throughput metric derived from VU count and request duration. With 100 VUs each sending one request per second, you get approximately 100 RPS. With 100 VUs sending requests with 200ms response times, you get roughly 500 RPS. k6 lets you control either directly: use constant-vus executor to fix VU count, or use constant-arrival-rate executor to fix RPS regardless of response time.
How do I set performance thresholds in k6?▼
Define thresholds in your script's options object as conditions on metric names. Common thresholds: http_req_duration for latency (e.g., p95 < 500 for p95 under 500ms), http_req_failed for error rate (e.g., rate < 0.01 for under 1% errors). If any threshold is breached, k6 exits with a non-zero code, failing your CI pipeline. You can also set thresholds per URL group using tagged metrics.
Can k6 test WebSocket or gRPC APIs?▼
Yes. k6 has native WebSocket support via the k6/ws module — use ws.connect() to open a connection and send/receive messages in a callback. For gRPC, import k6/net/grpc, load your proto definitions with client.load(), and call methods with client.invoke(). HTTP/2 is supported automatically when the server supports it. k6 also has modules for Redis, Kafka, and SQL in the k6 extension ecosystem (xk6).
How do I integrate k6 with CI/CD and alert on regressions?▼
Add k6 to CI by installing k6 (apt/brew/docker) and running k6 run --out json=results.json script.js. The exit code is non-zero if any threshold fails, so CI naturally fails on SLA breaches. For trend tracking, output results to Prometheus (k6 --out experimental-prometheus-rw), Grafana Cloud (k6 --out cloud), InfluxDB + Grafana, or Datadog (k6 --out datadog). Store baseline metrics and alert when p95 or error rate regresses beyond a tolerance.
What is the k6 stages array and how do I model realistic traffic?▼
The stages array in options lets you define a ramp-up shape for VU count over time. Each stage specifies a duration and target VU count. A typical load test ramps up gradually (0→50 VUs over 2 minutes), holds steady load (50 VUs for 5 minutes), then ramps down. For realistic traffic patterns, use the ramping-arrival-rate executor to model request-per-second curves. Spike tests go from 0 to max VUs in seconds. Soak tests hold moderate load for hours to expose memory leaks.
Related Products
on GumroadDeveloper Productivity Bundle
Stop rebuilding the same setup every project. 51 VSCode snippets, 40 Git aliases, 5 Docker Compose stacks, 5 GitHub Actions workflows, 5 README templates, and a dotfiles kit. Set up a professional dev environment in under an hour.
Buy on Gumroad →AI Prompt Engineering Toolkit
58 structured prompt templates for Claude, ChatGPT, and Gemini — organized by use case. Code Review, Debugging, Documentation, Architecture, and 10 model-specific tips. Stop writing prompts from scratch.
Buy on Gumroad →Developer Cheat Sheet Bundle
10 print-ready cheat sheets for the tools developers use every day: Git, Docker, Linux CLI, SQL, Regex, HTTP Status codes, VS Code shortcuts, Bash scripting, jq/JSON, and Markdown.
Buy on Gumroad →You Might Also Need
OpenAPI Validator
Paste your OpenAPI 3.x spec (YAML or JSON) to validate structure, find missing required fields, detect duplicate paths, check for missing operationIds, and get errors and warnings with path references.
Open free tool →
k6 Script Generator
Generate k6 load test scripts with stages, thresholds, and metrics. Configure virtual users, ramp-up patterns, and checks for HTTP performance testing.
Open free tool →
API Response Mock Generator
Generate realistic mock API responses with custom status codes, headers, and JSON body. Quick templates for 200, 401, 404, 429, 500 and more. Runs in your browser.
Open free tool →
More Free Tools
Related Articles
Best Load Testing Tools for Developers in 2025: k6, JMeter, Locust, and More
A practical comparison of load testing tools in 2025 — k6, JMeter, Locust, Gatling, and Artillery — covering scripting ease, CI integration, cloud execution, and when each tool fits your testing needs.
Performance Testing in 2026: Beyond Page Load Times
A practical guide to performance testing in 2026 — Core Web Vitals, load testing, stress testing, soak testing, and building a performance culture that treats speed as a feature. Includes tooling recommendations and actionable measurement strategies.
API Testing Best Practices 2026: Postman vs Insomnia vs Bruno
Complete guide to API testing in 2026. Compare Postman, Insomnia, and Bruno, learn best practices for REST and GraphQL testing, and build automation pipelines that catch regressions before production.
MSW 2.0: The Best Way to Mock APIs in Frontend Tests
A complete guide to Mock Service Worker (MSW) 2.0 — setup, request handlers, Vitest and Jest integration, Storybook mocking, and best practices for realistic API mocking in frontend tests.
API Rate Limiting: Algorithms and Implementation Guide 2026
Complete guide to API rate limiting in 2026. Token Bucket, Leaky Bucket, Sliding Window algorithms with Node.js and Redis implementations, distributed rate limiting, 429 handling, and best practices.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- ⚡ AI Code Review, Doc Generator & SQL Builder
- ⚡ All premium templates & early access
- ⚡ Member discounts on Gumroad products
$9/mo after trial · Cancel anytime
Want the full toolkit?
Get DevPlaybook Pro
Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.
MIT licensed · Instant download · No subscription
See what's included → Browse all deals →