What is K6?
K6 is a modern, developer-friendly load testing tool built for testing the performance of APIs, microservices, and websites. Write tests in JavaScript and run them from the CLI or CI/CD pipeline.
📮 Testing - POST API Load Test
Load test a POST endpoint with JSON payload and performance thresholds.
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '1m', target: 50 }, // Ramp up to 50 users
{ duration: '3m', target: 50 }, // Stay at 50 users
{ duration: '1m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
http_req_failed: ['rate<0.01'], // Less than 1% errors
},
};
export default function () {
const url = 'https://api.example.com/users';
const payload = JSON.stringify({
name: 'John Doe',
email: 'john@example.com',
role: 'developer',
});
const params = {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN_HERE',
},
};
const res = http.post(url, payload, params);
check(res, {
'status is 201': (r) => r.status === 201,
'response time < 500ms': (r) => r.timings.duration < 500,
'has user id': (r) => JSON.parse(r.body).id !== undefined,
});
sleep(1);
}
Expected Output:
$ k6 run post-load-test.js
✓ status is 201
✓ response time < 500ms
✓ has user id
checks.........................: 100.00% ✓ 9000 ✗ 0
data_received..................: 2.1 MB 42 kB/s
data_sent......................: 540 kB 11 kB/s
http_req_blocked...............: avg=1.2ms min=0s med=1ms max=45ms p(90)=2ms p(95)=3ms
http_req_connecting............: avg=1ms min=0s med=1ms max=42ms p(90)=2ms p(95)=2.5ms
✓ http_req_duration..............: avg=245ms min=120ms med=230ms max=480ms p(90)=350ms p(95)=420ms
http_req_failed................: 0.00% ✓ 0 ✗ 3000
http_req_receiving.............: avg=0.5ms min=0.1ms med=0.4ms max=5ms p(90)=1ms p(95)=1.5ms
http_req_sending...............: avg=0.3ms min=0.1ms med=0.2ms max=3ms p(90)=0.5ms p(95)=0.8ms
http_req_tls_handshaking.......: avg=0ms min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=244ms min=119ms med=229ms max=479ms p(90)=349ms p(95)=419ms
http_reqs......................: 3000 60/s
iteration_duration.............: avg=1.24s min=1.12s med=1.23s max=1.48s p(90)=1.35s p(95)=1.42s
iterations.....................: 3000 60/s
vus............................: 50 min=0 max=50
vus_max........................: 50 min=50 max=50
✓ Thresholds passed
When to use:
- Testing user registration endpoints
- Load testing POST APIs
- Validating API performance under load
- Setting performance baselines
🔍 Testing - GET API Stress Test
Stress test a GET endpoint to find breaking points.
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp up to 100 users
{ duration: '5m', target: 100 }, // Stay at 100 users
{ duration: '2m', target: 200 }, // Ramp up to 200 users
{ duration: '5m', target: 200 }, // Stay at 200 users
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(99)<1000', 'p(95)<800', 'p(50)<400'],
http_req_failed: ['rate<0.05'],
},
};
export default function () {
const url = 'https://api.example.com/products?page=1&limit=20';
const params = {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN_HERE',
},
};
const res = http.get(url, params);
check(res, {
'status is 200': (r) => r.status === 200,
'has products array': (r) => {
const body = JSON.parse(r.body);
return Array.isArray(body.products);
},
'response size ok': (r) => r.body.length < 100000,
});
sleep(0.5);
}
Expected Output:
$ k6 run get-stress-test.js
✓ status is 200
✓ has products array
✓ response size ok
checks.........................: 100.00% ✓ 24000 ✗ 0
data_received..................: 48 MB 60 kB/s
data_sent......................: 3.2 MB 4.0 kB/s
http_req_blocked...............: avg=0.8ms min=0s med=0.5ms max=35ms p(90)=1.5ms p(95)=2ms
http_req_connecting............: avg=0.7ms min=0s med=0.5ms max=32ms p(90)=1.2ms p(95)=1.8ms
✓ http_req_duration..............: avg=320ms min=95ms med=285ms max=980ms p(90)=520ms p(95)=680ms p(99)=890ms
http_req_failed................: 0.00% ✓ 0 ✗ 8000
http_req_receiving.............: avg=2ms min=0.2ms med=1.5ms max=25ms p(90)=4ms p(95)=6ms
http_req_sending...............: avg=0.2ms min=0.1ms med=0.2ms max=2ms p(90)=0.4ms p(95)=0.6ms
http_req_waiting...............: avg=318ms min=94ms med=283ms max=978ms p(90)=518ms p(95)=678ms
http_reqs......................: 8000 100/s
iteration_duration.............: avg=820ms min=595ms med=785ms max=1.48s p(90)=1.02s p(95)=1.18s
iterations.....................: 8000 100/s
vus............................: 200 min=0 max=200
vus_max........................: 200 min=200 max=200
✓ All thresholds passed
When to use:
- Finding system capacity limits
- Stress testing read-heavy APIs
- Performance regression testing
- Capacity planning
⚡ Testing - Spike Test (Sudden Traffic)
Test how your API handles sudden traffic spikes.
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '10s', target: 10 }, // Normal load
{ duration: '30s', target: 500 }, // Sudden spike!
{ duration: '1m', target: 500 }, // Sustained spike
{ duration: '10s', target: 10 }, // Recovery
],
thresholds: {
http_req_duration: ['p(95)<2000'], // More lenient during spikes
http_req_failed: ['rate<0.1'], // Allow 10% errors during spike
},
};
export default function () {
const res = http.get('https://api.example.com/health');
check(res, {
'status is 200': (r) => r.status === 200,
'response time OK': (r) => r.timings.duration < 3000,
});
sleep(Math.random() * 2); // Random think time
}
Expected Output:
$ k6 run spike-test.js
✓ status is 200
✗ response time OK
↳ 92% — ✓ 4140 / ✗ 360
checks.........................: 96.00% ✓ 8640 ✗ 360
data_received..................: 1.8 MB 18 kB/s
data_sent......................: 450 kB 4.5 kB/s
http_req_blocked...............: avg=2.5ms min=0s med=1ms max=120ms p(90)=5ms p(95)=8ms
http_req_connecting............: avg=2ms min=0s med=1ms max=115ms p(90)=4ms p(95)=7ms
✓ http_req_duration..............: avg=850ms min=180ms med=650ms max=2800ms p(90)=1500ms p(95)=1850ms
http_req_failed................: 8.00% ✓ 360 ✗ 4140
http_req_receiving.............: avg=1.2ms min=0.1ms med=0.8ms max=45ms p(90)=2.5ms p(95)=4ms
http_req_sending...............: avg=0.4ms min=0.1ms med=0.3ms max=8ms p(90)=0.8ms p(95)=1.2ms
http_req_waiting...............: avg=848ms min=179ms med=649ms max=2798ms p(90)=1498ms p(95)=1848ms
http_reqs......................: 4500 45/s
iteration_duration.............: avg=1.85s min=1.18s med=1.65s max=4.8s p(90)=2.5s p(95)=3.2s
iterations.....................: 4500 45/s
vus............................: 10 min=10 max=500
vus_max........................: 500 min=500 max=500
✓ Thresholds passed (degraded performance during spike expected)
When to use:
- Testing auto-scaling behavior
- Validating rate limiting
- Black Friday / traffic surge preparation
- DDoS resilience testing
🔐 Testing - Authentication Flow
Test authenticated POST requests with token refresh.
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 20,
duration: '2m',
thresholds: {
http_req_duration: ['p(95)<600'],
},
};
// Login once per VU
export function setup() {
const loginRes = http.post('https://api.example.com/auth/login', JSON.stringify({
username: 'testuser',
password: 'testpass123',
}), {
headers: { 'Content-Type': 'application/json' },
});
return { token: JSON.parse(loginRes.body).token };
}
export default function (data) {
const params = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${data.token}`,
},
};
// Create a post
const payload = JSON.stringify({
title: 'Test Post',
content: 'This is a test post content',
});
const res = http.post('https://api.example.com/posts', payload, params);
check(res, {
'post created': (r) => r.status === 201,
'has post id': (r) => JSON.parse(r.body).id !== undefined,
});
sleep(2);
}
Expected Output:
$ k6 run auth-flow-test.js
✓ post created
✓ has post id
checks.........................: 100.00% ✓ 2400 ✗ 0
data_received..................: 960 kB 8.0 kB/s
data_sent......................: 384 kB 3.2 kB/s
http_req_blocked...............: avg=0.6ms min=0s med=0.5ms max=12ms p(90)=1ms p(95)=1.5ms
http_req_connecting............: avg=0.5ms min=0s med=0.4ms max=10ms p(90)=0.9ms p(95)=1.2ms
✓ http_req_duration..............: avg=285ms min=145ms med=270ms max=580ms p(90)=390ms p(95)=450ms
http_req_failed................: 0.00% ✓ 0 ✗ 1200
http_req_receiving.............: avg=0.8ms min=0.2ms med=0.6ms max=8ms p(90)=1.5ms p(95)=2ms
http_req_sending...............: avg=0.3ms min=0.1ms med=0.2ms max=4ms p(90)=0.5ms p(95)=0.8ms
http_req_waiting...............: avg=284ms min=144ms med=269ms max=579ms p(90)=389ms p(95)=449ms
http_reqs......................: 1200 10/s
iteration_duration.............: avg=2.28s min=2.14s med=2.27s max=2.58s p(90)=2.39s p(95)=2.45s
iterations.....................: 1200 10/s
vus............................: 20 min=20 max=20
vus_max........................: 20 min=20 max=20
✓ All thresholds passed
When to use:
- Testing login/signup flows
- Validating JWT token handling
- Session-based API testing
- Auth service load testing
🔄 Testing - Multi-Endpoint Scenario
Test realistic user journey across multiple endpoints.
import http from 'k6/http';
import { check, group, sleep } from 'k6';
export const options = {
stages: [
{ duration: '1m', target: 30 },
{ duration: '3m', target: 30 },
{ duration: '1m', target: 0 },
],
thresholds: {
'group_duration{group:::User Journey}': ['p(95)<5000'],
http_req_duration: ['p(95)<500'],
},
};
export default function () {
group('User Journey', function () {
// 1. Browse products
let res = http.get('https://api.example.com/products');
check(res, { 'products loaded': (r) => r.status === 200 });
sleep(1);
// 2. View product details
res = http.get('https://api.example.com/products/123');
check(res, { 'product details loaded': (r) => r.status === 200 });
sleep(2);
// 3. Add to cart
const cartPayload = JSON.stringify({ productId: 123, quantity: 1 });
res = http.post('https://api.example.com/cart', cartPayload, {
headers: { 'Content-Type': 'application/json' },
});
check(res, { 'added to cart': (r) => r.status === 201 });
sleep(1);
// 4. Checkout
const checkoutPayload = JSON.stringify({
paymentMethod: 'card',
address: '123 Main St',
});
res = http.post('https://api.example.com/checkout', checkoutPayload, {
headers: { 'Content-Type': 'application/json' },
});
check(res, { 'checkout success': (r) => r.status === 200 });
});
sleep(3);
}
Expected Output:
$ k6 run multi-endpoint-test.js
✓ products loaded
✓ product details loaded
✓ added to cart
✓ checkout success
checks.........................: 100.00% ✓ 4800 ✗ 0
data_received..................: 9.6 MB 32 kB/s
data_sent......................: 1.2 MB 4.0 kB/s
group_duration.................: avg=2.8s min=2.1s med=2.7s max=4.5s p(90)=3.5s p(95)=3.9s
✓ group_duration{group:::User Journey}: avg=2.8s min=2.1s med=2.7s max=4.5s p(90)=3.5s p(95)=3.9s
http_req_blocked...............: avg=0.7ms min=0s med=0.5ms max=18ms p(90)=1.2ms p(95)=1.8ms
http_req_connecting............: avg=0.6ms min=0s med=0.4ms max=15ms p(90)=1ms p(95)=1.5ms
✓ http_req_duration..............: avg=298ms min=125ms med=280ms max=485ms p(90)=410ms p(95)=450ms
http_req_failed................: 0.00% ✓ 0 ✗ 4800
http_req_receiving.............: avg=1.2ms min=0.2ms med=0.9ms max=12ms p(90)=2.5ms p(95)=3.5ms
http_req_sending...............: avg=0.4ms min=0.1ms med=0.3ms max=5ms p(90)=0.7ms p(95)=1ms
http_req_waiting...............: avg=296ms min=124ms med=278ms max=483ms p(90)=408ms p(95)=448ms
http_reqs......................: 4800 16/s
iteration_duration.............: avg=9.8s min=9.1s med=9.7s max=11.5s p(90)=10.5s p(95)=10.9s
iterations.....................: 1200 4/s
vus............................: 30 min=0 max=30
vus_max........................: 30 min=30 max=30
✓ All thresholds passed
When to use:
- E-commerce flow testing
- User journey validation
- Complex workflow testing
- Integration testing under load
⏱️ Testing - Rate Limiting Validation
Test API rate limiting behavior.
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';
const rateLimitHit = new Rate('rate_limit_hit');
export const options = {
scenarios: {
rate_limit_test: {
executor: 'constant-arrival-rate',
rate: 100, // 100 requests per timeUnit
timeUnit: '1s',
duration: '1m',
preAllocatedVUs: 50,
maxVUs: 100,
},
},
};
export default function () {
const res = http.get('https://api.example.com/data');
check(res, {
'success': (r) => r.status === 200,
'rate limited': (r) => r.status === 429,
});
// Track rate limiting
rateLimitHit.add(res.status === 429);
if (res.status === 429) {
const retryAfter = res.headers['Retry-After'] || 1;
console.log(`Rate limited. Retry after ${retryAfter}s`);
}
sleep(0.1);
}
Expected Output:
$ k6 run rate-limit-test.js
INFO[0015] Rate limited. Retry after 60s source=console
INFO[0018] Rate limited. Retry after 60s source=console
INFO[0022] Rate limited. Retry after 60s source=console
✓ success
✓ rate limited
checks.........................: 100.00% ✓ 12000 ✗ 0
data_received..................: 2.4 MB 40 kB/s
data_sent......................: 1.2 MB 20 kB/s
http_req_blocked...............: avg=0.5ms min=0s med=0.4ms max=15ms p(90)=1ms p(95)=1.5ms
http_req_connecting............: avg=0.4ms min=0s med=0.3ms max=12ms p(90)=0.8ms p(95)=1.2ms
http_req_duration..............: avg=45ms min=25ms med=42ms max=250ms p(90)=68ms p(95)=85ms
http_req_failed................: 15.00% ✓ 900 ✗ 5100
http_req_receiving.............: avg=0.3ms min=0.1ms med=0.2ms max=3ms p(90)=0.5ms p(95)=0.8ms
http_req_sending...............: avg=0.2ms min=0.1ms med=0.1ms max=2ms p(90)=0.3ms p(95)=0.4ms
http_req_waiting...............: avg=44ms min=24ms med=41ms max=248ms p(90)=67ms p(95)=84ms
http_reqs......................: 6000 100/s
iteration_duration.............: avg=145ms min=125ms med=142ms max=350ms p(90)=168ms p(95)=185ms
iterations.....................: 6000 100/s
rate_limit_hit.................: 15.00% ✓ 900 ✗ 5100
vus............................: 50 min=50 max=100
vus_max........................: 100 min=100 max=100
Rate limiting triggered at ~85 req/s (expected: 100/s)
When to use:
- Validating rate limit configuration
- Testing throttling mechanisms
- API quota testing
- DDoS protection validation
🔮 Testing - GraphQL Query Load Test
Load test GraphQL endpoints with complex queries.
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 30,
duration: '2m',
thresholds: {
http_req_duration: ['p(95)<800'],
},
};
export default function () {
const query = `
query GetUserProfile($userId: ID!) {
user(id: $userId) {
id
name
email
posts(limit: 10) {
id
title
comments {
id
text
author {
name
}
}
}
}
}
`;
const variables = {
userId: Math.floor(Math.random() * 1000) + 1,
};
const payload = JSON.stringify({ query, variables });
const params = {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN_HERE',
},
};
const res = http.post('https://api.example.com/graphql', payload, params);
check(res, {
'query successful': (r) => r.status === 200,
'no errors': (r) => !JSON.parse(r.body).errors,
'has user data': (r) => JSON.parse(r.body).data.user !== null,
});
sleep(1);
}
Expected Output:
$ k6 run graphql-test.js
✓ query successful
✓ no errors
✓ has user data
checks.........................: 100.00% ✓ 3600 ✗ 0
data_received..................: 7.2 MB 60 kB/s
data_sent......................: 1.8 MB 15 kB/s
http_req_blocked...............: avg=0.9ms min=0s med=0.6ms max=22ms p(90)=1.5ms p(95)=2.2ms
http_req_connecting............: avg=0.7ms min=0s med=0.5ms max=18ms p(90)=1.2ms p(95)=1.8ms
✓ http_req_duration..............: avg=425ms min=220ms med=395ms max=780ms p(90)=590ms p(95)=680ms
http_req_failed................: 0.00% ✓ 0 ✗ 1200
http_req_receiving.............: avg=2.5ms min=0.5ms med=2ms max=18ms p(90)=4.5ms p(95)=6ms
http_req_sending...............: avg=0.5ms min=0.1ms med=0.4ms max=6ms p(90)=0.9ms p(95)=1.3ms
http_req_waiting...............: avg=422ms min=218ms med=393ms max=778ms p(90)=588ms p(95)=678ms
http_reqs......................: 1200 10/s
iteration_duration.............: avg=1.42s min=1.22s med=1.39s max=1.78s p(90)=1.59s p(95)=1.68s
iterations.....................: 1200 10/s
vus............................: 30 min=30 max=30
vus_max........................: 30 min=30 max=30
✓ All thresholds passed
When to use:
- GraphQL API load testing
- Complex nested query testing
- Resolver performance testing
- N+1 query problem detection
📤 Testing - File Upload Performance
Test file upload endpoints with multipart data.
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
const file = open('./test-file.pdf', 'b'); // Binary file
export const options = {
vus: 10,
duration: '1m',
thresholds: {
http_req_duration: ['p(95)<3000'], // Uploads can be slower
},
};
export default function () {
const formData = {
file: http.file(file, 'test-file.pdf', 'application/pdf'),
description: 'Test file upload',
category: 'documents',
};
const res = http.post('https://api.example.com/upload', formData, {
headers: {
'Authorization': 'Bearer YOUR_TOKEN_HERE',
},
});
check(res, {
'upload successful': (r) => r.status === 201,
'has file id': (r) => JSON.parse(r.body).fileId !== undefined,
'upload time reasonable': (r) => r.timings.duration < 5000,
});
sleep(2);
}
Expected Output:
$ k6 run file-upload-test.js
✓ upload successful
✓ has file id
✓ upload time reasonable
checks.........................: 100.00% ✓ 900 ✗ 0
data_received..................: 180 kB 3.0 kB/s
data_sent......................: 30 MB 500 kB/s
http_req_blocked...............: avg=1.5ms min=0s med=1ms max=45ms p(90)=3ms p(95)=5ms
http_req_connecting............: avg=1.2ms min=0s med=0.8ms max=38ms p(90)=2.5ms p(95)=4ms
✓ http_req_duration..............: avg=1.85s min=850ms med=1.72s max=2.95s p(90)=2.45s p(95)=2.68s
http_req_failed................: 0.00% ✓ 0 ✗ 300
http_req_receiving.............: avg=0.8ms min=0.2ms med=0.6ms max=8ms p(90)=1.5ms p(95)=2.2ms
http_req_sending...............: avg=125ms min=45ms med=118ms max=350ms p(90)=188ms p(95)=225ms
http_req_waiting...............: avg=1.72s min=795ms med=1.59s max=2.82s p(90)=2.32s p(95)=2.55s
http_reqs......................: 300 5/s
iteration_duration.............: avg=3.85s min=2.85s med=3.72s max=4.95s p(90)=4.45s p(95)=4.68s
iterations.....................: 300 5/s
vus............................: 10 min=10 max=10
vus_max........................: 10 min=10 max=10
✓ All thresholds passed
When to use:
- Testing document upload endpoints
- Image upload performance
- Large file handling
- Multipart form data testing
📊 Performance Metrics Guide
Understanding K6 output metrics:
Response Time Percentiles:
- P50 (Median): 50% of requests faster than this
- P90: 90% of requests faster than this
- P95: 95% of requests faster than this - common SLA target
- P99: 99% of requests faster than this - tail latency
Request Timing Breakdown:
http_req_duration: Total request time (waiting + receiving)http_req_waiting: Time to first byte (TTFB)http_req_receiving: Time to download responsehttp_req_sending: Time to send requesthttp_req_blocked: Time waiting for connectionhttp_req_connecting: TCP connection time
Success Metrics:
checks: Assertion pass ratehttp_req_failed: HTTP error ratehttp_reqs: Total requests per second
When to use different thresholds:
- API endpoints: p(95) < 500ms
- Database queries: p(95) < 200ms
- File uploads: p(95) < 3000ms
- Complex reports: p(95) < 2000ms